Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
114 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Loading and displaying .X files without DirectX


3. Animating the model

All that is left is to parse and display the animation sets of our model. The example code in Sample3.zip builds on the previous one to support animation information.

3.1. Design

3.1.1. Descriptions

Animating a skinned model is as simple as animating a cartoon character. The lead cartoonist draws key animation frames. Each of these key frames represents an important character pose at a precise time. If we were to look at key animation frames of the mouse Jerry (from Tom & Jerry), we might see 3 key frames:

  • Instant 0: Jerry hides a hammer behind its back.
  • Instant + 5 frames: Jerry brandishes the hammer over Tom's foot.
  • Instant + 15 frames: Jerry hammers Tom's foot.

The other cartoonists will use these key frames to draw all the intermediate frames (meaning they will draw frames 1 to 4 and 6 to 14).

The animation in a 3D model is only composed of key frames with a timing identification. The Key frame is a picture at a precise moment of the skeleton position. So a key frame can be represented by a set of local transform matrix describing all the bones orientation in character space. The local transform matrix can also be decomposed into:

  • a scaling vector,
  • a translation vector,
  • a quaternion rotation. A quaternion is a mathematical representation of a rotation around an axis as a 4 dimensional "vector".

Thus a local bone transformation can be either represented by a matrix or by a set of vectors and quaternions.

The programmer must calculate all the intermediate key frames by interpolating the key local bone transformations in relation to the time elapsed between two keyframes. The interpolation method is linear for matrices and vectors:

Interpolation = A + (B - A) * FrameID / DeltaKey

Where A and B are two key frames separated by DeltaKey frames, and FrameID is the frame number between both key frames.

Quaternions use spherical linear interpolation (aka SLERP):

Interpolation = A*[sin((1-Elapsed) * Angle)/sin(Angle)] + B * [sin (Elapsed * Angle) / sin(Angle)]

Where A and B are two key quaternion frames, Angle is the total angle between both quaternions and Elapsed = FrameID / DeltaKey.

In DirectX .X file, the animations are grouped into Animation Sets blocks. Within each Animation Set there is an Animation block for each animated bone. Each animation block is composed of one or many Animation Key blocks depending on the representation of the local bone transformations. The table below gives the correspondence between Key types and local bone transformation representations:

Key Type Representation
0 Quaternion rotation
1 Scaling vector
2 Translation vector
4 Matrix transformation

That's all we should know about skinned model animations.

Now let's define our list of desired functionalities:

  • Extract all the animation sets.
  • Map the animations to the model bones.
  • Calculate the animation matrices.
  • Display the animated model.
  • Switch between animation sets.

3.1.2. Code design

The design to store animation sets is straightforward:

The MaxKey member variable in the animation set class stores the last key frame timing index. Each animation set stores a list of animation description. An animation description corresponds to a bone. Inside an animation description, there is a list of each type of transformation representation possible. A transformation structure follows the same template as the Rotate Key below:

typedef struct {
   Uint32 Time;
   Frm::Quaternion Rotation
} RotateKey;
The responsibility to calculate the new local transformation matrix is given to our Object3D class:

Whoa! That looks complex. If we compare with the design in chapter 2, we see that Object3D takes an additional pointer to an animation set: the Object3D stores the current animation set played. It also has a new method MapAnimation. This method is called when the user switches animations to change the current animation set pointer and update the ObjectBone class.

The ObjectBone class gets a pointer to the animation class corresponding to its bone name reference. The Object3D method MapAnimation maps the animation to the ObjectBone. ObjectBone also gets a new method CalcAnimation that calculates the local transformation matrix from the current animation timing. So far, so good.

3.2. Implementation

3.2.1. Parsing the file

We add to the main parsing loop a new method to process animations sets. The pseudo-code:

    Create an instance of an AnimationSet object
    Get the Animation Set name
    If there is no name, we assign a name.
    While we have not reached the end of the block
      Read the block name (ProcessBlock)
        If the block name is recognised, we process it.
        Else avoid the block (AvoidTemplate)
    Once the animation set is processed, we add it to the model's list of sets.

Now let's process animation blocks:

    Create an instance of an Animation object
    While we have not reached the end of the block
      Read the block name (ProcessBlock)
        If the block name is recognised, we process it.
        If it is an open brace, we read in the Bone name
        Else avoid the block (AvoidTemplate)
    When the animation is processed, we add it to the AnimationSet animation list

There are animation key blocks inside each animation. We process the animation key block:

    Read in the type of transformation
    Read in the number of transformations stored in the animationkey
    Switch according to the type of transformation
      For each transformation listed
        Create a transformation key
        Read in the Timing
        Read in the transformation data
        Push the transformation key to the Animation corresponding transformation key.

Now we are ready.

3.2.2. Displaying a single animation set

Displaying the animated model is another matter. We setup as before in sample 2. The difference resides in that we call two new methods of Object3D for animation:

  • MapAnimationSet: there are two versions of that function. One uses a string as a parameter, the other an index number. This function will retrieve the animation set either by name or by index and map all the enclosed Animation instances to the ObjectBone hierarchy.
  • SetAnimationStep: sets up the step increase for the animation. At each timer call (or user keypress like in the sample), the animation set will advance the main animation set time counter by this step.

The drawing steps are almost the same:

    Clear the skinned mesh in Object3D
    Calculation the animation set local transform matrices
    Update the skinned mesh.

The new step is the calculation of each bone local transform matrices. This step calls a recursive private method CalcAnimation, which:

    Calls the animation method of the ObjectBone passed in parameter
    For each children ObjectBone
      Calls CalcAnimation(children ObjectBone)

The animation method of the ObjectBone does the following: If no animation instance is linked to that bone, bail out.

    If the transformations are expressed as matrices=
      Advance the internal matrix animation index according to the main Time count
      If we are at the last matrix index use the last matrix as a transform matrix
      Else interpolate the transform matrix between this matrix index and the next using the Time count parameter.
    Else
      Set the transform matrix to identity
      If there are rotations
        Advance the internal rotation animation index according to the main Time count
        If we are at the last rotation index multiply use the last quaternion
        Else interpolate the rotation quaternion between this rotation index and the next using the Time count parameter.
        Convert quaternion to a rotation matrix
        Multiply the transform matrix by the rotation matrix obtained
      If there are scalings
        Advance the internal scaling animation index according to the main Time count
        If we are at the last scaling index multiply use the last vector
        Else interpolate the scaling vector between this scaling index and the next using the Time count parameter.
        Convert the scaling vector to a scaling matrix
        Multiply the transform matrix by the scaling matrix obtained
        If there are translations
        Advance the internal translation animation index according to the main Time count
        If we are at the last translation index multiply use the last translation vector
        Else interpolate the translation vector between this translation index and the next using the Time count parameter.
        Convert the translation vector to a translation matrix
        Multiply the transform matrix by the translation matrix obtained

As you can see, we either use an interpolated matrix or a combined transformation matrix. Be aware that matrix multiplication is not commutative: the multiplication of rotation, scaling and translation matrices must always be done in that order.

Now that we have our new local transform matrices, we can calculate our final matrices as usual and obtain our animated skinned mesh.

Conclusion

Whew, that was a long tutorial. I hope you liked it. You are free to use the sample code as you see fit.

Enjoy!

Ghostly yours,
Red

References

[MSDN]
msdn.Microsoft.com/archive/en-us/directx9_c/directx/graphics/reference/fileformat/xfileformat.asp
This document describes the full format of a text and binary X File. You can also find this document in the DirectX SDK documentation.

[WML]
http://www.geometrictools.com/Mathematics.html
These are the source code for mathematics objects. I urge you to look at them since you can directly reuse them as such in your source code. (see the license http://www.magic-software.com/License/WildMagic.pdf)

[LUNA]
Skinned Mesh Character Animation with Direct3D 9.0b - FrankLuna
www.moon-labs.com
Very interesting article

[XBDEV]
www.xbdev.net/3dformats/x/xfileformat.php
Some tutorials and descriptions of 3D file formats. A good read.

[ADAMS]
Programming Role Playing Games with DirectX - Jim Adams - Premier Press
ISBN: 1-931841-09-8





Contents
  Introduction
  Bone hierarchy and skinning
  Animating the model

  Printable version
  Discuss this article