This page was last updated: February 26, 2009
This project is about performing a keyframe animation. There are two ways you can do this to satisfy the requirements of this project.
If you are not sure how to approach this, do linear interpolation first. It is more intuitive and easier to code. And, most of the code is re-usable for the smooth interpolation.
The linear interpolation technique simply divides a straight line path from Keyframe #i to Keyframe #i+1 into the required number of intermediate frames and employs the linear blending equation to do the in-betweening. Suppose we are interpolating between Keyframe #i and Keyframe #i+1:
x = (1.-t) · X[i] + t · X[i+1] t = (float)( NowFrame - KeyFrame[i] ) / (float)( KeyFrame[i+1] - KeyFrame[i] ) 0.0 <= t <= 1.0 dt = 1. / (float)( KeyFrame[i+1] - KeyFrame[i] )
So, for example, to interpolate from keyframe #NowKeyFrame to keyframe #NowKeyFrame+1, you would need to do something like:
Global variables: int NowKeyFrame; // we are between keyframes NowKeyFrame and NowKeyFrame+1 int NowFrame; // the current frame number float X, Y, Z; float Thetax, Thetay, Thetaz; In your Idle Function: << Compute NowFrame based on the clock -- see below >> << Determine NowKeyFrame such that NowFrame falls between it and the next keyframe >> float framesInThisInterval = (float)( Frames[NowKeyFrame+1].f - Frames[NowKeyFrame].f ); float t = (float)( NowFrame - Frames[NowKeyFrame].f ) / framesInThisInterval; X = (1.-t) * Frames[NowKeyFrame].x + t * Frames[NowKeyFrame+1].x; Y = ...; Z = ...; Thetax = ...; Thetay = ...; Thetaz = ...; glutSetWindow( GraphicsWindow ); glutPostRedisplay(); At the end of InitGlui(): GLUI_Master.set_glutIdleFunc( Animate ); In Display(): glPushMatrix(); glTranslatef( X, Y, Z ); glRotatef( Thetax, 1., 0., 0. ); glRotatef( Thetay, 0., 1., 0. ); glRotatef( Thetaz, 0., 0., 1. ); glCallList( ObjectList ); glPopMatrix();
The smooth interpolation technique that you will be using employs the Coons cubic curve (end points and end slopes). But, rather than you having to specify all of the slopes, the technique makes a reasonable approximation of them based on the surrounding points.
For example, the curve's parametric derivative at a point can be computed by the calculus chain rule. For example, the x parametric derivative, dx/dt, would be:
dx/dF is the change in X per Frame and is taken over the interval between the previous keyframe and the next keyframe. dF/dt is the change in Frame per change in t and is taken over just the keyframe interval that we are looking at right now. Remember that this is only the X component -- you will also need to compute the Y, Z and the three theta components.
Suppose we are interpolating between keyframe #K and keyframe #K+1.
At keyframe #K:
And at keyframe #K+1:
So, for example, to interpolate from keyframe #NowKeyFrame to keyframe #NowKeyFrame+1, you would need to do something like:
Global variables:
int NowKeyFrame; // we are between keyframes NowKeyFrame and NowKeyFrame+1
int NowFrame; // the current frame number
struct keyframe
{
int f; // frame #
float x, y, z; // x, y, and z locations
float ax, ay, az; // angles in degrees
float hue; // hue of the object (s=v=1.)
float dxdf, dydf, dzdf; // derivatives
float daxdf, daydf, dazdf; // derivatives
float dhdf; // derivatives
} Frames[MAXKEYFRAMES] =
{
. . .
};
In your Idle Function:
<< Compute NowFrame based on the clock -- see below >>
<< Determine NowKeyFrame such that NowFrame falls between it and the next keyframe >>
Ax = Frames[NowKeyFrame].x
Bx = Frames[NowKeyFrame].dxdt
Cx = -3.*Frames[NowKeyFrame].x + 3.*Frames[NowKeyFrame+1].x -
2.*Frames[NowKeyFrame].dxdt - Frames[NowKeyFrame+1].dxdt
Dx = 2.*Frames[NowKeyFrame].x - 2.*Frames[NowKeyFrame+1].x +
Frames[NowKeyFrame].dxdt + Frames[NowKeyFrame+1].dxdt
float framesInThisInterval = (float)( Frames[NowKeyFrame+1].f - Frames[NowKeyFrame].f );
float t = (float)( NowFrame - Frames[NowKeyFrame].f ) / framesInThisInterval;
X = Ax + t * ( Bx + t * ( Cx + t * Dx ) );
Y = ...;
Z = ...;
Thetax = ...;
Thetay = ...;
Thetaz = ...;
glutSetWindow( GraphicsWindow );
glutPostRedisplay();
At the end of InitGlui():
GLUI_Master.set_glutIdleFunc( Animate );
In Display():
glPushMatrix();
glTranslatef( X, Y, Z );
glRotatef( Thetax, 1., 0., 0. );
glRotatef( Thetay, 0., 1., 0. );
glRotatef( Thetaz, 0., 0., 1. );
glCallList( ObjectList );
glPopMatrix();
Make your entire animation finish in exactly 10 seconds, no matter which system I run it on, and no matter what the window size is.
To do this, first define the cycle time in milliseconds:
// time in milliseconds for cycle to complete:
const int MSEC = { 10*1000 };
Then, convert the elapsed time into a global variable NowFrame
that ranges from 0 to MAXFRAMES throughout the animation.
int NowFrame;
...
if( AnimationIsOn )
{
// # msec into the cycle ( 0 - MSEC-1 ):
int msec = glutGet( GLUT_ELAPSED_TIME ) % MSEC;
// turn that into the current frame number:
NowFrame = (int)( (float)MAXFRAME * (float)msec / (float)MSEC );
}
You then figure out which pair of keyframes NowFrame lies between and interpolate in there.
Show the shadow of something on a floor and/or a wall.
You can cast the shadow using a scaling trick. First, draw the 3D object normally. Then, in English:
Remember that OpenGL transformations get programmed in the reverse order of the English order.
The shadow can be drawn as a black object. It can be drawn as a flat colored object. Or, it can be alpha-blended into the floor color. All of these are OK for this project.
| Item | Points |
| Something moves in a predictable way | 30 |
| Smooth transition at the keyframes | 20 |
| Animate all 7 quantities | 30 |
| Time the animation | 20 |
| Shadow Extra Credit | 5 |
| Potential Total | 105 |
This is here as an example only -- do not use these same parameters! Be more clever than Joe Graphics was.
// the keyframes:
struct keyframe
{
int f; // frame #
float x, y, z; // x, y, and z locations
float ax, ay, az; // angles in degrees
float hue; // hue of the object (s=v=1.)
float dxdf, dydf, dzdf; // derivatives
float daxdf, daydf, dazdf; // derivatives
float dhdf; // derivatives
} Frames[] =
{
{ 0, 20., 50., 0., 0., 0., 0., 0. },
{ 200, 650., 650., 600., 0., 0., 450., 120. },
{ 300, 500., 400., 400., 0., 0., 270., 240. },
{ 450, 80., 650., 800., -90., 180., 90., 300. },
{ 525, 400., 650., 0., 225., -180., 90., 540. },
{ 600, 650., 50., 200., 225., 0., 90., 180. },
{ 800, 20., 50., 0., 0., 0., 0., 0. },
{ -1 }
};