Using Key−Framed Skeletal Animation(5)

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.

Using Key−Framed Skeletal Animation(5)_第1张图片

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

 

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,
                             
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



你可能感兴趣的:(Using Key−Framed Skeletal Animation(5))