天行健 君子当自强而不息

Using Key−Framed Skeletal Animation(2)

Working with the Four Key Types

Currently, there are four types of keys you can use in your animation sets, each signified by a value ranging from 0 to 4 that is listed in the .X file following the frame reference inside an AnimationKey template. These four keys and their respective values are:

Rotational keys (type 0). These are quaternion rotational values, stored using four components: w,x, y, and z.

Scaling keys (type 1). You can also use this type of key to animate scaling values. A scale key uses three components that represent the x, y, and z scaling values to use.
 
Translation keys (type 2). These keys specify a position in 3D space using three components that represent the x, y, and z coordinates. You can easily store these three values as a vector.

Transformation matrix keys (type 4). You can use this key to compound all transformations into matrices. This key uses 16 floats that represent a homogenous 4x4 transformation matrix that transforms a bone.

So getting back to the previous Animation data objects, you can see that the very first AnimationKey object (which affects the Bip01 bone) defines a transformation matrix key (represented by the value 4), as shown here:

{Bip01}
AnimationKey {
4;
3;
0; 16; 1.00000, 0.00000, 0.00000, 0.00000,
0.00000, 1.00000, 0.00000, 0.00000,
0.00000, 0.00000, 1.00000, 0.00000,
0.00000, 0.00000, 0.00000, 1.00000;;,
1000; 16; 1.00000, 0.00000, 0.00000, 0.00000,
0.00000, 1.00000, 0.00000, 0.00000,
0.00000, 0.00000, 1.00000, 0.00000,
0.00000, 0.00000, 0.00000, 1.00000;;,
2000; 16; 1.00000, 0.00000, 0.00000, 0.00000,
0.00000, 1.00000, 0.00000, 0.00000,
0.00000, 0.00000, 1.00000, 0.00000,
0.00000, 0.00000, 0.00000, 1.00000;;;
}

As for the second AnimationKey object (which affects the Bip01_LeftArm bone), there are three keys in use: translation (value 2), scaling (value 1), and rotation (value 0).

{Bip01_LeftArm}
AnimationKey {
0;
1;
0; 4; 1.00000, 0.000000, 0.00000, 0.000000;;;
}
AnimationKey {
1;
1;
0; 4; 1.000000, 1.00000, 1.000000;;;
}
AnimationKey {
2;
1;
0; 3; 0.000000, 0.00000, 0.000000;;;
}

As you may have surmised by now, you can have any number of AnimationKey objects per Animation object, with each AnimationKey object using one specific key type. Following the key's type value (0=rotational, 1=scaling, 2=position, 4=matrix) is the number of keys to use in the animation sequence for that specific bone. In the first bone's set (Bip01) there are three matrix type keys defined, whereas the remaining AnimationKey objects (that affect the Bip01_LeftArm bone) use only one key for each of the remaining transformation types (rotation, scaling, and position).

Next comes the key data. The first value for each key is the time value, which is specified using an arbitrary value that you choose (such as seconds, milliseconds, frames, or any other form of measurement you wish to use). In my examples, I always specify time as milliseconds. A number that defines how many key values are to follow comes after the time value. Take the following key data, for example:

AnimationKey {
  2; // Key type
  1; // # of keys
  0; // Key time
  3; // # of values to follow for key's data
  10.00000, 20.00000, 30.00000;;; // key's data
}

The first value, 2, means the key is used to contain translation animation keys. The 1 means there is one key to follow. The first and only key is located at time 0. The value 3 follows the time, which means that three more values (10, 20, and 30) are to follow. The three values represent the coordinates to use for that time in the animation.

Going back to the earlier example, you can see that the first animation key (the transformation matrix key) has three matrices that are used at times 0, 1000, and 2000. At those exact times during the animation, you will set the transformation matrix for the Bip01 bone.

For the time values between keys, you need to interpolate the matrices to come up with the correct transformations. In fact, you can interpolate all key types to get the correct values to use between keys. The easiest way to interpolate is to use the transformation matrix, scaling, translation, or rotation values from the animation keys, divide by the time between two keys, and multiply the result based on the time into the key. You saw me use linear interpolation for a transformation matrix in the previous section. Before long, I'll show you how to interpolate translation, scaling, and rotation values as well.

That's basically it for the AnimationKey! You just need to read in each key contained within your AnimationKey data objects and apply it to the proper bone transformations using the interpolated matrices over time.

Okay, enough of the animation templates, data objects, keys, and interpolation for now; we'll get back to that stuff in a bit. For now, let's get your hands into some real code and see how to load the animation data into your game. Then I'll get back to showing you how to work with the actual data.

 

Reading Animation Data from .X Files

You learned how to load meshes and frame hierarchies from an .X file, as well as how to use frame hierarchies to deform (modify) the mesh while rendering. This chapter's purpose is to teach you how to read in the animation data contained in an .X file so you can play back key−framed animations.

The first step to reading in the animation data is to look at the templates you'll be using and build a couple classes to contain the data from those templates' data objects. First, here are the templates that you'll be working with, along with their declarations:

template AnimationKey {
<10DD46A8−775B−11cf−8F52−0040333594A3>
DWORD keyType;
DWORD nKeys;
array TimedFloatKeys keys[nKeys];
}
template Animation {
<3D82AB4F−62DA−11cf−AB39−0020AF71E433>
[AnimationKey]
[AnimationOptions]
[...]
}
template AnimationSet {
<3D82AB50−62DA−11cf−AB39−0020AF71E433>
[Animation]
}

At the top of the list you have AnimationKey, which stores the type of animation key data, the number of key values to follow, and the key data itself, which is stored in an array of TimedFloatKey objects that store the time and an array of floating−point values in the form Time:NumValues:Values.

Data objects of the Animation template class type can store an AnimationKey object and an AnimationOptions object. Notice that the Animation template is left open because it needs a frame data object reference to match the animation key to a bone.

Last, there's the AnimationSet template, which only contains Animation objects. You can store any number of animations within an animation set; typically, you'll have one animation for each bone.

Note The AnimationOptions template, is highly useful if you want your artists to specify playback options. Inside the AnimationOptions
template, you'll find two variablesopenclosed and positionquality.If openclosed is set to 0, then the animation in which the object is embedded doesn't loop; a value of 1 means the animation loops. As for positionquality, setting it to a value of 0 means to use spline positions, whereas a value of 1 means to use linear positions. Typically, you'd set positionquality to 1.

You'll want to use some custom classes to store your animation data; those classes will pretty much mirror the Animation templates' data exactly. First, you want a class that contains the values of the various key types: scaling, translation, rotation, and transformation. The first two types, scaling and translation, both use a vector, so one class will suffice.

class cAnimationVectorKey
{
public:
  float m_Time;
  D3DXVECTOR3 m_vecKey;
};

Rotation keys use a quaternion (a four−dimensional vector).

class cAnimationQuaternionKey
{
public:
  float m_Time;
  D3DXQUATERNION m_quatKey;
};

Last, the transformation key uses a 4x4 homogenous matrix.

class cAnimationMatrixKey
{
public:
  float m_Time;
  D3DXMATRIX m_matKey;
};

So far, so good. Remember that each bone in your animation has its own list of keys to use, which is the purpose of the Animation template. For each bone in your hierarchy, there is a matching Animation data object. Your matching animation class will therefore contain the name of the bone to which it is connected, the number of keys for each type (translation, scaling, rotation, and transformation), a linked list data pointer, and a pointer to the bone (or frame) structure you're using in your hierarchy. Also, you need to include a constructor and destructor that clear out the class's data.

class cAnimation
{
public:
char *m_Name; // Bone's name
D3DXFRAME *m_Bone; // Pointer to bone
cAnimation *m_Next; // Next animation object in list
	// # each key type and array of each type's keys
	DWORD m_NumTranslationKeys;
cAnimationVectorKey *m_TranslationKeys;
	DWORD m_NumScaleKeys;
cAnimationVectorKey *m_ScaleKeys;
	DWORD m_NumRotationKeys;
cAnimationQuaternionKey *m_RotationKeys;
	DWORD m_NumMatrixKeys;
cAnimationMatrixKey *m_MatrixKeys;
public:
cAnimation();
~cAnimation();
};

Finally, the AnimationSet template contains the Animation objects for an entire bone hierarchy. At this point, all your animation set class needs to do is track an array of cAnimation classes (remember that each bone in the hierarchy has a matching cAnimation class), as well as the length of the complete animation.

class cAnimationSet
{
public:
char *m_Name; // Name of animation
DWORD m_Length; // Length of animation
cAnimationSet *m_Next; // Next set in linked list
DWORD m_NumAnimations;
cAnimation *m_Animations;
public:
cAnimationSet();
~cAnimationSet();
}

posted on 2008-04-24 18:50 lovedday 阅读(464) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论