Category Archives: Math

Designing a Function

From time to time you need a function in procedural content generation. A function with certain properties which can be edited easily. A function of your own design.

First of all you need a tool to plot functions or create triangulations (for 2D/3D functions). I took GNU Octave to design the following functions and than ported the code.

Lets start with a small example. Later I will show you a more complex variant I designed for sound synthesis.Func_Example1

A good start is a picture. Draw it and think about what should happen for a wish list of parameters. Lets assume we would like to have a function which starts at and ends at . Inside this box we would like to set one point which is on the function. This is a standard interpolation problem but it is a good example and an element of the complex function we will see later.

In next step I always think about other functions which approximate my picture: exp, log, polynomials, sin, cos, ...
Well for our example a line will suffice. The only problem: it must have a break. The easiest thing is to use two lines and somehow combine them.

The first one goes through and its slope is well defined by . The second one can be defined by a view on the upper corner. Again the slope is a simple quotient which can be put into the line equation and gives


Now we could create a piecewise-defined function or use. The second variant is valid only for the upper left triangle. It might be useful for another parametrization or function.

My Universal Periodic Function

I searched for a function with a small parameter set which can:

  • Create high frequent basic tones
  • Be used for fading of different frequencies
  • Be used as fade function for the volume

I started with following ideas: A function for a sound must be periodic. I choose as the domain for one period and a co-domain of because then we have a good control later when using this function. To get something periodic in the interval we could use fmod or just the fractional part. As long as we define a function in the given interval we do not need to think about periodicity.
Furthermore I would like to be able to mix any standard periodic function (Sinus, Triangle, Square, Sawtooth) by setting parameters. I designed 3 parameters to achieve that:

Func_ShapeShape: Just interpolate between Sinus and Triangle function. This can be seen as smoothness parameter too. The implementation is as simple as the idea. The triangle function is designed from three lines with slopes 4, -4 and 4 again.

 

Stretch: Func_StretchAdd some space at the vertices of the base functions. This can be used to create a Rectangular function or it is useful for volume and frequency fading too. Just take the first half and you have a function which fades in and out and keeps one level in between.

 

ShiftFunc_ShiftMove the vertices left and right (point symmetric). Values near -1 or 1 create a Sawtooth function. Again using only the first half this can be used to control fading.

Moreover Stretch and Shift can be used to design Attack, Sustain and Release of a note. This is not the full ADSR model, but if I can use the same function on many levels it is a good compromise.

Implementation

I started with the implementation of the shape factor. Here I had no better idea than a simple interpolation. The Triangle wave consists of three linear functions: . To combine them one could use branches for and , but I expect the usage of min/max to be faster. This time there isn't any parameter which disturbs the ordering of the functions as in the example above.

Afterwards I added the stretch factor. Therefore we must solve the problem: How can we add space at the points 0.25 and 0.75?
Easier is to think: How must x look like that sin(x) will have these plateaus?

Func_Stretch2So if we have a function as on the picture we are ready. Applying it to x before calling upf(x) adds the plateaus. Moreover we don't need to bother about the shape factor. Its functionality is orthogonal so we can change the parameters independently.

This trick to remap the input is like changing the contrast in a picture. All we need is a monotonous function and we can change the out coming function while staying inside the given range.

In the implementation I just focused on one single step in , copied that and added 0.5 to the right side.

Func_Shift2Last of all we will add the shift parameter the same way as the stretch parameter. Again a picture can explain that best. Essentially we are using the example from the introduction but we choose the point . For the right side the same slopes can be used again so three lines (two with the same scope) are created from the one point.

Holding is important because we want the shift to move the vertices and no arbitrary points. At the point where the remapping returns 0.25 the vertices are created later which is where we want the change in frequency to happen. I also tested a smoother (exponential) step function but it was less controllable.

This time I consider branching as faster. It is also important to guarantee stability. For parameters -1 and 1 px gets 0. For both cases the branching will end up in the middle part where everything works fine. Otherwise we could get a division by zero.

Summary

Taking all together we have a really customizable function.

 

Except the function shown here I earlier created a light falloff function without a singularity and a determined radius... I could get used to build my own functions. Finally I will give you a list of things I learned:

  • Use a tool which can plot functions.
  • Always draw your idea of a function on a paper and than try to find an approximation.
  • You should know graphs of functions as: sinus, tangents, exp, gauss, ln, polynomials, ...
  • Linear functions are your friend for parametrization: you will exactly know what they will do.
  • Many things can be achieved with a polynomial: If you have enough criteria as fixed points and derivatives you can just solve some equations to find the proper factors for a fitting polynomial

Collision Detection Basics

The basis for all collision detections is analytical geometry. Math can answer the question where two primitives intersect or give the distance between two objects. This post will only cover such basic functions. So it is a collection of primitive - primitive relation functions. There are two functions given. The first only returns collision or not. The second variant returns a resolvement vector. This is a translation vector which have to be applied to object 1 to move the two objects out of each other.

3D

Object Properties
Sphere center , radius
Line start and end , implicit direction
Triangle The 3 vertices in counter clock wise order , implicit normal

Sphere - Sphere

For a detection the distance has to be less or equal to the sum of the radi.

For resolvement just take the connection between the midpoints and the intersection length.

Sphere - Line

First the nearest point on a straight must be computed. If this point does not lie between the start and the end point choose the start or end point respectively.


 

The resolvement vector is the connection between sphere center and the nearest point on the line.

Sphere - Triangle

The usual way to do this is to project the sphere center to the triangle's plane. Afterwards the barycentric coordinates of the point would be calculated to test if it is inside. If not we still have to test whether the circle from the plane-sphere cut intersects with a triangle or not. Therefore it is sufficient to test the 3 line-sphere cuts because the center is definitely outside. I changed the middle part to hopefully increase the speed. Instead of calculating the barycentric coordinates I compute the closest points on all three sides which I would need in the last step non the less. Then the following condition must be true if the point is inside: the opposite vertex to a side (and therefore a point on that side) must lie on two different "sides" of the projected center. So the dot product of the vectors from the center to a closest point on a triangle-side and to the related triangle-vertex has to be at least 0.

There was one problem when using a full triangle collision in the game Pathfinder. Imagine a box made of triangles. Then the sphere is placed on top near to an edge. The simulation will first push the sphere down. Afterwards the collision detection would report two triangles. The one on the top and one of a side where the sphere collides with a back face. This has the effect that the sphere is pushed away from the edge to the box center on the upper side. To avoid that I use the condition if( fDist < 0.0f || fDist > _S.fRadius ) in lines 10 and 46 instead of if( abs(fDist) > _S.fRadius ) .