天行健 君子当自强而不息

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(
thissizeof(*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(
thissizeof(*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;
        }
    }
}

 

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,
                             
00640480, 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, 00, 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, falsefalse);

    
// 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) * 3true);

    
// 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.0f0.0f0.0f);
    D3DXVECTOR3 up(
0.0f1.0f0.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(000255), 1.0f0);

    g_device
->BeginScene();    
    draw_mesh(g_mesh_container);
    g_device
->EndScene();

    g_device
->Present(NULL, NULL, NULL, NULL);
}


download source file



posted on 2008-04-25 13:43 lovedday 阅读(603) 评论(0)  编辑 收藏 引用


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


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论