Blending Skeletal Animations(3)

Blending Skeletal Animations(3)

Check Out the Demo

Although this chapter only has one demo to tout, it sure is a whopper! Demonstrating the technique of blended animation, the SkeletalAnimBlend demo program (see Figure 6.3) shows off Microsoft's Tiny character in all her blended glory!

Blending Skeletal Animations(3)_第1张图片

Figure 6.3: Explore your new found blended skeletal animation techniques by choosing which animations to blend in real time.

I edited the Tiny.x file to split her animation up into multiple sets. There are animation sets for each of her arms and legs, as well as an animation set for her body. Pressing any of the keys displayed on the screen toggles the blending of the appropriate animation set. For instance, hitting 1 toggles the blending of her left arm animation sequence. When enabled, the left arm animation has Tiny swinging her arm in sequence with her step. When disabled, her left arm hangs limp.

To really illustrate the power of blending, suppose you add a new animation set to the Tiny.x file that has Tiny waving her arm as opposed to swinging it back and forth. You only need to turn off blending of the swinging animation and blend in the waving animation to create a new and totally unique animation!

 

SkeletalAnimBlend.h:

#ifndef SKELETAL_ANIM_BLEND_H
#define  SKELETAL_ANIM_BLEND_H

#include 
" SkeletalAnim.h "

class  cBlendAnimationCollection :  public  cAnimationCollection
{
public :
    
void  blend( const   char *  anim_set_name, DWORD time,  bool  is_loop,  float  blend);
};

#endif

 

SkeletalAnimBlend.cpp:

#include  " SkeletalAnimBlend.h "

#pragma warning(disable : 
4996 )

void  cBlendAnimationCollection::blend( const   char *  anim_set_name, 
                                      DWORD time, 
bool  is_loop,  float  blend)
{
    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
        D3DXMATRIX anim_matrix;
        D3DXMatrixIdentity(
& anim_matrix);

        
//  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_matrix 
*=  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_matrix 
*=  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_matrix 
*=  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_matrix  *=  diff_matrix;
        }

        D3DXMATRIX diff_matrix 
=  anim_matrix  -  anim -> bone -> mat_original;
        diff_matrix 
*=  blend;
        anim
-> bone -> TransformationMatrix  +=  diff_matrix;
    }
}

 

WinMain.cpp:

/* **********************************************************************************************
 Demonstrates how multiple skeletal-based animations can be blended together into one animation.
**********************************************************************************************
*/

#include 
< windows.h >
#include 
< d3d9.h >
#include 
< d3dx9.h >
#include 
" Direct3D.h "
#include 
" SkeletalAnimBlend.h "

IDirect3D9
*                 g_d3d;
IDirect3DDevice9
*         g_device;
D3DXMESHCONTAINER_EX
*     g_mesh_container;
D3DXFRAME_EX
*             g_frame;
IDirect3DTexture9
*         g_guide_texture;
ID3DXSprite
*             g_guide_sprite;

cBlendAnimationCollection    g_blend_anim_collection;

char  g_blend_flags[ 5 ];         //  blending toggles (arms, legs, )

const   char  CLASS_NAME[]  =   " BlendSkeletalAnimClass " ;
const   char  CAPTION[]     =   " Blended 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_KEYUP:
        
//  toggle a body part
         if (wParam  >=   ' 1 '   &&  wParam  <=   ' 5 ' )
            g_blend_flags[wParam 
-   ' 1 ' ^=   1 ;

        
//  clear toggles
         if (wParam  ==  VK_SPACE)
            memset(g_blend_flags, 
1 sizeof (g_blend_flags));

        
break ;

    
case  WM_KEYDOWN:
        
if (wParam  ==  VK_ESCAPE)
            DestroyWindow(hwnd);
        
break ;

    
case  WM_DESTROY:
        PostQuitMessage(
0 );
        
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_blend_anim_collection.load( " ..\\Data\\tiny.x " ))
        
return   false ;

    
//  map the animation to the frame hierarchy
    g_blend_anim_collection.map_frames(g_frame);

    
//  load the guide texture and create the sprite interface

    D3DXCreateTextureFromFileEx(g_device, 
" ..\\Data\\Guide.bmp " , D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 
                                
0 , D3DFMT_A1R5G5B5, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT,
                                
0xFF000000 , NULL, NULL,  & g_guide_texture);

    D3DXCreateSprite(g_device, 
& g_guide_sprite);

    
//  clear toggles
    memset(g_blend_flags,  1 sizeof (g_blend_flags));

    
return   true ;
}

void  do_shutdown()
{
    
//  free mesh data
    delete g_mesh_container;    g_mesh_container  =  NULL;
    delete g_frame;                g_frame 
=  NULL;

    
//  free guide texture and sprite interface
    release_com(g_guide_texture);
    release_com(g_guide_sprite);
    
    
//  release D3D objects
    release_com(g_device);
    release_com(g_d3d);
}

void  do_frame()
{
    
static  DWORD start_time  =  timeGetTime();
    DWORD curr_time 
=  timeGetTime();

    g_frame
-> reset();

    DWORD time 
=  curr_time  -  start_time;

    
//  blend the animations

    
if (g_blend_flags[ 0 ])
        g_blend_anim_collection.blend(
" left_arm " ,    time,  true 1.5f );
    
    
if (g_blend_flags[ 1 ])
        g_blend_anim_collection.blend(
" right_arm " ,    time,  true 1.5f );

    
if (g_blend_flags[ 2 ])
        g_blend_anim_collection.blend(
" left_leg " ,    time,  true 1.2f );

    
if (g_blend_flags[ 3 ])
        g_blend_anim_collection.blend(
" right_leg " ,    time,  true 1.2f );

    
if (g_blend_flags[ 4 ])
        g_blend_anim_collection.blend(
" body " ,        time,  true 1.0f );

    
//  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
    
    D3DXMATRIX  mat_view;
    D3DXVECTOR3 eye(
600.0f 200.0f - 600.0f );
    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 64 255 ),  1.0f 0 );

    g_device
-> BeginScene();    

    draw_mesh(g_mesh_container);

    
//  enable per pixel alpha testing
    g_device -> SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);

    
//  specifies a reference alpha value against which pixels are tested
    g_device -> SetRenderState(D3DRS_ALPHAREF,  0x01 );

    
//  accept the new pixel if its value is greater than the value of the current pixel
    g_device -> SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);

    
//  draw the guid teture
    g_guide_sprite -> Begin( 0 );
    g_guide_sprite
-> Draw(g_guide_texture, NULL, NULL, NULL, D3DCOLOR_RGBA( 255  , 255 0 255 ));
    g_guide_sprite
-> End();

    g_device
-> SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);

    g_device
-> EndScene();

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

 

download source file


你可能感兴趣的:(Blending Skeletal Animations(3))