本篇是
創建游戲內核(13)的續篇,其中涉及到的網格繪制知識請參閱D3D中網格模型的運用。
使用.X樣式三維動畫
三維動畫與二維動畫相比較是一個全新的處理過程。不用再奢侈地繪制圖像,然后按順序顯示來創建動畫。在三維世界中,一個對象可以從任何方向進行觀察。
基本上,三維動畫改變那些在運行期間用于為網格模型指明方向的框架變換矩陣,進而產生出在框架中移動的各式各樣的網格模型,這個運動的網格模型就是動畫。可以使用任何方式平移、旋轉、甚至縮放網格模型。
在處理蒙皮網格模型時,框架變換的使用只不過是使網格模型產生動畫的一種方法。因為一個蒙皮網格模型就是一個單一的網格模型(并非由多個網格模型組成),需要改變框架比便進行頂點的變形。修改框架變換矩陣最簡單的方法就是使用一種稱之為關鍵楨(key
frame)的技術。
關鍵楨技術
在計算機動畫中,關鍵楨描述了這樣一種技術,它使用兩個完整的、單獨的方位(關鍵楨),以及兩者之間基于某一系數(例如時間)的插值。另一方面,通過使用兩個不同框架的方位(每個方位稱之為一個關鍵點key),就可以計算出那些關鍵點任何位置以及任何時間所在的方位。
如下所示,框架的方位,從開始時的方位到結束時的方位,隨時間變化進行插值計算。

關鍵幀的效率非常高,它確保了動畫能夠以相同的速度運行在所有的系統中。運行較慢的計算機會減少幀數(以動畫的跳躍為代價),而較快的計算機能夠產生出更多的幀,從而提供更平滑的動畫。
只需要在那些幀之間進行插值計算便可以產生平滑的動畫,插值(interpolating)是一種計算兩個數值之間,隨時間變化的過渡數值的方法。這里使用的關鍵楨形式是矩陣關鍵楨(matrix key
framing),因為使用的是D3DX矩陣對象,所以使用這種格式的關鍵楨會十分容易。比如說,有兩個矩陣mat1和mat2,分別表示開始和結束的矩陣,他們之間的時間距離表示為time_length,同時當前的時間表示為time(其范圍從0到length),計算插值矩陣,就像下面代碼所示:
D3DXMATRIX mat1, mat2;
DWORD time_length, time;
D3DXMATRIX mat_inter; // 最終的插值矩陣
// 計算插值矩陣
mat_inter = mat1 + ((mat2 - mat1) / time_length) * time;
.X中的動畫
微軟公司在.X文件中提供了動畫數據,該動畫數據位于一個特定數據對象的集合里,同時,也可以使用與加載蒙皮網格模型相同的技術去加載那些動畫對象的數據。特定的數據對象包含了在關鍵幀技術中所使用到的各種各樣的關鍵點,每個關鍵點代表了一種變換:旋轉、縮放、平移。為了使之更加容易,可以指定同時組合了所有變換的矩陣關鍵點。
每個關鍵點都有一個與之相關聯的激活時間。換句話說,在time=0的旋轉關鍵點意味著在時間為0時,關鍵點中的旋轉數值被使用。而第二個旋轉關鍵點在time=200時激活。當計時繼續時,位于第一個和第二個旋轉關鍵點之間某個位置的旋轉插值被計算出來。當時間到達200時,旋轉數值與第二個旋轉關鍵點相等。這種形式的插值計算可以被運用到所有的關鍵點類型中。
動畫以集合的形式出現,同時每個集合被分配特定的框架,可以將多個關鍵點分配給相同的集合,以便使多個動畫關鍵點能夠同時影響到框架。舉例來說,一個框架可以同時被旋轉關鍵點和平移關鍵點的集合來修改,以便同時旋轉并平移框架。
使用OBJECT繪制對象
要繪制網格,必須在網格定義和顯示器之間搭設一道橋梁。為什么不使用MESH對象處理渲染呢?原因在于內存使用(memory
usage),如果要反復使用相同的網格,該怎么辦?解決的辦法就是使用OBJECT對象。
首先定義表示插值旋轉變換、平移變換、縮放、變換矩陣的數據結構:
struct S_ROTATE_KEY
{
DWORD time;
D3DXQUATERNION quat;
};
struct S_POSITION_KEY
{
DWORD time;
D3DXVECTOR3 pos;
D3DXVECTOR3 pos_inter;
};
struct S_SCALE_KEY
{
DWORD time;
D3DXVECTOR3 scale;
D3DXVECTOR3 scale_inter;
};
struct S_MATRIX_KEY
{
DWORD time;
D3DXMATRIX matrix;
D3DXMATRIX mat_inter;
};
其次定義一個叫做S_ANIATION的結構體來封裝框架的變換矩陣。
struct S_ANIMATION
{
public:
char* m_name;
char* m_frame_name;
S_FRAME* m_frame;
BOOL m_is_loop;
BOOL m_is_linear;
DWORD m_num_position_keys;
S_POSITION_KEY* m_position_keys;
DWORD m_num_rotate_keys;
S_ROTATE_KEY* m_rotate_keys;
DWORD m_num_scale_keys;
S_SCALE_KEY* m_scale_keys;
DWORD m_num_matrix_keys;
S_MATRIX_KEY* m_matrix_keys;
S_ANIMATION* m_next;
//--------------------------------------------------------------------
// Constructor, initialize member data.
//--------------------------------------------------------------------
S_ANIMATION()
{
m_name = NULL;
m_frame_name = NULL;
m_frame = NULL;
m_is_loop = FALSE;
m_is_linear = TRUE;
m_num_position_keys = 0;
m_num_rotate_keys = 0;
m_num_scale_keys = 0;
m_num_matrix_keys = 0;
m_position_keys = NULL;
m_rotate_keys = NULL;
m_scale_keys = NULL;
m_matrix_keys = NULL;
m_next = NULL;
}
//--------------------------------------------------------------------
// Destructor, release resource.
//--------------------------------------------------------------------
~S_ANIMATION()
{
delete[] m_name; m_name = NULL;
delete[] m_frame_name; m_frame_name = NULL;
delete[] m_position_keys; m_position_keys = NULL;
delete[] m_rotate_keys; m_rotate_keys = NULL;
delete[] m_scale_keys; m_scale_keys = NULL;
delete[] m_matrix_keys; m_matrix_keys = NULL;
delete[] m_next; m_next = NULL;
}
//--------------------------------------------------------------------
// Update current frame's transformed matrix.
//--------------------------------------------------------------------
void Update(DWORD time, BOOL is_smooth)
{
unsigned long key_index;
DWORD time_diff, time_inter;
D3DXMATRIX matrix, mat_temp;
D3DXVECTOR3 vector;
D3DXQUATERNION quat;
if(m_frame == NULL)
return;
// Update rotation, scale, and position keys.
if(m_num_rotate_keys != 0 || m_num_scale_keys != 0 || m_num_position_keys != 0)
{
D3DXMatrixIdentity(&matrix);
// update rotation matrix
if(m_num_rotate_keys != 0 && m_rotate_keys != NULL)
{
// find the key that fits this time
key_index = 0;
for(unsigned long i = 0; i < m_num_rotate_keys; i++)
{
if(m_rotate_keys[i].time <= time)
key_index = i;
else
break;
}
// If it's the last key or non-smooth animation, then just set the key value.
if(key_index == m_num_rotate_keys - 1 || is_smooth == FALSE)
quat = m_rotate_keys[key_index].quat;
else
{
// calculate the time difference and interpolate time
time_diff = m_rotate_keys[key_index + 1].time - m_rotate_keys[key_index].time;
time_inter = time - m_rotate_keys[key_index].time;
// Get the quarternion value
//
// Interpolates between two quaternions, using spherical linear interpolation.
D3DXQuaternionSlerp(&quat, &m_rotate_keys[key_index].quat,
&m_rotate_keys[key_index+1].quat, (float)time_inter / time_diff);
}
// combine with the new matrix
// builds a rotation matrix from a quaternion.
D3DXMatrixRotationQuaternion(&mat_temp, &quat);
D3DXMatrixMultiply(&matrix, &matrix, &mat_temp);
}
// update scale matrix
if(m_num_scale_keys != 0 && m_scale_keys != NULL)
{
// find the key that fits this time
key_index = 0;
for(unsigned long i = 0; i < m_num_scale_keys; i++)
{
if(m_scale_keys[i].time <= time)
key_index = i;
else
break;
}
// If it's the last key or non-smooth animation, then just set the key value.
if(key_index == m_num_scale_keys - 1 || is_smooth == FALSE)
vector = m_scale_keys[key_index].scale;
else
{
// calculate the time difference and interpolate time
time_inter = time - m_scale_keys[key_index].time;
// get the interpolated vector value
vector = m_scale_keys[key_index].scale +
m_scale_keys[key_index].scale_inter * (float)time_inter;
}
// combine with the new matrix
D3DXMatrixScaling(&mat_temp, vector.x, vector.y, vector.z);
D3DXMatrixMultiply(&matrix, &matrix, &mat_temp);
}
// update translation matrix
if(m_num_position_keys != 0 && m_position_keys != NULL)
{
// find the key that fits this time
key_index = 0;
for(unsigned long i = 0; i < m_num_position_keys; i++)
{
if(m_position_keys[i].time <= time)
key_index = i;
else
break;
}
// If it's the last key or non-smooth animation, then just set the key value.
if(key_index == m_num_position_keys - 1 || is_smooth == FALSE)
vector = m_position_keys[key_index].pos;
else
{
// calculate the time difference and interpolate time
time_inter = time - m_position_keys[key_index].time;
// get the interpolated vector value
vector = m_position_keys[key_index].pos +
m_position_keys[key_index].pos_inter * (float)time_inter;
}
// combine with the new matrix
D3DXMatrixTranslation(&mat_temp, vector.x, vector.y, vector.z);
D3DXMatrixMultiply(&matrix, &matrix, &mat_temp);
}
// set the new matrix
m_frame->m_mat_transformed = matrix;
}
// update matrix keys
if(m_num_matrix_keys != 0 && m_matrix_keys != NULL)
{
// find the key that fits this time
key_index = 0;
for(unsigned long i = 0; i < m_num_matrix_keys; i++)
{
if(m_matrix_keys[i].time <= time)
key_index = i;
else
break;
}
// If it's the last key or non-smooth animation, then just set the matrix.
if(key_index == m_num_matrix_keys - 1 || is_smooth == FALSE)
m_frame->m_mat_transformed = m_matrix_keys[key_index].matrix;
else
{
// calculate the time difference and interpolated time
time_inter = time - m_matrix_keys[key_index].time;
// set the new interpolation matrix
matrix = m_matrix_keys[key_index].mat_inter * (float) time_inter;
m_frame->m_mat_transformed = m_matrix_keys[key_index].matrix + matrix;
}
}
}
};
接著再定義一個結構體S_ANIMATION_SET來封裝動畫集合。
struct S_ANIMATION_SET
{
public:
char* m_name;
S_ANIMATION* m_animation;
unsigned long m_time_length;
S_ANIMATION_SET* m_next;
//-----------------------------------------------------------------------------
// Constructor, initialize member data.
//-----------------------------------------------------------------------------
S_ANIMATION_SET()
{
m_name = NULL;
m_animation = NULL;
m_next = NULL;
m_time_length = 0;
}
//-----------------------------------------------------------------------------
// Destructor, release resource.
//-----------------------------------------------------------------------------
~S_ANIMATION_SET()
{
delete[] m_name; m_name = NULL;
delete m_animation; m_animation = NULL;
delete m_next; m_next = NULL;
}
//-----------------------------------------------------------------------------
// Find animation set in animation set link list which match specified name.
//-----------------------------------------------------------------------------
S_ANIMATION_SET* Find_Set(char* name)
{
// return first instance if name is NULL
if(name == NULL)
return this;
// compare names and return if exact match
if(m_name != NULL && !strcmp(name, m_name))
return this;
// search next in list
if(m_next != NULL)
{
S_ANIMATION_SET* anim_set;
if((anim_set = m_next->Find_Set(name)) != NULL)
return anim_set;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Update all animations in current animation set.
//-----------------------------------------------------------------------------
void Update(DWORD time, BOOL is_smooth)
{
S_ANIMATION* anim = m_animation;
while(anim != NULL)
{
if(m_time_length == 0)
{
// If time length of the animation set is zero, just use first frame.
anim->Update(0, FALSE);
}
else if(time >= m_time_length && anim->m_is_loop == FALSE)
{
// If time beyonds max time length of the animation set and is not loop animation, use last frame.
anim->Update(time, FALSE);
}
else
{
// Now, update animation usually.
anim->Update(time % m_time_length, is_smooth);
}
anim = anim->m_next;
}
}
};
在定義好以上數據結構的方法的基礎上,我們定義一個叫做OBJECT的類來封裝網格對象的繪制:
class OBJECT
{
protected:
GRAPHICS* _graphics;
MESH* _mesh;
S_ANIMATION_SET* _animation_set;
WORLD_POSITION _pos;
BOOL _use_billboard;
unsigned long _start_time;
void _Update_Frame(S_FRAME* frame, D3DXMATRIX* matrix);
void _Draw_Frame(S_FRAME* frame);
public:
OBJECT();
~OBJECT();
BOOL Create(GRAPHICS* graphics, MESH* mesh = NULL);
void Free();
void Enable_Billboard(BOOL enable = TRUE);
void Attach_To_Object(OBJECT* object, char* frame_name = NULL);
void Move(float x_pos, float y_pos, float z_pos);
void Move_Rel(float x_add, float y_add, float z_add);
void Rotate(float x_rot, float y_rot, float z_rot);
void Rotate_Rel(float x_add, float y_add, float z_add);
void Scale(float x_scale, float y_scale, float z_scale);
void Scale_Rel(float x_add, float y_add, float z_add);
D3DXMATRIX* Get_Matrix();
float Get_X_Pos();
float Get_Y_Pos();
float Get_Z_Pos();
float Get_X_Rotation();
float Get_Y_Rotation();
float Get_Z_Rotation();
float Get_X_Scale();
float Get_Y_Scale();
float Get_Z_Scale();
BOOL Get_Bounds(float* min_x, float* min_y, float* min_z, float* max_x, float* max_y, float* max_z, float* radius);
void Set_Mesh(MESH* mesh);
MESH* Get_Mesh();
void Set_Animation(ANIMATION* animation, char* name = NULL, unsigned long start_time = 0);
char* Get_Animation_Name();
void Reset_Animation(unsigned long start_time = 0);
void Update_Animation(unsigned long time, BOOL is_smooth = TRUE);
BOOL Animation_Complete(unsigned long time);
void Update();
BOOL Render();
};
接著是類OBJECT的實現:
//-------------------------------------------------------------------
// Constructor, initialize member data.
//-------------------------------------------------------------------
OBJECT::OBJECT()
{
_graphics = NULL;
_mesh = NULL;
_animation_set = NULL;
}
//-------------------------------------------------------------------
// Destructor, release resource.
//-------------------------------------------------------------------
OBJECT::~OBJECT()
{
Free();
}
//-------------------------------------------------------------------
// Create an object with specified mesh object.
//-------------------------------------------------------------------
BOOL OBJECT::Create(GRAPHICS* graphics, MESH* mesh)
{
if((_graphics = graphics) == NULL)
return FALSE;
_mesh = mesh;
Move(0.0f, 0.0f, 0.0f);
Rotate(0.0f, 0.0f, 0.0f);
Scale(1.0f, 1.0f, 1.0f);
return TRUE;
}
//-------------------------------------------------------------------
// Release resource.
//-------------------------------------------------------------------
void OBJECT::Free()
{
_graphics = NULL;
_mesh = NULL;
_animation_set = NULL;
}
//-------------------------------------------------------------------
// Enable or disable billboard.
//-------------------------------------------------------------------
void OBJECT::Enable_Billboard(BOOL enable)
{
_pos.Enable_Billboard(enable);
}
//-------------------------------------------------------------------
// Attach another object's transformed matrix to current object.
//-------------------------------------------------------------------
void OBJECT::Attach_To_Object(OBJECT* object, char* frame_name)
{
if(object == NULL || object->_mesh == NULL)
{
_pos.Set_Combine_Matrix_1(NULL);
_pos.Set_Combine_Matrix_2(NULL);
}
else
{
S_FRAME* frame = object->_mesh->Get_Frame(frame_name);
if(frame == NULL)
{
_pos.Set_Combine_Matrix_1(NULL);
_pos.Set_Combine_Matrix_2(NULL);
}
else
{
_pos.Set_Combine_Matrix_1(&frame->m_mat_combined);
_pos.Set_Combine_Matrix_2(object->_pos.Get_Matrix());
}
}
}
//-------------------------------------------------------------------
// Move object to new world position with specified relative value.
//-------------------------------------------------------------------
void OBJECT::Move(float x_pos, float y_pos, float z_pos)
{
_pos.Move(x_pos, y_pos, z_pos);
}
//-------------------------------------------------------------------
// Move object to new world position which is specified by new relative
// value and current position.
//-------------------------------------------------------------------
void OBJECT::Move_Rel(float x_add, float y_add, float z_add)
{
_pos.Move_Rel(x_add, y_add, z_add);
}
//-------------------------------------------------------------------
// Rotate around x, y, z, axis with specified degree.
//-------------------------------------------------------------------
void OBJECT::Rotate(float x_rot, float y_rot, float z_rot)
{
_pos.Rotate(x_rot, y_rot, z_rot);
}
//-------------------------------------------------------------------
// Rotate around x, y, z, axis which is specified with new relative
// degree and current rotation value.
//-------------------------------------------------------------------
void OBJECT::Rotate_Rel(float x_add, float y_add, float z_add)
{
_pos.Rotate_Rel(x_add, y_add, z_add);
}
//-------------------------------------------------------------------
// Build scaling matrix.
//-------------------------------------------------------------------
void OBJECT::Scale(float x_scale, float y_scale, float z_scale)
{
_pos.Scale(x_scale, y_scale, z_scale);
}
//-------------------------------------------------------------------
// Build scaling matrix with specified value and current scaling value.
//-------------------------------------------------------------------
void OBJECT::Scale_Rel(float x_add, float y_add, float z_add)
{
_pos.Scale_Rel(x_add, y_add, z_add);
}
//-------------------------------------------------------------------
// Get current world transform matrix.
//-------------------------------------------------------------------
D3DXMATRIX* OBJECT::Get_Matrix()
{
return _pos.Get_Matrix();
}
//-------------------------------------------------------------------
// Set mesh to current object.
//-------------------------------------------------------------------
void OBJECT::Set_Mesh(MESH *mesh)
{
_mesh = mesh;
}
//-------------------------------------------------------------------
// Get current object's mesh.
//-------------------------------------------------------------------
MESH* OBJECT::Get_Mesh()
{
return _mesh;
}
//-------------------------------------------------------------------
// Set animation set with specified name and start time.
//-------------------------------------------------------------------
void OBJECT::Set_Animation(ANIMATION* animation, char* name, unsigned long start_time)
{
_start_time = start_time;
if(animation == NULL)
_animation_set = NULL;
else
_animation_set = animation->Get_Animation_Set(name);
}
//-------------------------------------------------------------------
// Get the name of animation set.
//-------------------------------------------------------------------
char* OBJECT::Get_Animation_Name()
{
if(_animation_set == NULL)
return NULL;
return _animation_set->m_name;
}
//-------------------------------------------------------------------
// Reset start time of animation set.
//-------------------------------------------------------------------
void OBJECT::Reset_Animation(unsigned long start_time)
{
_start_time = start_time;
}
//-------------------------------------------------------------------
// Get x coordinate of current obejct.
//-------------------------------------------------------------------
float OBJECT::Get_X_Pos()
{
return _pos.Get_X_Pos();
}
//-------------------------------------------------------------------
// Get y coordinate of current obejct.
//-------------------------------------------------------------------
float OBJECT::Get_Y_Pos()
{
return _pos.Get_Y_Pos();
}
//-------------------------------------------------------------------
// Get z coordinate of current obejct.
//-------------------------------------------------------------------
float OBJECT::Get_Z_Pos()
{
return _pos.Get_Z_Pos();
}
//-------------------------------------------------------------------
// Get current rotation value which rotate around x axis.
//-------------------------------------------------------------------
float OBJECT::Get_X_Rotation()
{
return _pos.Get_X_Rotation();
}
//-------------------------------------------------------------------
// Get current rotation value which rotate around y axis.
//-------------------------------------------------------------------
float OBJECT::Get_Y_Rotation()
{
return _pos.Get_Y_Rotation();
}
//-------------------------------------------------------------------
// Get current rotation value which rotate around z axis.
//-------------------------------------------------------------------
float OBJECT::Get_Z_Rotation()
{
return _pos.Get_Z_Rotation();
}
//-------------------------------------------------------------------
// Get current scale value which around x axis.
//-------------------------------------------------------------------
float OBJECT::Get_X_Scale()
{
return _pos.Get_X_Scale();
}
//-------------------------------------------------------------------
// Get current scale value which around y axis.
//-------------------------------------------------------------------
float OBJECT::Get_Y_Scale()
{
return _pos.Get_Y_Scale();
}
//-------------------------------------------------------------------
// Get current scale value which around z axis.
//-------------------------------------------------------------------
float OBJECT::Get_Z_Scale()
{
return _pos.Get_Z_Scale();
}
//-------------------------------------------------------------------
// Get bound coordinate and radius after scale.
//-------------------------------------------------------------------
BOOL OBJECT::Get_Bounds(float *min_x, float *min_y, float *min_z, float *max_x, float *max_y, float *max_z,
float *radius)
{
if(_mesh == NULL)
return FALSE;
// Get bound box coordiante and radius.
_mesh->Get_Bounds(min_x, min_y, min_z, max_x, max_y, max_z, radius);
// scale bounds
float x_scale = _pos.Get_X_Scale();
float y_scale = _pos.Get_Y_Scale();
float z_scale = _pos.Get_Z_Scale();
if(min_x != NULL) *min_x *= x_scale;
if(min_y != NULL) *min_y *= y_scale;
if(min_z != NULL) *min_z *= z_scale;
if(max_x != NULL) *max_x *= x_scale;
if(max_y != NULL) *max_y *= y_scale;
if(max_z != NULL) *max_z *= z_scale;
if(radius != NULL)
{
float length = (float) sqrt(x_scale * x_scale + y_scale * y_scale + z_scale * z_scale);
(*radius) *= length;
}
return TRUE;
}
//-------------------------------------------------------------------
// Update world tranform matrix.
//-------------------------------------------------------------------
void OBJECT::Update()
{
_pos.Update(_graphics);
}
//-----------------------------------------------------------------------------
// Update all animations in current animation set.
//-----------------------------------------------------------------------------
void OBJECT::Update_Animation(unsigned long time, BOOL is_smooth)
{
if(_animation_set)
{
// reset all frames's transformed matrices to original matrices
_mesh->Get_Root_Frame()->Reset_Matrices();
// update all animations in current animation set
_animation_set->Update(time - _start_time, is_smooth);
}
}
//-----------------------------------------------------------------------------
// Judge whether animation has completed.
//-----------------------------------------------------------------------------
BOOL OBJECT::Animation_Complete(unsigned long time)
{
if(_animation_set == NULL)
return TRUE;
if((time - _start_time) >= _animation_set->m_time_length)
return TRUE;
return FALSE;
}
//-----------------------------------------------------------------------------
// Render all meshes in this object.
//-----------------------------------------------------------------------------
BOOL OBJECT::Render()
{
D3DXMATRIX matrix;
// error checking
if(_graphics == NULL || _mesh == NULL || _mesh->Get_Root_Frame() == NULL || _mesh->Get_Root_Mesh() == NULL)
return FALSE;
// update the object matrix
Update();
// update the frame matrices
D3DXMatrixIdentity(&matrix);
_Update_Frame(_mesh->Get_Root_Frame(), &matrix);
// copy frame matrices to bone matrices
_mesh->Get_Root_Mesh()->Copy_Frame_To_Bone_Matrices();
// draw all frame meshes
_Draw_Frame(_mesh->Get_Root_Frame());
return TRUE;
}
//-----------------------------------------------------------------------------
// Update transformation matrix of all frames, call recursively.
//-----------------------------------------------------------------------------
void OBJECT::_Update_Frame(S_FRAME *frame, D3DXMATRIX *matrix)
{
// return if no more frames
if(frame == NULL)
return;
// calculate frame matrix based on animation or not
if(_animation_set == NULL)
D3DXMatrixMultiply(&frame->m_mat_combined, &frame->m_mat_original, matrix);
else
D3DXMatrixMultiply(&frame->m_mat_combined, &frame->m_mat_transformed, matrix);
// update child frames
_Update_Frame(frame->m_child, &frame->m_mat_combined);
// update sibling frames
_Update_Frame(frame->m_sibling, matrix);
}
//-----------------------------------------------------------------------------
// Draw all meshes in frames which under current specified frame, call recursively.
//-----------------------------------------------------------------------------
void OBJECT::_Draw_Frame(S_FRAME *frame)
{
S_MESH_LIST* list;
S_MESH* mesh;
D3DXMATRIX mat_world;
if(frame == NULL)
return;
if((list = frame->m_mesh_list) != NULL)
{
// draw all meshes in this frame
while(list != NULL)
{
// see if there's a mesh to draw
if((mesh = list->m_mesh) != NULL)
{
// generate the mesh if using bones and set world matrix
if(mesh->m_num_bones && mesh->m_skin_mesh)
{
void* src_ptr;
void* dest_ptr;
// lock the source and destination vertex buffers
mesh->m_mesh->LockVertexBuffer(D3DLOCK_READONLY, (void**) &src_ptr);
mesh->m_skin_mesh->LockVertexBuffer(0, (void**) &dest_ptr);
// perfrom skinned mesh update
mesh->m_skin_info->UpdateSkinnedMesh(mesh->m_matrices, NULL, src_ptr, dest_ptr);
// unlock vertex buffers
mesh->m_skin_mesh->UnlockVertexBuffer();
mesh->m_mesh->UnlockVertexBuffer();
// set object world transformation
_graphics->Get_Device_COM()->SetTransform(D3DTS_WORLD, _pos.Get_Matrix());
}
else
{
// set the world transformation matrix for this frame
D3DXMatrixMultiply(&mat_world, &frame->m_mat_combined, _pos.Get_Matrix());
_graphics->Get_Device_COM()->SetTransform(D3DTS_WORLD, &mat_world);
}
// loop through materials and draw the mesh
for(DWORD i = 0; i < mesh->m_num_materials; i++)
{
// don't draw materials with no alpha (0.0)
if(mesh->m_materials[i].Diffuse.a != 0.0f)
{
_graphics->Get_Device_COM()->SetMaterial(&mesh->m_materials[i]);
_graphics->Get_Device_COM()->SetTexture(0, mesh->m_textures[i]);
// enabled alpha blending based on material alpha
if(mesh->m_materials[i].Diffuse.a != 1.0f)
_graphics->Enable_Alpha_Blending(TRUE, D3DBLEND_SRCCOLOR, D3DBLEND_ONE);
// draw mesh or skinned mehs
if(mesh->m_skin_mesh != NULL)
mesh->m_skin_mesh->DrawSubset(i);
else
mesh->m_mesh->DrawSubset(i);
// disable alpha blending based on material alpha
if(mesh->m_materials[i].Diffuse.a != 1.0f)
_graphics->Enable_Alpha_Blending(FALSE);
}
}
}
// next mesh in lsit
list = list->m_next;
}
}
// draw child frames and sibling frames
_Draw_Frame(frame->m_child);
_Draw_Frame(frame->m_sibling);
}
其中涉及到網格動畫組件類ANIMATION,其簡要定義如下:
class ANIMATION
{
protected:
long _num_animations;
S_ANIMATION_SET* _animation_set;
public:
ANIMATION();
~ANIMATION();
S_ANIMATION_SET* Get_Animation_Set(char* name = NULL);
void Free();
};
//-------------------------------------------------------------------
// Constructor, initialize member data.
//-------------------------------------------------------------------
ANIMATION::ANIMATION()
{
_num_animations = 0;
_animation_set = NULL;
}
//-------------------------------------------------------------------
// Destructor, free resource.
//-------------------------------------------------------------------
ANIMATION::~ANIMATION()
{
Free();
}
//-------------------------------------------------------------------
// Free resource.
//-------------------------------------------------------------------
void ANIMATION::Free()
{
delete _animation_set;
_animation_set = NULL;
_num_animations = 0;
}
//-------------------------------------------------------------------
// 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);
}
接著,我們編寫測試代碼來測試類OBJECT:
點擊下載源碼和工程
/*****************************************************************************
PURPOSE:
Test for class OBJECT.
*****************************************************************************/
#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;
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, 400, 400, 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);
// load mesh
if(! _mesh.Load(&_graphics, "warrior.x"))
return FALSE;
// create object to draw
if(! _object.Create(&_graphics, &_mesh))
return FALSE;
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, 128, 0, 255));
// begin scene
if(_graphics.Begin_Scene())
{
// rotate object along x-axis, y-axis.
_object.Rotate((float) (timeGetTime() / 2000.0), (float) (timeGetTime() / 1000.0), 0);
// 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();
}
運行截圖:
