Here is the second half of your notes for the assignment due Wednesday March 11, In these notes we talk about controlling animation parameters over time. Fortunately, this part is quite easy - it's not going to come to many lines of code - so if you've gotten the first part done by now, you should be able to wrap this part up very quickly.

We've already established that if you have matrix transformations and a hierarchical structure of scene objects, you can animate lots of things simply by varying the arguments to your translate, rotate and scale matrices over time. This simplifies the process of animation, since it means you only need to vary numbers over time to create your animation.

In order to get the most out of this mechanism, you'll want a really good way to vary these numbers over time. You've already seen, from our earlier lectures, that you can use functions like sines and cosines to make things undulate back and forth. But we want to get beyond that, to make some really controlled animation.

One way to think of your animated scene is that the entire scene is a kind of puppet, one that you can pose in various positions. If you choose a particular set of values for the numeric parameters to your translate, rotate and scale matrices, then the scene will assume some particular "pose".

You can think of animation as moving from pose to pose, in succession. This "pose to pose" technique is a large part of the way professional animators work - an animated character smiles, then frowns, turns his head, perhaps lifts his arm to point, all in some sequence over time.

You want some mechanism for creating particular sequences numeric values over time, so you can use those values as arguments to your transformation matrices. You also want those numeric values to vary continuously, rather than jumping discontinuously. In the real world, objects don't just jump instantly from one place to another - they vary smoothly and continuously.

Let's consider a computer graphic animation of a puppet. Among the many things that can be rotated over time on this puppet are the z-axis (outward from body) rotation of the shoulder, elbow and wrist of one of the puppet's arms.

If you build this puppet arm by using nested objects with matrices to make a hierarchical skeleton, as we discussed last week, then you can effect these rotations by varying some numeric parameters over time - the arguments to various calls to the rotateZ method in your application applet.

If we think of the animation of this puppet as a transition between various poses over time, then we can plot the values of the rotation parameters, as they move from pose to pose to pose. If the "inbetween" values for the times between poses are just straight lines, then you get a plot something like this one:

To implement this animation, you can store the pose values in a two dimensional array of key frame poses. One dimension of this array will index each animation parameter (eg: shoulder rotation about z, elbow rotation about x, etc), allocating one slot per numeric value that you want to animate over time.

The other dimension of the array will index the successive key frames - those points in time that contain nice poses for the puppet. Your animation will consist of in-betweening from pose to pose, by interpolating over time the values that are stored in this array.

You will also want another timing array to indicate when each key-frame pose occurs. This is just a one-dimensional array of time values. For example, if your key-frames happen to occur at regular intervals, every 3/4 of a second, then your timing array could be defined by:

double timing[] = {0.00, 0.75, 1.50, 2.25, ... };

There is no reason that key frames need to occur at regular intervals of time, but the time values do need to increase over time.

At any given moment in time, the algorithm to convert this table into numeric values is fairly straighforward:

  1. Figure out where the current time t is within the timing array;

  2. Compute the fractional amount within that time intervel of the current time (a value between 0.0 and 1.0);

  3. Within this time interval, linearly interpolate all the values in the key-frame array.

For example, consider our three shoulder/elbow/wrist parameters, as shown in the figure below. At a particular moment in the animation, the current time will lie in some interval between two key poses. We can linearly interpolate between pose values at the beginning and end of that interval to get the rotation values at that moment in time.

To figure out which is the correct interval for a given time t, simply loop through the timing array until you find an index i such that timing[i] <= t < timing[i+1]. If t < timing[0] then you can just use all the parameter values at the first index of the animation array. Similarly, if t runs off the end of the array, then just use all the parameter values at the last index of the animation array.

Once you have found the proper interval, then you need to compute the fractional position of the current time within the interval: fraction = (t - timing[i]) / (timing[i+1] - timing[i]).

Finally, for every animation parameter, linearly interpolate to get the current value for that parameter:

currentParamValue = lerp(fraction, keyPose[i][paramIndex], keyPose[i+1[paramIndex]);