Using Key−Framed Skeletal Animation(5)
Check Out the Demos
In this chapter, you learned how to load animation sets and use that data to animate your on−screen meshes. To better demonstrate these animation concepts, I have created a program (SkeletalAnim) that shows your favorite lady of skeletal−based animation, Microsoft's Tiny (from the DirectX SDK samples), doing what she does best walking around! When you run the demo application, you'll be greeted with a scene like the one shown in Figure 5.3.
Figure 5.3: Tiny on the move in the SkeletalAnim demo! This demo shows you how to use skeletal−based animated meshes.
SkeletalAnim.h:
#ifndef SKELETAL_ANIM_H
#define SKELETAL_ANIM_H
#include < windows.h >
#include " Direct3D.h "
#include " XParser.h "
// =====================================================================================
// Animation key type data structures
// =====================================================================================
struct sAnimVectorKey
{
DWORD time;
D3DXVECTOR3 vec;
};
struct sAnimQuatKey
{
DWORD time;
D3DXQUATERNION quat;
};
struct sAnimMatrixKey
{
DWORD time;
D3DXMATRIX matrix;
};
// =====================================================================================
// Animation structures
// =====================================================================================
struct sAnimation
{
char * bone_name;
D3DXFRAME_EX * bone; // pointer to bone frame
sAnimation * next; // next animation object in list
// each key type and array of each type's keys
DWORD num_translation_keys;
sAnimVectorKey * translation_keys;
DWORD num_scale_keys;
sAnimVectorKey * scale_keys;
DWORD num_rotation_keys;
sAnimQuatKey * rotation_keys;
DWORD num_matrix_keys;
sAnimMatrixKey * matrix_keys;
sAnimation()
{
ZeroMemory( this , sizeof ( * this ));
}
~ sAnimation()
{
delete[] bone_name; bone_name = NULL;
delete[] translation_keys;
delete[] scale_keys;
delete[] rotation_keys;
delete[] matrix_keys;
delete next; next = NULL;
}
};
// =====================================================================================
// Animation set is container of animation.
// =====================================================================================
struct sAnimationSet
{
char * name; // name of animation set
DWORD length; // length of animation
sAnimationSet * next;
DWORD num_anims;
sAnimation * anims;
sAnimationSet()
{
ZeroMemory( this , sizeof ( * this ));
}
~ sAnimationSet()
{
delete[] name; name = NULL;
delete anims; anims = NULL;
delete next; next = NULL;
}
};
// =====================================================================================
// Parse animation data from X file.
// =====================================================================================
class cAnimationCollection : public cXParser
{
protected :
DWORD m_num_anim_sets;
sAnimationSet * m_anim_sets;
protected :
virtual bool parse_objects(ID3DXFileData * xfile_data,
ID3DXFileData * parent_xfile_data,
DWORD depth,
void ** data,
bool force_ref);
public :
cAnimationCollection()
{
m_num_anim_sets = 0 ;
m_anim_sets = NULL;
}
~ cAnimationCollection()
{
free();
}
void free()
{
m_num_anim_sets = 0 ;
delete m_anim_sets; m_anim_sets = NULL;
}
bool load( const char * filename)
{
free(); // free a prior loaded collection
return parse(filename, NULL);
}
void map_frames(D3DXFRAME_EX * root_frame);
void update( const char * anim_set_name, DWORD time, bool is_loop);
};
#endif
#define SKELETAL_ANIM_H
#include < windows.h >
#include " Direct3D.h "
#include " XParser.h "
// =====================================================================================
// Animation key type data structures
// =====================================================================================
struct sAnimVectorKey
{
DWORD time;
D3DXVECTOR3 vec;
};
struct sAnimQuatKey
{
DWORD time;
D3DXQUATERNION quat;
};
struct sAnimMatrixKey
{
DWORD time;
D3DXMATRIX matrix;
};
// =====================================================================================
// Animation structures
// =====================================================================================
struct sAnimation
{
char * bone_name;
D3DXFRAME_EX * bone; // pointer to bone frame
sAnimation * next; // next animation object in list
// each key type and array of each type's keys
DWORD num_translation_keys;
sAnimVectorKey * translation_keys;
DWORD num_scale_keys;
sAnimVectorKey * scale_keys;
DWORD num_rotation_keys;
sAnimQuatKey * rotation_keys;
DWORD num_matrix_keys;
sAnimMatrixKey * matrix_keys;
sAnimation()
{
ZeroMemory( this , sizeof ( * this ));
}
~ sAnimation()
{
delete[] bone_name; bone_name = NULL;
delete[] translation_keys;
delete[] scale_keys;
delete[] rotation_keys;
delete[] matrix_keys;
delete next; next = NULL;
}
};
// =====================================================================================
// Animation set is container of animation.
// =====================================================================================
struct sAnimationSet
{
char * name; // name of animation set
DWORD length; // length of animation
sAnimationSet * next;
DWORD num_anims;
sAnimation * anims;
sAnimationSet()
{
ZeroMemory( this , sizeof ( * this ));
}
~ sAnimationSet()
{
delete[] name; name = NULL;
delete anims; anims = NULL;
delete next; next = NULL;
}
};
// =====================================================================================
// Parse animation data from X file.
// =====================================================================================
class cAnimationCollection : public cXParser
{
protected :
DWORD m_num_anim_sets;
sAnimationSet * m_anim_sets;
protected :
virtual bool parse_objects(ID3DXFileData * xfile_data,
ID3DXFileData * parent_xfile_data,
DWORD depth,
void ** data,
bool force_ref);
public :
cAnimationCollection()
{
m_num_anim_sets = 0 ;
m_anim_sets = NULL;
}
~ cAnimationCollection()
{
free();
}
void free()
{
m_num_anim_sets = 0 ;
delete m_anim_sets; m_anim_sets = NULL;
}
bool load( const char * filename)
{
free(); // free a prior loaded collection
return parse(filename, NULL);
}
void map_frames(D3DXFRAME_EX * root_frame);
void update( const char * anim_set_name, DWORD time, bool is_loop);
};
#endif
SkeletalAnim.cpp:
#include
<
d3dx9xof.h
>
#include " XTemplate.h "
#include " SkeletalAnim.h "
#pragma warning(disable : 4996 )
bool cAnimationCollection::parse_objects(ID3DXFileData * xfile_data,
ID3DXFileData * parent_xfile_data,
DWORD depth, void ** data, bool force_ref)
{
GUID type;
get_object_guid(xfile_data, & type);
if (type == TID_D3DRMAnimationSet)
{
// create and link in a sAnimationSet object
sAnimationSet * anim_set = new sAnimationSet;
anim_set -> next = m_anim_sets;
m_anim_sets = anim_set;
m_num_anim_sets ++ ;
anim_set -> name = get_object_name(xfile_data);
}
else if (type == TID_D3DRMAnimation && m_anim_sets)
{
// add a sAnimation to top-level sAnimationSet
sAnimation * anim = new sAnimation;
anim -> next = m_anim_sets -> anims;
m_anim_sets -> anims = anim;
m_anim_sets -> num_anims ++ ;
}
else if (type == TID_D3DRMFrame && force_ref == true && m_anim_sets && m_anim_sets -> anims)
{
// a frame reference inside animation template
if (parent_xfile_data)
{
GUID parent_type;
get_object_guid(parent_xfile_data, & parent_type);
// make sure parent object is an animation template
if (parent_type == TID_D3DRMAnimation)
m_anim_sets -> anims -> bone_name = get_object_name(xfile_data);
}
return true ; // do not process child of reference frames
}
else if (type == TID_D3DRMAnimationKey && m_anim_sets && m_anim_sets -> anims)
{
sAnimation * anim = m_anim_sets -> anims;
SIZE_T size;
DWORD * data_ptr;
xfile_data -> Lock( & size, (LPCVOID * ) & data_ptr);
DWORD type = * data_ptr ++ ;
DWORD num_keys = * data_ptr ++ ;
// branch based on key type
switch (type)
{
case 0 : // rotation
delete[] anim -> rotation_keys;
anim -> num_rotation_keys = num_keys;
anim -> rotation_keys = new sAnimQuatKey[num_keys];
for (DWORD i = 0 ; i < num_keys; i ++ )
{
anim -> rotation_keys[i].time = * data_ptr ++ ;
if (anim -> rotation_keys[i].time > m_anim_sets -> length)
m_anim_sets -> length = anim -> rotation_keys[i].time;
data_ptr ++ ; // skip number of keys to follow (should be 4)
// quaternion data stored with w,x,y,z order in xfile, so can not cast directly to assigned!
float * float_ptr = ( float * ) data_ptr;
anim -> rotation_keys[i].quat.w = * float_ptr ++ ;
anim -> rotation_keys[i].quat.x = * float_ptr ++ ;
anim -> rotation_keys[i].quat.y = * float_ptr ++ ;
anim -> rotation_keys[i].quat.z = * float_ptr ++ ;
data_ptr += 4 ;
}
break ;
case 1 : // scaling
delete[] anim -> scale_keys;
anim -> num_scale_keys = num_keys;
anim -> scale_keys = new sAnimVectorKey[num_keys];
for (DWORD i = 0 ; i < num_keys; i ++ )
{
anim -> scale_keys[i].time = * data_ptr ++ ;
if (anim -> scale_keys[i].time > m_anim_sets -> length)
m_anim_sets -> length = anim -> scale_keys[i].time;
data_ptr ++ ; // skip number of keys to follow (should be 3)
anim -> scale_keys[i].vec = * ((D3DXVECTOR3 * ) data_ptr);
data_ptr += 3 ;
}
break ;
case 2 : // translation
delete[] anim -> translation_keys;
anim -> num_translation_keys = num_keys;
anim -> translation_keys = new sAnimVectorKey[num_keys];
for (DWORD i = 0 ; i < num_keys; i ++ )
{
anim -> translation_keys[i].time = * data_ptr ++ ;
if (anim -> translation_keys[i].time > m_anim_sets -> length)
m_anim_sets -> length = anim -> translation_keys[i].time;
data_ptr ++ ; // skip number of keys to follow (should be 3)
anim -> translation_keys[i].vec = * ((D3DXVECTOR3 * ) data_ptr);
data_ptr += 3 ;
}
break ;
case 4 : // transformation matrix
delete[] anim -> matrix_keys;
anim -> num_matrix_keys = num_keys;
anim -> matrix_keys = new sAnimMatrixKey[num_keys];
for (DWORD i = 0 ; i < num_keys; i ++ )
{
anim -> matrix_keys[i].time = * data_ptr ++ ;
if (anim -> matrix_keys[i].time > m_anim_sets -> length)
m_anim_sets -> length = anim -> matrix_keys[i].time;
data_ptr ++ ; // skip number of keys to follow (should be 16)
anim -> matrix_keys[i].matrix = * ((D3DXMATRIX * ) data_ptr);
data_ptr += 16 ;
}
break ;
}
xfile_data -> Unlock();
}
return parse_child_objects(xfile_data, depth, data, force_ref);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void cAnimationCollection::map_frames(D3DXFRAME_EX * root_frame)
{
for (sAnimationSet * anim_set = m_anim_sets; anim_set != NULL; anim_set = anim_set -> next)
{
for (sAnimation * anim = anim_set -> anims; anim != NULL; anim = anim -> next)
anim -> bone = root_frame -> find(anim -> bone_name);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void cAnimationCollection::update( const char * anim_set_name, DWORD time, bool is_loop)
{
sAnimationSet * anim_set = m_anim_sets;
// look for matching animation set if name used
if (anim_set_name)
{
// find matching animation set name
while (anim_set != NULL)
{
// break when match found
if ( ! stricmp(anim_set -> name, anim_set_name))
break ;
anim_set = anim_set -> next;
}
}
if (anim_set == NULL) // no animation set found
return ;
// bounds time to animation length
if (time > anim_set -> length)
time = is_loop ? (time % (anim_set -> length + 1 )) : anim_set -> length;
for (sAnimation * anim = anim_set -> anims; anim != NULL; anim = anim -> next)
{
if (anim -> bone == NULL) // only process if it is attached to a bone
continue ;
// reset transformation
D3DXMatrixIdentity( & anim -> bone -> TransformationMatrix);
// apply various matrices to transformation
// scaling
if (anim -> num_scale_keys && anim -> scale_keys)
{
DWORD key1 = 0 , key2 = 0 ;
// loop for matching scale key
for (DWORD i = 0 ; i < anim -> num_scale_keys; i ++ )
{
if (time >= anim -> scale_keys[i].time)
key1 = i;
}
key2 = (key1 >= (anim -> num_scale_keys - 1 )) ? key1 : key1 + 1 ;
DWORD time_diff = anim -> scale_keys[key2].time - anim -> scale_keys[key1].time;
if (time_diff == 0 )
time_diff = 1 ;
float scalar = ( float )(time - anim -> scale_keys[key1].time) / time_diff;
// calculate interpolated scale values
D3DXVECTOR3 scale_vec = anim -> scale_keys[key2].vec - anim -> scale_keys[key1].vec;
scale_vec *= scalar;
scale_vec += anim -> scale_keys[key1].vec;
// create scale matrix and combine with transformation
D3DXMATRIX scale_matrix;
D3DXMatrixScaling( & scale_matrix, scale_vec.x, scale_vec.y, scale_vec.z);
anim -> bone -> TransformationMatrix *= scale_matrix;
}
// rotation
if (anim -> num_rotation_keys && anim -> rotation_keys)
{
DWORD key1 = 0 , key2 = 0 ;
// loop for matching rotation key
for (DWORD i = 0 ; i < anim -> num_rotation_keys; i ++ )
{
if (time >= anim -> rotation_keys[i].time)
key1 = i;
}
key2 = (key1 >= (anim -> num_rotation_keys - 1 )) ? key1 : key1 + 1 ;
DWORD time_diff = anim -> rotation_keys[key2].time - anim -> rotation_keys[key1].time;
if (time_diff == 0 )
time_diff = 1 ;
float scalar = ( float )(time - anim -> rotation_keys[key1].time) / time_diff;
// slerp rotation values
D3DXQUATERNION rot_quat;
D3DXQuaternionSlerp( & rot_quat, & anim -> rotation_keys[key1].quat, & anim -> rotation_keys[key2].quat, scalar);
// create rotation matrix and combine with transformation
D3DXMATRIX rot_matrix;
D3DXMatrixRotationQuaternion( & rot_matrix, & rot_quat);
anim -> bone -> TransformationMatrix *= rot_matrix;
}
// translation
if (anim -> num_translation_keys && anim -> translation_keys)
{
DWORD key1 = 0 , key2 = 0 ;
// loop for matching translation key
for (DWORD i = 0 ; i < anim -> num_translation_keys; i ++ )
{
if (time >= anim -> translation_keys[i].time)
key1 = i;
}
key2 = (key1 >= (anim -> num_matrix_keys - 1 )) ? key1 : key1 + 1 ;
DWORD time_diff = anim -> translation_keys[key2].time - anim -> translation_keys[key1].time;
if (time_diff == 0 )
time_diff = 1 ;
float scalar = ( float )(time - anim -> translation_keys[key1].time) / time_diff;
// calculate interpolated vector values
D3DXVECTOR3 pos_vec = anim -> translation_keys[key2].vec - anim -> translation_keys[key1].vec;
pos_vec *= scalar;
pos_vec += anim -> translation_keys[key1].vec;
// create translation matrix and combine with transformation
D3DXMATRIX translation_matrix;
D3DXMatrixTranslation( & translation_matrix, pos_vec.x, pos_vec.y, pos_vec.z);
anim -> bone -> TransformationMatrix *= translation_matrix;
}
// matrix
if (anim -> num_matrix_keys && anim -> matrix_keys)
{
DWORD key1 = 0 , key2 = 0 ;
// loop for matching matrix key
for (DWORD i = 0 ; i < anim -> num_matrix_keys; i ++ )
{
if (time >= anim -> matrix_keys[i].time)
key1 = i;
}
key2 = (key1 >= (anim -> num_matrix_keys - 1 )) ? key1 : key1 + 1 ;
DWORD time_diff = anim -> matrix_keys[key2].time - anim -> matrix_keys[key1].time;
if (time_diff == 0 )
time_diff = 1 ;
float scalar = ( float )(time - anim -> matrix_keys[key1].time) / time_diff;
// calculate interpolated matrix
D3DXMATRIX diff_matrix = anim -> matrix_keys[key2].matrix - anim -> matrix_keys[key1].matrix;
diff_matrix *= scalar;
diff_matrix += anim -> matrix_keys[key1].matrix;
// combine with transformation
anim -> bone -> TransformationMatrix *= diff_matrix;
}
}
}
#include " XTemplate.h "
#include " SkeletalAnim.h "
#pragma warning(disable : 4996 )
bool cAnimationCollection::parse_objects(ID3DXFileData * xfile_data,
ID3DXFileData * parent_xfile_data,
DWORD depth, void ** data, bool force_ref)
{
GUID type;
get_object_guid(xfile_data, & type);
if (type == TID_D3DRMAnimationSet)
{
// create and link in a sAnimationSet object
sAnimationSet * anim_set = new sAnimationSet;
anim_set -> next = m_anim_sets;
m_anim_sets = anim_set;
m_num_anim_sets ++ ;
anim_set -> name = get_object_name(xfile_data);
}
else if (type == TID_D3DRMAnimation && m_anim_sets)
{
// add a sAnimation to top-level sAnimationSet
sAnimation * anim = new sAnimation;
anim -> next = m_anim_sets -> anims;
m_anim_sets -> anims = anim;
m_anim_sets -> num_anims ++ ;
}
else if (type == TID_D3DRMFrame && force_ref == true && m_anim_sets && m_anim_sets -> anims)
{
// a frame reference inside animation template
if (parent_xfile_data)
{
GUID parent_type;
get_object_guid(parent_xfile_data, & parent_type);
// make sure parent object is an animation template
if (parent_type == TID_D3DRMAnimation)
m_anim_sets -> anims -> bone_name = get_object_name(xfile_data);
}
return true ; // do not process child of reference frames
}
else if (type == TID_D3DRMAnimationKey && m_anim_sets && m_anim_sets -> anims)
{
sAnimation * anim = m_anim_sets -> anims;
SIZE_T size;
DWORD * data_ptr;
xfile_data -> Lock( & size, (LPCVOID * ) & data_ptr);
DWORD type = * data_ptr ++ ;
DWORD num_keys = * data_ptr ++ ;
// branch based on key type
switch (type)
{
case 0 : // rotation
delete[] anim -> rotation_keys;
anim -> num_rotation_keys = num_keys;
anim -> rotation_keys = new sAnimQuatKey[num_keys];
for (DWORD i = 0 ; i < num_keys; i ++ )
{
anim -> rotation_keys[i].time = * data_ptr ++ ;
if (anim -> rotation_keys[i].time > m_anim_sets -> length)
m_anim_sets -> length = anim -> rotation_keys[i].time;
data_ptr ++ ; // skip number of keys to follow (should be 4)
// quaternion data stored with w,x,y,z order in xfile, so can not cast directly to assigned!
float * float_ptr = ( float * ) data_ptr;
anim -> rotation_keys[i].quat.w = * float_ptr ++ ;
anim -> rotation_keys[i].quat.x = * float_ptr ++ ;
anim -> rotation_keys[i].quat.y = * float_ptr ++ ;
anim -> rotation_keys[i].quat.z = * float_ptr ++ ;
data_ptr += 4 ;
}
break ;
case 1 : // scaling
delete[] anim -> scale_keys;
anim -> num_scale_keys = num_keys;
anim -> scale_keys = new sAnimVectorKey[num_keys];
for (DWORD i = 0 ; i < num_keys; i ++ )
{
anim -> scale_keys[i].time = * data_ptr ++ ;
if (anim -> scale_keys[i].time > m_anim_sets -> length)
m_anim_sets -> length = anim -> scale_keys[i].time;
data_ptr ++ ; // skip number of keys to follow (should be 3)
anim -> scale_keys[i].vec = * ((D3DXVECTOR3 * ) data_ptr);
data_ptr += 3 ;
}
break ;
case 2 : // translation
delete[] anim -> translation_keys;
anim -> num_translation_keys = num_keys;
anim -> translation_keys = new sAnimVectorKey[num_keys];
for (DWORD i = 0 ; i < num_keys; i ++ )
{
anim -> translation_keys[i].time = * data_ptr ++ ;
if (anim -> translation_keys[i].time > m_anim_sets -> length)
m_anim_sets -> length = anim -> translation_keys[i].time;
data_ptr ++ ; // skip number of keys to follow (should be 3)
anim -> translation_keys[i].vec = * ((D3DXVECTOR3 * ) data_ptr);
data_ptr += 3 ;
}
break ;
case 4 : // transformation matrix
delete[] anim -> matrix_keys;
anim -> num_matrix_keys = num_keys;
anim -> matrix_keys = new sAnimMatrixKey[num_keys];
for (DWORD i = 0 ; i < num_keys; i ++ )
{
anim -> matrix_keys[i].time = * data_ptr ++ ;
if (anim -> matrix_keys[i].time > m_anim_sets -> length)
m_anim_sets -> length = anim -> matrix_keys[i].time;
data_ptr ++ ; // skip number of keys to follow (should be 16)
anim -> matrix_keys[i].matrix = * ((D3DXMATRIX * ) data_ptr);
data_ptr += 16 ;
}
break ;
}
xfile_data -> Unlock();
}
return parse_child_objects(xfile_data, depth, data, force_ref);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void cAnimationCollection::map_frames(D3DXFRAME_EX * root_frame)
{
for (sAnimationSet * anim_set = m_anim_sets; anim_set != NULL; anim_set = anim_set -> next)
{
for (sAnimation * anim = anim_set -> anims; anim != NULL; anim = anim -> next)
anim -> bone = root_frame -> find(anim -> bone_name);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void cAnimationCollection::update( const char * anim_set_name, DWORD time, bool is_loop)
{
sAnimationSet * anim_set = m_anim_sets;
// look for matching animation set if name used
if (anim_set_name)
{
// find matching animation set name
while (anim_set != NULL)
{
// break when match found
if ( ! stricmp(anim_set -> name, anim_set_name))
break ;
anim_set = anim_set -> next;
}
}
if (anim_set == NULL) // no animation set found
return ;
// bounds time to animation length
if (time > anim_set -> length)
time = is_loop ? (time % (anim_set -> length + 1 )) : anim_set -> length;
for (sAnimation * anim = anim_set -> anims; anim != NULL; anim = anim -> next)
{
if (anim -> bone == NULL) // only process if it is attached to a bone
continue ;
// reset transformation
D3DXMatrixIdentity( & anim -> bone -> TransformationMatrix);
// apply various matrices to transformation
// scaling
if (anim -> num_scale_keys && anim -> scale_keys)
{
DWORD key1 = 0 , key2 = 0 ;
// loop for matching scale key
for (DWORD i = 0 ; i < anim -> num_scale_keys; i ++ )
{
if (time >= anim -> scale_keys[i].time)
key1 = i;
}
key2 = (key1 >= (anim -> num_scale_keys - 1 )) ? key1 : key1 + 1 ;
DWORD time_diff = anim -> scale_keys[key2].time - anim -> scale_keys[key1].time;
if (time_diff == 0 )
time_diff = 1 ;
float scalar = ( float )(time - anim -> scale_keys[key1].time) / time_diff;
// calculate interpolated scale values
D3DXVECTOR3 scale_vec = anim -> scale_keys[key2].vec - anim -> scale_keys[key1].vec;
scale_vec *= scalar;
scale_vec += anim -> scale_keys[key1].vec;
// create scale matrix and combine with transformation
D3DXMATRIX scale_matrix;
D3DXMatrixScaling( & scale_matrix, scale_vec.x, scale_vec.y, scale_vec.z);
anim -> bone -> TransformationMatrix *= scale_matrix;
}
// rotation
if (anim -> num_rotation_keys && anim -> rotation_keys)
{
DWORD key1 = 0 , key2 = 0 ;
// loop for matching rotation key
for (DWORD i = 0 ; i < anim -> num_rotation_keys; i ++ )
{
if (time >= anim -> rotation_keys[i].time)
key1 = i;
}
key2 = (key1 >= (anim -> num_rotation_keys - 1 )) ? key1 : key1 + 1 ;
DWORD time_diff = anim -> rotation_keys[key2].time - anim -> rotation_keys[key1].time;
if (time_diff == 0 )
time_diff = 1 ;
float scalar = ( float )(time - anim -> rotation_keys[key1].time) / time_diff;
// slerp rotation values
D3DXQUATERNION rot_quat;
D3DXQuaternionSlerp( & rot_quat, & anim -> rotation_keys[key1].quat, & anim -> rotation_keys[key2].quat, scalar);
// create rotation matrix and combine with transformation
D3DXMATRIX rot_matrix;
D3DXMatrixRotationQuaternion( & rot_matrix, & rot_quat);
anim -> bone -> TransformationMatrix *= rot_matrix;
}
// translation
if (anim -> num_translation_keys && anim -> translation_keys)
{
DWORD key1 = 0 , key2 = 0 ;
// loop for matching translation key
for (DWORD i = 0 ; i < anim -> num_translation_keys; i ++ )
{
if (time >= anim -> translation_keys[i].time)
key1 = i;
}
key2 = (key1 >= (anim -> num_matrix_keys - 1 )) ? key1 : key1 + 1 ;
DWORD time_diff = anim -> translation_keys[key2].time - anim -> translation_keys[key1].time;
if (time_diff == 0 )
time_diff = 1 ;
float scalar = ( float )(time - anim -> translation_keys[key1].time) / time_diff;
// calculate interpolated vector values
D3DXVECTOR3 pos_vec = anim -> translation_keys[key2].vec - anim -> translation_keys[key1].vec;
pos_vec *= scalar;
pos_vec += anim -> translation_keys[key1].vec;
// create translation matrix and combine with transformation
D3DXMATRIX translation_matrix;
D3DXMatrixTranslation( & translation_matrix, pos_vec.x, pos_vec.y, pos_vec.z);
anim -> bone -> TransformationMatrix *= translation_matrix;
}
// matrix
if (anim -> num_matrix_keys && anim -> matrix_keys)
{
DWORD key1 = 0 , key2 = 0 ;
// loop for matching matrix key
for (DWORD i = 0 ; i < anim -> num_matrix_keys; i ++ )
{
if (time >= anim -> matrix_keys[i].time)
key1 = i;
}
key2 = (key1 >= (anim -> num_matrix_keys - 1 )) ? key1 : key1 + 1 ;
DWORD time_diff = anim -> matrix_keys[key2].time - anim -> matrix_keys[key1].time;
if (time_diff == 0 )
time_diff = 1 ;
float scalar = ( float )(time - anim -> matrix_keys[key1].time) / time_diff;
// calculate interpolated matrix
D3DXMATRIX diff_matrix = anim -> matrix_keys[key2].matrix - anim -> matrix_keys[key1].matrix;
diff_matrix *= scalar;
diff_matrix += anim -> matrix_keys[key1].matrix;
// combine with transformation
anim -> bone -> TransformationMatrix *= diff_matrix;
}
}
}
WinMain.cpp:
#include
<
windows.h
>
#include < d3d9.h >
#include < d3dx9.h >
#include " Direct3D.h "
#include " SkeletalAnim.h "
IDirect3D9 * g_d3d;
IDirect3DDevice9 * g_device;
D3DXMESHCONTAINER_EX * g_mesh_container;
D3DXFRAME_EX * g_frame;
cAnimationCollection g_anim_collection;
float g_mesh_radius = 0.0f ; // bounding radius of mesh
const char CLASS_NAME[] = " SkeletalAnimClass " ;
const char CAPTION[] = " Skeletal Animation Demo " ;
////////////////////////////////////////////////////////////////////////////////////////////////
LRESULT FAR PASCAL window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
bool do_init(HWND hwnd);
void do_shutdown();
void do_frame();
///////////////////////////////////////////////////////////////////////////////////////////// /
int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR, int cmd_show)
{
CoInitialize(NULL); // Initialize the COM system
// Create the window class here and register it
WNDCLASSEX win_class;
win_class.cbSize = sizeof (win_class);
win_class.style = CS_CLASSDC;
win_class.lpfnWndProc = window_proc;
win_class.cbClsExtra = 0 ;
win_class.cbWndExtra = 0 ;
win_class.hInstance = inst;
win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
win_class.hbrBackground = NULL;
win_class.lpszMenuName = NULL;
win_class.lpszClassName = CLASS_NAME;
win_class.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if ( ! RegisterClassEx( & win_class))
return - 1 ;
// Create the main window
HWND hwnd = CreateWindow(CLASS_NAME, CAPTION, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
0 , 0 , 640 , 480 , NULL, NULL, inst, NULL);
if (hwnd == NULL)
return - 1 ;
ShowWindow(hwnd, cmd_show);
UpdateWindow(hwnd);
// Call init function and enter message pump
if (do_init(hwnd))
{
MSG msg;
ZeroMemory( & msg, sizeof (MSG));
// Start message pump, waiting for user to exit
while (msg.message != WM_QUIT)
{
if (PeekMessage( & msg, NULL, 0 , 0 , PM_REMOVE))
{
TranslateMessage( & msg);
DispatchMessage( & msg);
}
do_frame(); // Render a single frame
}
}
do_shutdown();
UnregisterClass(CLASS_NAME, inst);
CoUninitialize();
return 0 ;
}
LRESULT FAR PASCAL window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Only handle window destruction messages
switch (msg)
{
case WM_DESTROY:
PostQuitMessage( 0 );
break ;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(hwnd);
break ;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
bool do_init(HWND hwnd)
{
init_d3d( & g_d3d, & g_device, hwnd, false , false );
// load skeletal mesh
if (FAILED(load_mesh( & g_mesh_container, & g_frame, g_device, " ..\\Data\\tiny.x " , " ..\\Data\\ " , 0 , D3DXMESH_SYSTEMMEM)))
return false ;
// load animation data
if ( ! g_anim_collection.load( " ..\\Data\\tiny.x " ))
return false ;
// map the animation to the frame hierarchy
g_anim_collection.map_frames(g_frame);
// get the bounding radius of the object
g_mesh_radius = 0.0f ;
D3DXMESHCONTAINER_EX * mesh_container = g_mesh_container;
while (mesh_container)
{
ID3DXMesh * mesh = mesh_container -> MeshData.pMesh;
if (mesh)
{
// lock the vertex buffer, get its radius, and unlock buffer.
D3DXVECTOR3 * vertices;
D3DXVECTOR3 center;
float radius;
mesh -> LockVertexBuffer(D3DLOCK_READONLY, ( void ** ) & vertices);
D3DXComputeBoundingSphere(vertices, mesh -> GetNumVertices(), D3DXGetFVFVertexSize(mesh -> GetFVF()),
& center, & radius);
mesh -> UnlockVertexBuffer();
// update radius
if (radius > g_mesh_radius)
g_mesh_radius = radius;
}
// goto next mesh
mesh_container = (D3DXMESHCONTAINER_EX * ) mesh_container -> pNextMeshContainer;
}
return true ;
}
void do_shutdown()
{
// free mesh data
delete g_mesh_container; g_mesh_container = NULL;
delete g_frame; g_frame = NULL;
// release D3D objects
release_com(g_device);
release_com(g_d3d);
}
void do_frame()
{
static DWORD start_time = timeGetTime();
DWORD curr_time = timeGetTime();
// update the animation (convert to 30 fps)
g_anim_collection.update(NULL, (curr_time - start_time) * 3 , true );
// rebuild the frame hierarchy transformations
if (g_frame)
g_frame -> update_hierarchy(NULL);
// rebuild the mesh
update_skin_mesh(g_mesh_container);
// calculate a view transformation matrix using the mesh's bounding radius to position the viewer
float distance = g_mesh_radius * 3.0f ;
float angle = timeGetTime() / 2000.0f ;
D3DXMATRIX mat_view;
D3DXVECTOR3 eye(cos(angle) * distance, g_mesh_radius, sin(angle) * distance);
D3DXVECTOR3 at( 0.0f , 0.0f , 0.0f );
D3DXVECTOR3 up( 0.0f , 1.0f , 0.0f );
D3DXMatrixLookAtLH( & mat_view, & eye, & at, & up);
g_device -> SetTransform(D3DTS_VIEW, & mat_view);
D3DXMATRIX mat_world;
D3DXMatrixIdentity( & mat_world);
g_device -> SetTransform(D3DTS_WORLD, & mat_world);
// clear the device and start drawing the scene
g_device -> Clear( 0 , NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA( 0 , 0 , 0 , 255 ), 1.0f , 0 );
g_device -> BeginScene();
draw_mesh(g_mesh_container);
g_device -> EndScene();
g_device -> Present(NULL, NULL, NULL, NULL);
}
#include < d3d9.h >
#include < d3dx9.h >
#include " Direct3D.h "
#include " SkeletalAnim.h "
IDirect3D9 * g_d3d;
IDirect3DDevice9 * g_device;
D3DXMESHCONTAINER_EX * g_mesh_container;
D3DXFRAME_EX * g_frame;
cAnimationCollection g_anim_collection;
float g_mesh_radius = 0.0f ; // bounding radius of mesh
const char CLASS_NAME[] = " SkeletalAnimClass " ;
const char CAPTION[] = " Skeletal Animation Demo " ;
////////////////////////////////////////////////////////////////////////////////////////////////
LRESULT FAR PASCAL window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
bool do_init(HWND hwnd);
void do_shutdown();
void do_frame();
///////////////////////////////////////////////////////////////////////////////////////////// /
int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR, int cmd_show)
{
CoInitialize(NULL); // Initialize the COM system
// Create the window class here and register it
WNDCLASSEX win_class;
win_class.cbSize = sizeof (win_class);
win_class.style = CS_CLASSDC;
win_class.lpfnWndProc = window_proc;
win_class.cbClsExtra = 0 ;
win_class.cbWndExtra = 0 ;
win_class.hInstance = inst;
win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
win_class.hbrBackground = NULL;
win_class.lpszMenuName = NULL;
win_class.lpszClassName = CLASS_NAME;
win_class.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if ( ! RegisterClassEx( & win_class))
return - 1 ;
// Create the main window
HWND hwnd = CreateWindow(CLASS_NAME, CAPTION, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
0 , 0 , 640 , 480 , NULL, NULL, inst, NULL);
if (hwnd == NULL)
return - 1 ;
ShowWindow(hwnd, cmd_show);
UpdateWindow(hwnd);
// Call init function and enter message pump
if (do_init(hwnd))
{
MSG msg;
ZeroMemory( & msg, sizeof (MSG));
// Start message pump, waiting for user to exit
while (msg.message != WM_QUIT)
{
if (PeekMessage( & msg, NULL, 0 , 0 , PM_REMOVE))
{
TranslateMessage( & msg);
DispatchMessage( & msg);
}
do_frame(); // Render a single frame
}
}
do_shutdown();
UnregisterClass(CLASS_NAME, inst);
CoUninitialize();
return 0 ;
}
LRESULT FAR PASCAL window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Only handle window destruction messages
switch (msg)
{
case WM_DESTROY:
PostQuitMessage( 0 );
break ;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(hwnd);
break ;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
bool do_init(HWND hwnd)
{
init_d3d( & g_d3d, & g_device, hwnd, false , false );
// load skeletal mesh
if (FAILED(load_mesh( & g_mesh_container, & g_frame, g_device, " ..\\Data\\tiny.x " , " ..\\Data\\ " , 0 , D3DXMESH_SYSTEMMEM)))
return false ;
// load animation data
if ( ! g_anim_collection.load( " ..\\Data\\tiny.x " ))
return false ;
// map the animation to the frame hierarchy
g_anim_collection.map_frames(g_frame);
// get the bounding radius of the object
g_mesh_radius = 0.0f ;
D3DXMESHCONTAINER_EX * mesh_container = g_mesh_container;
while (mesh_container)
{
ID3DXMesh * mesh = mesh_container -> MeshData.pMesh;
if (mesh)
{
// lock the vertex buffer, get its radius, and unlock buffer.
D3DXVECTOR3 * vertices;
D3DXVECTOR3 center;
float radius;
mesh -> LockVertexBuffer(D3DLOCK_READONLY, ( void ** ) & vertices);
D3DXComputeBoundingSphere(vertices, mesh -> GetNumVertices(), D3DXGetFVFVertexSize(mesh -> GetFVF()),
& center, & radius);
mesh -> UnlockVertexBuffer();
// update radius
if (radius > g_mesh_radius)
g_mesh_radius = radius;
}
// goto next mesh
mesh_container = (D3DXMESHCONTAINER_EX * ) mesh_container -> pNextMeshContainer;
}
return true ;
}
void do_shutdown()
{
// free mesh data
delete g_mesh_container; g_mesh_container = NULL;
delete g_frame; g_frame = NULL;
// release D3D objects
release_com(g_device);
release_com(g_d3d);
}
void do_frame()
{
static DWORD start_time = timeGetTime();
DWORD curr_time = timeGetTime();
// update the animation (convert to 30 fps)
g_anim_collection.update(NULL, (curr_time - start_time) * 3 , true );
// rebuild the frame hierarchy transformations
if (g_frame)
g_frame -> update_hierarchy(NULL);
// rebuild the mesh
update_skin_mesh(g_mesh_container);
// calculate a view transformation matrix using the mesh's bounding radius to position the viewer
float distance = g_mesh_radius * 3.0f ;
float angle = timeGetTime() / 2000.0f ;
D3DXMATRIX mat_view;
D3DXVECTOR3 eye(cos(angle) * distance, g_mesh_radius, sin(angle) * distance);
D3DXVECTOR3 at( 0.0f , 0.0f , 0.0f );
D3DXVECTOR3 up( 0.0f , 1.0f , 0.0f );
D3DXMatrixLookAtLH( & mat_view, & eye, & at, & up);
g_device -> SetTransform(D3DTS_VIEW, & mat_view);
D3DXMATRIX mat_world;
D3DXMatrixIdentity( & mat_world);
g_device -> SetTransform(D3DTS_WORLD, & mat_world);
// clear the device and start drawing the scene
g_device -> Clear( 0 , NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA( 0 , 0 , 0 , 255 ), 1.0f , 0 );
g_device -> BeginScene();
draw_mesh(g_mesh_container);
g_device -> EndScene();
g_device -> Present(NULL, NULL, NULL, NULL);
}
download source file