The purpose of P1c was threefold. First, we began to see how to apply the techniques from P1a & b to a real application.
Second, we gained exposure to the circular interpolation. Third, we explored the idea of having multiple parameterizations in play at the same time.
This last point refers to traveling along the circular curve (interpolated) at a speed determined by a Bezier interpolation of the initial speeds.
This dual interpolation scheme is similar to the extra credit question posed on the quiz (round 2).
0 - Linear Interpolation Mode
1 - Bezier Interpolation Mode
2 - Circular Interpolation Mode
r - Toggle road editor on/off
SHIFT-1 - Show 1 Car
SHIFT-2 - Show 2 Cars
SHIFT-3 - Show 3 Cars
SHIFT-4 - Show 4 Cars
SHIFT-5 - Show 5 Cars
SHIFT-6 - Show 6 Cars
3 - Use 3 key frames per car
4 - Use 4 key frames per car
5 - Use 5 key frames per car
6 - Use 6 key frames per car
space - Start/Stop animation
s - Save all
l - Load all (currently not implemented)
h - Reset to initial state.
|
|
To do this, we created a function called myBezier which took in the four points created at the top of the interpolateBezier
function and the t parameter. myBezier performed the De Casteljau construction and returned a data structure containing both a position
and the tangent at that position. Both of these are products of the construction. The position is just that – the position on the Bezier curve.
The tangent represents the direction of motion of the car at that position. To align the car to that we used the angle function to calculate a
rotation angle required (from some canonical vector) to move the car to the desired alignment on the tangent vector.
The myBezier function is shown below. All of this code resides in the “car” file of p1c.
bezierData myBezier(float t, pt P1, pt P2, pt P3, pt P4) {
pt P5 = slide(P1,P2,t);
pt P6 = slide(P2,P3,t);
pt P7 = slide(P3,P4,t);
pt P8 = slide(P5, P6,t);
pt P9 = slide(P6, P7,t);
bezierData data = new bezierData();
data.position = slide( P8, P9, t );
data.tangent = P8.vecTo(P9);
data.tangent.unit();
return data;
}
/**
* finds the position along the vector AB indicated by param
**/
pt slide(pt A, pt B, float t) {
vec C = A.vecTo(B);
A.addScaledVec(t, C);
return A;
}
An image of the Bezier driving curve is shown below. The image was captured before the road editor was complete so the
interface on the final turn-in may have changed slightly from what is depicted here.
|
The road editor that we created was largely taken from the p1b polyloop editing code.
It works in a similar manner (ex: to remove control points drag them off the screen). Vertices can be dragged to change the shape of the road.
Also, in order to add more control vertices simply click in the middle of an edge. The polyloop that is created has a fill color set to give
it a road-like appearance. The major difference is that you can not sub-divide roads -- only the control loop gets rendered.
Also, you are able to lock the editor to prevent changing the nodes while trying to manipulate cars (a problem that would really annoy you).
|
To implement animation, we made significant changes to the underlying code structure. First off, we are now creating a 2-d array of cars rather than just the single array of cars.
The first layer is for the cars themselves, the second: that car's key animation frames. These key frames are just instances of cars themselves, and are stored
a car class. This allows the master car to manage all its own frames, completly independent of other cars. This structure also allows for further nesting of cars,
so if one desired, she could have key frames of key frames of key frames. . . oh just imagine the fun!!.
int nFrames = 6; // number of frames this car uses
int MyIndex = -1; // the index of this car (from the master array)
int ParentIndex = -1; // parent index
car Frame[] = new car[nFrames]; // our array of frames
int interpolationMode = 0; // the interpolation mode to use for this car
int lastSelectedFrame = -1; // used for parent cars; stores the last selected frame
Creating the cars this way allows us to modify each car's key frame independently.. both of other key frames and other cars.
Once you click on the main car image (which will have the full saturation of color), you are now in "edit mode" for that car.
You can now edit each key frame of the car (include the main car position as well) in order. So key frame 0 first, then 1, etc..
Once you move out the frames you are free to edit them in any manner you wish. Also, hitting '0', '1', '2' will set the mode for any
given key frame; this allows you to further fine tune the transformations on the fly. And, for even more control, each car's key frame
list can be different lengths (from 3 to 6, currently) so you can add or subtract key frames as needed.

|
The interpolateTwist function moves from one car to the next along a circular arc. Travel along this arc occurs at constant speed.
The assignment was to modify the speed at which we traveled along the arc via using a Bezier interpolation of the speeds at the endpoints.
Our strategy was to take the input “t” parameter to the interpolateTwist function and remap it to a new “t” based on the output of a
one dimensional Bezier speed interpolation (like P1a).
In order to do this we created a Bezier interpolation in one dimension where the points were:
A: The initial car position (zero)
B: A + 1/3 * initial car velocity
C: D – 1/3 * final car velocity
D: The distance from A to D
The output of this 1D Bezier function is normalized to a [0,1] range. We can do this because the “curve” is actually a line whose length we know (A to D, negative velocities are disallowed too!) and the de Casteljau construction gives a point on this line (aka a distance traveled so far).
Below we show a graph of how this re-parameterization worked for 2 curve segments.
The straight line represents the initial t input. The x axis represents per draw() samplings (aka, time along the curve from 0 to 1).
The “series-2” and “series-4” represent the slow to fast and fast to slow travel along the arcs shown in the images below the graph.
|
|