Matching Animations to Bones
After you've loaded the animation data, you need to map the animation classes
to their respective bones in the bone hierarchy. Mapping the hierarchies is
important because whenever an animation is updated, you need a quick way to
access the bone's transformations. By mapping, you create an easier method of
accessing the bones.
In this instance, the bone hierarchy will be represented in a D3DXFRAME
hierarchy. Inside the D3DXFRAME structure, there are two linked list pointers
that you'll use to help construct the hierarchy. From the root D3DXFRAME
structure you are using, you can access child objects through the
D3DXFRAME::pFrameFirstChild pointer and sibling objects through the
D3DXFRAME::pFrameSibling pointer.
The next function in cAnimationCollection to which you want to pay attention
is Map. You use the Map function to map the animation structure's m_Bone pointer
to a frame in the frame hierarchy that shares the same name.
The Map function scans through every cAnimationSet object and iterates every
cAnimation object contained in each of the animation set objects. The name of
each cAnimation object is compared to each of the frame's names; if a match is
found, the cAnimation::m_Bone pointer is set to the frame's address.
The Map function takes the hierarchy's root frame parameter.
void cAnimationCollection::Map(D3DXFRAME *RootFrame)
{
// Go through each animation set
cAnimationSet *AnimSet = m_AnimationSets;
while(AnimSet != NULL)
{
// Go through each animation object
cAnimation *Anim = AnimSet−>m_Animations;
while(Anim != NULL)
{
// Go through all frames and look for match
Anim−>m_Bone = FindFrame(RootFrame, Anim−>m_Name);
// Go to next animation object
Anim = Anim−>m_Next;
}
// Go to next animation set object
AnimSet = AnimSet−>m_Next;
}
}
Whereas the Map function only scans through each of the cAnimationSet and
cAnimation objects, the FindFrame function recursively works through the frame
hierarchy to look for a match to the name you provide. When it finds a matching
name, the FindFrame function returns the pointer to the specific frame. Take a
look at the FindFrame code on which the Map function depends.
D3DXFRAME *cAnimationCollection::FindFrame(D3DXFRAME *Frame, char *Name)
{
D3DXFRAME *FramePtr;
// Return NULL if no frame
if(!Frame)
return NULL;
// Return current frame if no name used
if(!Name)
return Frame;
// Process child frames
if((FramePtr = FindFrame(Frame−>pFrameFirstChild, Name)))
return FramePtr;
// Process sibling frames
if((FramePtr = FindFrame(Frame−>pFrameSibling, Name)))
return FramePtr;
// Nothing found
return NULL;
}
Again, take a deep breath. The animation data has been loaded, and you've
mapped the animation objects to the bone hierarchy. All that's left to do is
update the animation and set the transformation matrices for the bones.
Updating Animations
After you've matched the animation classes to the bone hierarchy, you can
begin animating your meshes! All you have to do is scan the animation keys for
each bone, applying the interpolated transformations to each bone's
transformation before rendering. This is merely a matter of iterating through
each animation class and its keys to find the proper key values to use.
Going back to the cAnimationCollection class, you can see that one function
will do all that for you. By supplying the cAnimationCollection::Update function
with the name of the animation set you want to use, as well as the time in the
animation, all of the transformation matrices in your entire mapped bone
hierarchy will be set and ready for rendering.
Take a closer look at the Update function to see how you can update your
animation data.
void cAnimationCollection::Update(char *AnimationSetName, DWORD Time)
{
cAnimationSet *AnimSet = m_AnimationSets;
DWORD i, Key, Key2;
// Look for matching animation set name if used
if(AnimationSetName)
{
// Find matching animation set name
while(AnimSet != NULL)
{
// Break when match found
if(!stricmp(AnimSet−>m_Name, AnimationSetName))
break;
// Go to next animation set object
AnimSet = AnimSet−>m_Next;
}
}
// Return no set found
if(AnimSet == NULL)
return;
The Update function starts by scanning the list of animation sets loaded into
the linked list. If you instead supply a NULL value for AnimationSetName, Update
will merely use the first animation set in the list (which happens to be the
last set loaded). If no matching sets are found using the name you specified,
the function returns without further delay.
Once a matching animation set is found, however, the code continues by
scanning each cAnimation object in it. For each animation object, the entire
list of keys (translation, scaling, rotation, and transformation) is searched,
and the time you specify is checked to see which key to use.
After you've found the proper key to use, the values (rotation, scaling,
translation, or transformation) are interpolated, and a final transformation
matrix is computed. This final transformation matrix is then stored in the
mapped bone (as pointed to by the m_Bone pointer).
You've already seen how to scan a list of keys to look for the ones between
which a specific time falls, so I'll skip the code here.
Once you've calculated the transformations to apply to each bone from the
animation data, you can jump right back into the game and render the mesh.
Remember, you must apply the transformation matrices for each bone to the
appropriate vertices in the mesh, and the best way to do so is to use a vertex
shader.