本篇是
创建游戏内核(14)的续篇。
使用ANIMATION移动网格
ANIMATION类是网格动画组件,也是图形内核的最后一个类。使用ANIMATION,就能够从.X文件加载一连串动画集,把它同OBJECT结合起来创建网格动画。ANIMATION类很小,类似
于MESH,它也有一些用于保存动画数据链表的结构体。
首先定义一些结构体来存储.X文件里的旋转关键点、缩放关键点、平移关键点、矩阵关键点信息。
struct S_XFILE_ROTATE_KEY
{
DWORD time;
DWORD floats;
float w;
float x;
float y;
float z;
};
struct S_XFILE_SCALE_KEY
{
DWORD time;
DWORD floats;
D3DXVECTOR3 scale;
};
struct S_XFILE_POSITION_KEY
{
DWORD time;
DWORD floats;
D3DXVECTOR3 pos;
};
struct S_XFILE_MATRIX_KEY
{
DWORD time;
DWORD floats;
D3DXMATRIX matrix;
};
接着我们定义类ANIMATION:
class ANIMATION
{
protected:
long _num_animations;
S_ANIMATION_SET* _animation_set;
void _Parse_XFile_Data(ID3DXFileData* xfile_data, S_ANIMATION_SET* anim_set_to_parse, S_ANIMATION* anim_to_parse);
public:
ANIMATION();
~ANIMATION();
BOOL Is_Loaded();
long Get_Num_Animations();
S_ANIMATION_SET* Get_Animation_Set(char* name = NULL);
unsigned long Get_Time_Length(char* name = NULL);
BOOL Load(const char* filename, MESH* map_mesh = NULL);
void Free();
BOOL Map_To_Mesh(MESH* mesh);
BOOL Set_Loop(BOOL is_loop, char* name = NULL);
};
以下是类ANIMATION的实现:
//-------------------------------------------------------------------
// Constructor, initialize member data.
//-------------------------------------------------------------------
ANIMATION::ANIMATION()
{
_num_animations = 0;
_animation_set = NULL;
}
//-------------------------------------------------------------------
// Destructor, free resource.
//-------------------------------------------------------------------
ANIMATION::~ANIMATION()
{
Free();
}
//-------------------------------------------------------------------
// Load animation information from specified file and map mesh.
//-------------------------------------------------------------------
BOOL ANIMATION::Load(const char *filename, MESH *map_mesh)
{
ID3DXFile* xfile = NULL;
ID3DXFileEnumObject* xfile_enum = NULL;
ID3DXFileData* xfile_data = NULL;
// free a prior animation
Free();
// error checking
if(filename == NULL)
return FALSE;
// create the file object
if(FAILED(D3DXFileCreate(&xfile)))
return FALSE;
// register the templates
if(FAILED(xfile->RegisterTemplates((LPVOID) D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES)))
{
xfile->Release();
return FALSE;
}
// create an enumeration object
if(FAILED(xfile->CreateEnumObject((LPVOID) filename, DXFILELOAD_FROMFILE, &xfile_enum)))
{
xfile->Release();
return FALSE;
}
SIZE_T num_child;
// retrieve the number of children in this file data object
xfile_enum->GetChildren(&num_child);
// loop through all objects looking for the animation
for(SIZE_T i = 0; i < num_child; i++)
{
if(FAILED(xfile_enum->GetChild(i, &xfile_data)))
return FALSE;
// parse xfile data
_Parse_XFile_Data(xfile_data, NULL, NULL);
Release_COM(xfile_data);
}
Release_COM(xfile_enum);
Release_COM(xfile);
// map the animation to the supplied mesh (if any)
if(map_mesh != NULL)
Map_To_Mesh(map_mesh);
return TRUE;
}
//-------------------------------------------------------------------
// Parse xfile data object which type is animation or animation set,
// call recursively.
//-------------------------------------------------------------------
void ANIMATION::_Parse_XFile_Data(ID3DXFileData* xfile_data,
S_ANIMATION_SET* anim_set_to_parse, S_ANIMATION* anim_to_parse)
{
GUID type;
DWORD size;
char* name = NULL;
// get the template type
if(FAILED(xfile_data->GetType(&type)))
return;
// get the template name (if any)
if(FAILED(xfile_data->GetName(NULL, &size)))
return;
if(size != 0)
{
if((name = new char[size]) != NULL)
xfile_data->GetName(name, &size);
}
// give template a default name if none found
if(name == NULL)
{
if((name = new char[9]) == NULL)
return;
strcpy(name, "$NoName$");
}
// set current animation set and current animation
S_ANIMATION_SET* cur_anim_set = anim_set_to_parse;
S_ANIMATION* cur_anim = anim_to_parse;
// process the templates
S_ANIMATION* new_anim = NULL;
S_ANIMATION_SET* new_anim_set = NULL;
PBYTE* data_ptr;
DWORD time;
S_XFILE_ROTATE_KEY* rot_key;
S_XFILE_SCALE_KEY* scale_key;
S_XFILE_POSITION_KEY* pos_key;
S_XFILE_MATRIX_KEY* mat_key;
if(type == TID_D3DRMAnimationSet) // process an animation set
{
// create an animation set structure
if((new_anim_set = new S_ANIMATION_SET()) == NULL)
return;
// set the name
new_anim_set->m_name = name;
name = NULL;
// link into the animation set list
new_anim_set->m_next = _animation_set;
_animation_set = new_anim_set;
// set new animation set as current animation set
cur_anim_set = new_anim_set;
}
else if(type == TID_D3DRMAnimation && cur_anim_set != NULL) // process an animtion
{
// create an animation structure
if((new_anim = new S_ANIMATION()) == NULL)
return;
// set the name
new_anim->m_name = name;
name = NULL;
// link into the animaton set
new_anim->m_next = cur_anim_set->m_animation;
cur_anim_set->m_animation = new_anim;
cur_anim = new_anim;
}
else if(type == TID_D3DRMAnimationKey && cur_anim != NULL) // process an animation key
{
// load in this animation's key data
if(FAILED(xfile_data->Lock(&size, (LPCVOID*) &data_ptr)))
return;
DWORD key_type = ((DWORD*) data_ptr)[0];
DWORD num_keys = ((DWORD*) data_ptr)[1];
switch(key_type)
{
case 0: // rotate key type
delete[] cur_anim->m_rotate_keys;
if((cur_anim->m_rotate_keys = new S_ROTATE_KEY[num_keys]) == NULL)
return;
cur_anim->m_num_rotate_keys = num_keys;
rot_key = (S_XFILE_ROTATE_KEY*) ((char*) data_ptr + sizeof(DWORD) * 2);
// translate rotate key information from xfile to animation
for(DWORD i = 0; i < num_keys; i++)
{
cur_anim->m_rotate_keys[i].time = rot_key->time;
cur_anim->m_rotate_keys[i].quat.x = -rot_key->x;
cur_anim->m_rotate_keys[i].quat.y = -rot_key->y;
cur_anim->m_rotate_keys[i].quat.z = -rot_key->z;
cur_anim->m_rotate_keys[i].quat.w = -rot_key->w;
if(rot_key->time > cur_anim_set->m_time_length)
cur_anim_set->m_time_length = rot_key->time;
rot_key += 1;
}
break;
case 1: // scale key type
delete[] cur_anim->m_scale_keys;
if((cur_anim->m_scale_keys = new S_SCALE_KEY[num_keys]) == NULL)
return;
cur_anim->m_num_scale_keys = num_keys;
scale_key = (S_XFILE_SCALE_KEY*) ((char*) data_ptr + sizeof(DWORD) * 2);
// translate scale key information from xfile to animtion
for(DWORD i = 0; i < num_keys; i++)
{
cur_anim->m_scale_keys[i].time = scale_key->time;
cur_anim->m_scale_keys[i].scale = scale_key->scale;
if(scale_key->time > cur_anim_set->m_time_length)
cur_anim_set->m_time_length = scale_key->time;
scale_key += 1;
}
// calculate the interpolation values
if(num_keys > 1)
{
for(DWORD i = 0; i < num_keys -1; i++)
{
time = cur_anim->m_scale_keys[i+1].time - cur_anim->m_scale_keys[i].time;
if(time == 0)
time = 1;
cur_anim->m_scale_keys[i].scale_inter =
(cur_anim->m_scale_keys[i+1].scale - cur_anim->m_scale_keys[i].scale) / (float)time;
}
}
break;
case 2: // position key type
delete[] cur_anim->m_position_keys;
if((cur_anim->m_position_keys = new S_POSITION_KEY[num_keys]) == NULL)
return;
cur_anim->m_num_position_keys = num_keys;
pos_key = (S_XFILE_POSITION_KEY*) ((char*) data_ptr + sizeof(DWORD) * 2);
// translate position key informaton from xfile to animtion
for(DWORD i = 0; i < num_keys; i++)
{
cur_anim->m_position_keys[i].time = pos_key->time;
cur_anim->m_position_keys[i].pos = pos_key->pos;
if(pos_key->time > cur_anim_set->m_time_length)
cur_anim_set->m_time_length = pos_key->time;
pos_key += 1;
}
// calculate the interpolation values
if(num_keys > 1)
{
for(DWORD i = 0;i < num_keys - 1; i++)
{
time = cur_anim->m_position_keys[i+1].time - cur_anim->m_position_keys[i].time;
if(time == 0)
time = 1;
cur_anim->m_position_keys[i].pos_inter =
(cur_anim->m_position_keys[i+1].pos - cur_anim->m_position_keys[i].pos) / (float)time;
}
}
break;
case 4: // matrix key type
delete[] cur_anim->m_matrix_keys;
if((cur_anim->m_matrix_keys = new S_MATRIX_KEY[num_keys]) == NULL)
return;
cur_anim->m_num_matrix_keys = num_keys;
mat_key = (S_XFILE_MATRIX_KEY*) ((char*) data_ptr + sizeof(DWORD) * 2);
// translate matrix key information from xfile to animation
for(DWORD i = 0; i < num_keys; i++)
{
cur_anim->m_matrix_keys[i].time = mat_key->time;
cur_anim->m_matrix_keys[i].matrix = mat_key->matrix;
if(mat_key->time > cur_anim_set->m_time_length)
cur_anim_set->m_time_length = mat_key->time;
mat_key += 1;
}
// calculate the interpolation matrices
if(num_keys > 1)
{
for(DWORD i = 0; i < num_keys - 1; i++)
{
time = cur_anim->m_matrix_keys[i+1].time - cur_anim->m_matrix_keys[i].time;
if(time == 0)
time = 1;
cur_anim->m_matrix_keys[i].mat_inter =
(cur_anim->m_matrix_keys[i+1].matrix - cur_anim->m_matrix_keys[i].matrix) / (float)time;
}
}
break;
}
xfile_data->Unlock();
}
else if(type == TID_D3DRMAnimationOptions && cur_anim != NULL) // process animation options
{
// load in this animation's options
if(FAILED(xfile_data->Lock(&size, (LPCVOID*) &data_ptr)))
return;
// process looping information
if(((DWORD*) data_ptr)[0] == 0)
cur_anim->m_is_loop = TRUE;
else
cur_anim->m_is_loop = FALSE;
// process linear information
if(((DWORD*) data_ptr)[1] == 0)
cur_anim->m_is_linear = FALSE;
else
cur_anim->m_is_linear = TRUE;
xfile_data->Unlock();
}
else if(type == TID_D3DRMFrame && cur_anim != NULL) // process a frame reference
{
cur_anim->m_frame_name = name;
name = NULL;
// don't enumerate child templates
return;
}
// release name buffer
delete[] name;
SIZE_T num_child;
ID3DXFileData* child_xfile_data = NULL;
xfile_data->GetChildren(&num_child);
// scan for embeded templates
for(SIZE_T i = 0; i < num_child; i++)
{
xfile_data->GetChild(i, &child_xfile_data);
// parse child xfile data
_Parse_XFile_Data(child_xfile_data, cur_anim_set, cur_anim);
Release_COM(child_xfile_data);
}
}
//-------------------------------------------------------------------
// Free resource.
//-------------------------------------------------------------------
void ANIMATION::Free()
{
delete _animation_set;
_animation_set = NULL;
_num_animations = 0;
}
//-------------------------------------------------------------------
// Judge whether animation set has beed loaded.
//-------------------------------------------------------------------
BOOL ANIMATION::Is_Loaded()
{
return (_animation_set != NULL);
}
//-------------------------------------------------------------------
// Map all frames in the animation sets to mesh's frames.
//-------------------------------------------------------------------
BOOL ANIMATION::Map_To_Mesh(MESH* mesh)
{
S_ANIMATION_SET* anim_set;
S_ANIMATION* anim;
// make sure there is a mesh to work with
if(mesh == NULL)
return FALSE;
// assign links to frames by name
if((anim_set = _animation_set) == NULL)
return FALSE;
// scan through all animation sets
while(anim_set != NULL)
{
// scan through all animations in current animation set
anim = anim_set->m_animation;
while(anim != NULL)
{
// find the matching frame from mesh
anim->m_frame = mesh->Get_Frame(anim->m_frame_name);
anim = anim->m_next;
}
anim_set = anim_set->m_next;
}
return TRUE;
}
//-------------------------------------------------------------------
// Get number of animations.
//-------------------------------------------------------------------
long ANIMATION::Get_Num_Animations()
{
return _num_animations;
}
//-------------------------------------------------------------------
// Get animation set which match specified name.
//-------------------------------------------------------------------
S_ANIMATION_SET* ANIMATION::Get_Animation_Set(char *name)
{
if(_animation_set == NULL)
return NULL;
return _animation_set->Find_Set(name);
}
//-------------------------------------------------------------------
// Set loop flag of all animations in the animation set which match
// specified name.
//-------------------------------------------------------------------
BOOL ANIMATION::Set_Loop(BOOL is_loop, char *name)
{
S_ANIMATION_SET* anim_set;
if((anim_set = Get_Animation_Set(name)) == NULL)
return FALSE;
S_ANIMATION* anim = anim_set->m_animation;
while(anim != NULL)
{
anim->m_is_loop = is_loop;
anim = anim->m_next;
}
return TRUE;
}
//-------------------------------------------------------------------
// Get time length of animation set which match specified name.
//-------------------------------------------------------------------
unsigned long ANIMATION::Get_Time_Length(char* name)
{
S_ANIMATION_SET* anim_set;
if((anim_set = Get_Animation_Set(name)) == NULL)
return 0;
return anim_set->m_time_length;
}
对于ANIMATION类,一般只调用4个常用的函数:Load,Free,Map_To_Mesh和Set_Loop。要确保动画类能够找到需要修改的网格矩阵,有必要将动画映射到网格。对于Set_Loop函数,要注意name参数,name指出了要设置重复循环的动画名称。
像框架和网格一样,动画也能够在.X文件内进行命名。这样就可以将多个动画打包到.X文件中,并通过名字对它们们进行引用。举例来说,如果.X文件包含一个名称为Walk的动画集,就能够将字符串"Walk"作为name传递给Set_Loop函数。给name指定一个NULL值就指定使用链表中最上面的动画。
其他需要注意的地方就是OBJECT类中的start_time参数,start_time参数提供了初始参考值,动画使用此值对动画进行定时。因此,如果动画基于时间(使用诸如timeGetTime的函数),就要将StartTime设置成开始播放动画的时间。播放动画时,时间可以是任意的,可以用秒、毫秒、帧等等来表示时间
,必须选择并一直使用一个时间度量单位。
接下来调用OBJECT::Update_Animation,使用的是所提供时间与start_time之间的差值,这样就给出了一个清除定时机构的思路(换句话说,准确的定时是基于0秒的开始播放时间,而不是任意的时间值)。
我们编写一段代码来测试类ANIMATION:
点击下载源码和工程
/*****************************************************************************
PURPOSE:
Test for class ANIMATION.
*****************************************************************************/
#include "Core_Global.h"
#pragma warning(disable : 4996)
//===========================================================================
// Defines class APP which public inherits from class APPLICATION.
//===========================================================================
class APP : public APPLICATION
{
private:
GRAPHICS _graphics;
MESH _mesh;
OBJECT _object;
ANIMATION _animation;
DWORD _last_anim_time;
BOOL _is_first_render;
public:
BOOL Init();
BOOL Shutdown();
BOOL Frame();
};
//-----------------------------------------------------------------------------
// Initialize graphics, set display mode, set vertex buffer, load texture file.
//-----------------------------------------------------------------------------
BOOL APP::Init()
{
D3DXMATRIX mat_view;
// initialize graphics
if (! _graphics.Init())
return FALSE;
// set display mode for graphics
if(! _graphics.Set_Mode(Get_Hwnd(), TRUE, FALSE, 640, 480, 16))
return FALSE;
// disable D3D lighting
_graphics.Enable_Lighting(FALSE);
// set perspective projection transform matrix.
_graphics.Set_Perspective(D3DX_PI/4.0f, 1.33333f, 1.0f, 1000.0f);
// create and set the view matrix
D3DXMatrixLookAtLH(&mat_view,
&D3DXVECTOR3(0.0, 50.0, -150.0),
&D3DXVECTOR3(0.0, 50.0, 0.0),
&D3DXVECTOR3(0.0, 1.0, 0.0));
_graphics.Get_Device_COM()->SetTransform(D3DTS_VIEW, &mat_view);
const char* xfilename = "Walk.x";
// load mesh
if(! _mesh.Load(&_graphics, xfilename))
return FALSE;
// load animation
if(! _animation.Load(xfilename, &_mesh))
return FALSE;
// play loop
_animation.Set_Loop(TRUE, NULL);
// create object to draw
if(! _object.Create(&_graphics, &_mesh))
return FALSE;
// attach animation to object
_object.Set_Animation(&_animation, NULL, timeGetTime());
_last_anim_time = timeGetTime();
_is_first_render = TRUE;
return TRUE;
}
//-----------------------------------------------------------------------------
// Release all d3d resource.
//-----------------------------------------------------------------------------
BOOL APP::Shutdown()
{
return TRUE;
}
//-----------------------------------------------------------------------------
// Render a frame.
//-----------------------------------------------------------------------------
BOOL APP::Frame()
{
D3DXMATRIX mat_world;
// clear display with specified color
_graphics.Clear_Display(D3DCOLOR_RGBA(0, 0, 0, 255));
// begin scene
if(_graphics.Begin_Scene())
{
// rotate object along x-axis, y-axis.
_object.Rotate(0, (float) (timeGetTime() / 1000.0), 0);
if(timeGetTime() - _last_anim_time > 300 || _is_first_render)
{
// update animation
_object.Update_Animation(timeGetTime(), TRUE);
// update last animation time
_last_anim_time = timeGetTime();
_is_first_render = FALSE;
}
// draw object
_object.Render();
// end the scene
_graphics.End_Scene();
}
// display video buffer
_graphics.Display();
return TRUE;
}
int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
APP app;
return app.Run();
}
运行截图: