X文件的动画类及基本使用方法

------------------------------Animation.h--------------------------------------没有Animation.cpp-----------

#ifndef __AnimationH__
#define __AnimationH__

#include <d3d9.h>
#include <d3dx9.h>

//struct D3DXFRAME_DERIVED & D3DXMESHCONTAINER_DERIVED
struct D3DXFRAME_DERIVED : public D3DXFRAME
{
 D3DXMATRIXA16 CombinedTransformationMatrix;
};
struct D3DXMESHCONTAINER_DERIVED : public D3DXMESHCONTAINER
{
 LPDIRECT3DTEXTURE9* ppTextures;       // array of textures, entries are NULL if no texture specified   

 // SkinMesh info            
 LPD3DXMESH pOrigMesh;
 LPD3DXATTRIBUTERANGE pAttributeTable;
 DWORD NumAttributeGroups;
 DWORD NumInfl;
 LPD3DXBUFFER pBoneCombinationBuf;
 D3DXMATRIX** ppBoneMatrixPtrs;
 D3DXMATRIX* pBoneOffsetMatrices;
 DWORD NumPaletteEntries;
 bool UseSoftwareVP;
 DWORD iAttributeSW;     // used to denote the split between SW and HW if necessary for non-indexed skinning
};

//class CAllocateHierarchy
class CAllocateHierarchy : public ID3DXAllocateHierarchy
{
public:
 STDMETHOD( CreateFrame )( THIS_ LPCSTR Name, LPD3DXFRAME *ppNewFrame );
 STDMETHOD( CreateMeshContainer )( THIS_
  LPCSTR Name,
  CONST D3DXMESHDATA *pMeshData,
  CONST D3DXMATERIAL *pMaterials,
  CONST D3DXEFFECTINSTANCE *pEffectInstances,
  DWORD NumMaterials,
  CONST DWORD *pAdjacency,
  LPD3DXSKININFO pSkinInfo,
  LPD3DXMESHCONTAINER *ppNewMeshContainer );
 STDMETHOD( DestroyFrame )( THIS_ LPD3DXFRAME pFrameToFree );
 STDMETHOD( DestroyMeshContainer )( THIS_ LPD3DXMESHCONTAINER pMeshContainerBase );
 HRESULT AllocateName( LPCSTR Name, LPSTR* pNewName );
 HRESULT GenerateSkinnedMesh( IDirect3DDevice9* pd3dDevice, D3DXMESHCONTAINER_DERIVED* pMeshContainer );
 CAllocateHierarchy()
 {
 }
};
HRESULT CAllocateHierarchy::CreateFrame( LPCSTR Name, LPD3DXFRAME* ppNewFrame )
{
 HRESULT hr = S_OK;
 D3DXFRAME_DERIVED* pFrame;

 *ppNewFrame = NULL;

 pFrame = new D3DXFRAME_DERIVED;
 if( pFrame == NULL )
 {
  hr = E_OUTOFMEMORY;
  goto e_Exit;
 }

 hr = AllocateName( Name, &pFrame->Name );
 if( FAILED( hr ) )
  goto e_Exit;

 // initialize other data members of the frame
 D3DXMatrixIdentity( &pFrame->TransformationMatrix );
 D3DXMatrixIdentity( &pFrame->CombinedTransformationMatrix );

 pFrame->pMeshContainer = NULL;
 pFrame->pFrameSibling = NULL;
 pFrame->pFrameFirstChild = NULL;

 *ppNewFrame = pFrame;
 pFrame = NULL;

e_Exit:
 delete pFrame;
 return hr;
}
HRESULT CAllocateHierarchy::CreateMeshContainer(
 LPCSTR Name,
 CONST D3DXMESHDATA *pMeshData,
 CONST D3DXMATERIAL *pMaterials,
 CONST D3DXEFFECTINSTANCE *pEffectInstances,
 DWORD NumMaterials,
 CONST DWORD *pAdjacency,
 LPD3DXSKININFO pSkinInfo,
 LPD3DXMESHCONTAINER *ppNewMeshContainer )
{
 HRESULT hr;
 D3DXMESHCONTAINER_DERIVED *pMeshContainer = NULL;
 UINT NumFaces;
 UINT iMaterial;
 UINT iBone, cBones;
 LPDIRECT3DDEVICE9 pd3dDevice = NULL;

 LPD3DXMESH pMesh = NULL;

 *ppNewMeshContainer = NULL;

 // this sample does not handle patch meshes, so fail when one is found
 if( pMeshData->Type != D3DXMESHTYPE_MESH )
 {
  hr = E_FAIL;
  goto e_Exit;
 }

 // get the pMesh interface pointer out of the mesh data structure
 pMesh = pMeshData->pMesh;

 // this sample does not FVF compatible meshes, so fail when one is found
 if( pMesh->GetFVF() == 0 )
 {
  hr = E_FAIL;
  goto e_Exit;
 }

 // allocate the overloaded structure to return as a D3DXMESHCONTAINER
 pMeshContainer = new D3DXMESHCONTAINER_DERIVED;
 if( pMeshContainer == NULL )
 {
  hr = E_OUTOFMEMORY;
  goto e_Exit;
 }
 memset( pMeshContainer, 0, sizeof( D3DXMESHCONTAINER_DERIVED ) );

 // make sure and copy the name.  All memory as input belongs to caller, interfaces can be addref'd though
 hr = AllocateName( Name, &pMeshContainer->Name );
 if( FAILED( hr ) )
  goto e_Exit;

 pMesh->GetDevice( &pd3dDevice );
 NumFaces = pMesh->GetNumFaces();

 // if no normals are in the mesh, add them
 if( !( pMesh->GetFVF() & D3DFVF_NORMAL ) )
 {
  pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;

  // clone the mesh to make room for the normals
  hr = pMesh->CloneMeshFVF( pMesh->GetOptions(),
   pMesh->GetFVF() | D3DFVF_NORMAL,
   pd3dDevice, &pMeshContainer->MeshData.pMesh );
  if( FAILED( hr ) )
   goto e_Exit;

  // get the new pMesh pointer back out of the mesh container to use
  // NOTE: we do not release pMesh because we do not have a reference to it yet
  pMesh = pMeshContainer->MeshData.pMesh;

  // now generate the normals for the pmesh
  D3DXComputeNormals( pMesh, NULL );
 }
 else  // if no normals, just add a reference to the mesh for the mesh container
 {
  pMeshContainer->MeshData.pMesh = pMesh;
  pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;

  pMesh->AddRef();
 }

 // allocate memory to contain the material information.  This sample uses
 //   the D3D9 materials and texture names instead of the EffectInstance style materials
 pMeshContainer->NumMaterials = max( 1, NumMaterials );
 pMeshContainer->pMaterials = new D3DXMATERIAL[pMeshContainer->NumMaterials];
 pMeshContainer->ppTextures = new LPDIRECT3DTEXTURE9[pMeshContainer->NumMaterials];
 pMeshContainer->pAdjacency = new DWORD[NumFaces*3];
 if( ( pMeshContainer->pAdjacency == NULL ) || ( pMeshContainer->pMaterials == NULL ) )
 {
  hr = E_OUTOFMEMORY;
  goto e_Exit;
 }

 memcpy( pMeshContainer->pAdjacency, pAdjacency, sizeof( DWORD ) * NumFaces*3 );
 memset( pMeshContainer->ppTextures, 0, sizeof( LPDIRECT3DTEXTURE9 ) * pMeshContainer->NumMaterials );

 // if materials provided, copy them
 if( NumMaterials > 0 )
 {
  memcpy( pMeshContainer->pMaterials, pMaterials, sizeof( D3DXMATERIAL ) * NumMaterials );

  for( iMaterial = 0; iMaterial < NumMaterials; iMaterial++ )
  {
   if( pMeshContainer->pMaterials[iMaterial].pTextureFilename != NULL )
   {
    WCHAR strTexturePath[MAX_PATH];
    MultiByteToWideChar( CP_ACP, 0, pMeshContainer->pMaterials[iMaterial].pTextureFilename, -1, strTexturePath, MAX_PATH );
    strTexturePath[MAX_PATH - 1] = L'\0';
    if( FAILED( D3DXCreateTextureFromFile( pd3dDevice, strTexturePath,
     &pMeshContainer->ppTextures[iMaterial] ) ) )
     pMeshContainer->ppTextures[iMaterial] = NULL;

    // don't remember a pointer into the dynamic memory, just forget the name after loading
    pMeshContainer->pMaterials[iMaterial].pTextureFilename = NULL;
   }
  }
 }
 else // if no materials provided, use a default one
 {
  pMeshContainer->pMaterials[0].pTextureFilename = NULL;
  memset( &pMeshContainer->pMaterials[0].MatD3D, 0, sizeof( D3DMATERIAL9 ) );
  pMeshContainer->pMaterials[0].MatD3D.Diffuse.r = 0.5f;
  pMeshContainer->pMaterials[0].MatD3D.Diffuse.g = 0.5f;
  pMeshContainer->pMaterials[0].MatD3D.Diffuse.b = 0.5f;
  pMeshContainer->pMaterials[0].MatD3D.Specular = pMeshContainer->pMaterials[0].MatD3D.Diffuse;
 }

 // if there is skinning information, save off the required data and then setup for HW skinning
 if( pSkinInfo != NULL )
 {
  // first save off the SkinInfo and original mesh data
  pMeshContainer->pSkinInfo = pSkinInfo;
  pSkinInfo->AddRef();

  pMeshContainer->pOrigMesh = pMesh;
  pMesh->AddRef();

  // Will need an array of offset matrices to move the vertices from the figure space to the bone's space
  cBones = pSkinInfo->GetNumBones();
  pMeshContainer->pBoneOffsetMatrices = new D3DXMATRIX[cBones];
  if( pMeshContainer->pBoneOffsetMatrices == NULL )
  {
   hr = E_OUTOFMEMORY;
   goto e_Exit;
  }

  // get each of the bone offset matrices so that we don't need to get them later
  for( iBone = 0; iBone < cBones; iBone++ )
  {
   pMeshContainer->pBoneOffsetMatrices[iBone] = *( pMeshContainer->pSkinInfo->GetBoneOffsetMatrix( iBone ) );
  }

  // GenerateSkinnedMesh will take the general skinning information and transform it to a HW friendly version
  hr = GenerateSkinnedMesh( pd3dDevice, pMeshContainer );
  if( FAILED( hr ) )
   goto e_Exit;
 }

 *ppNewMeshContainer = pMeshContainer;
 pMeshContainer = NULL;

e_Exit:
 SAFE_RELEASE( pd3dDevice );

 // call Destroy function to properly clean up the memory allocated
 if( pMeshContainer != NULL )
 {
  DestroyMeshContainer( pMeshContainer );
 }

 return hr;
}
HRESULT CAllocateHierarchy::DestroyFrame( LPD3DXFRAME pFrameToFree )
{
 SAFE_DELETE_ARRAY( pFrameToFree->Name );
 SAFE_DELETE( pFrameToFree );
 return S_OK;
}
HRESULT CAllocateHierarchy::DestroyMeshContainer( LPD3DXMESHCONTAINER pMeshContainerBase )
{
 UINT iMaterial;
 D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;

 SAFE_DELETE_ARRAY( pMeshContainer->Name );
 SAFE_DELETE_ARRAY( pMeshContainer->pAdjacency );
 SAFE_DELETE_ARRAY( pMeshContainer->pMaterials );
 SAFE_DELETE_ARRAY( pMeshContainer->pBoneOffsetMatrices );

 // release all the allocated textures
 if( pMeshContainer->ppTextures != NULL )
 {
  for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ )
  {
   SAFE_RELEASE( pMeshContainer->ppTextures[iMaterial] );
  }
 }

 SAFE_DELETE_ARRAY( pMeshContainer->ppTextures );
 SAFE_DELETE_ARRAY( pMeshContainer->ppBoneMatrixPtrs );
 SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
 SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
 SAFE_RELEASE( pMeshContainer->pSkinInfo );
 SAFE_RELEASE( pMeshContainer->pOrigMesh );
 SAFE_DELETE( pMeshContainer );
 return S_OK;
}
HRESULT CAllocateHierarchy::AllocateName( LPCSTR Name, LPSTR* pNewName )
{
 UINT cbLength;

 if( Name != NULL )
 {
  cbLength = ( UINT )strlen( Name ) + 1;
  *pNewName = new CHAR[cbLength];
  if( *pNewName == NULL )
   return E_OUTOFMEMORY;
  memcpy( *pNewName, Name, cbLength * sizeof( CHAR ) );
 }
 else
 {
  *pNewName = NULL;
 }

 return S_OK;
}
HRESULT CAllocateHierarchy::GenerateSkinnedMesh( IDirect3DDevice9* pd3dDevice, D3DXMESHCONTAINER_DERIVED* pMeshContainer )
{
 HRESULT hr = S_OK;
 D3DCAPS9 d3dCaps;
 pd3dDevice->GetDeviceCaps( &d3dCaps );

 if( pMeshContainer->pSkinInfo == NULL )
  return hr;

 SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
 SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );

 hr = pMeshContainer->pSkinInfo->ConvertToBlendedMesh
  (
  pMeshContainer->pOrigMesh,
  D3DXMESH_MANAGED | D3DXMESHOPT_VERTEXCACHE,
  pMeshContainer->pAdjacency,
  NULL, NULL, NULL,
  &pMeshContainer->NumInfl,
  &pMeshContainer->NumAttributeGroups,
  &pMeshContainer->pBoneCombinationBuf,
  &pMeshContainer->MeshData.pMesh
  );
 if( FAILED( hr ) )
  goto e_Exit;


 // If the device can only do 2 matrix blends, ConvertToBlendedMesh cannot approximate all meshes to it
 // Thus we split the mesh in two parts: The part that uses at most 2 matrices and the rest. The first is
 // drawn using the device's HW vertex processing and the rest is drawn using SW vertex processing.
 LPD3DXBONECOMBINATION rgBoneCombinations = reinterpret_cast<LPD3DXBONECOMBINATION>(
  pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );

 // look for any set of bone combinations that do not fit the caps
 for( pMeshContainer->iAttributeSW = 0; pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups;
  pMeshContainer->iAttributeSW++ )
 {
  DWORD cInfl = 0;

  for( DWORD iInfl = 0; iInfl < pMeshContainer->NumInfl; iInfl++ )
  {
   if( rgBoneCombinations[pMeshContainer->iAttributeSW].BoneId[iInfl] != UINT_MAX )
   {
    ++cInfl;
   }
  }

  if( cInfl > d3dCaps.MaxVertexBlendMatrices )
  {
   break;
  }
 }

 // if there is both HW and SW, add the Software Processing flag
 if( pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups )
 {
  LPD3DXMESH pMeshTmp;

  hr = pMeshContainer->MeshData.pMesh->CloneMeshFVF( D3DXMESH_SOFTWAREPROCESSING |
   pMeshContainer->MeshData.pMesh->GetOptions(),
   pMeshContainer->MeshData.pMesh->GetFVF(),
   pd3dDevice, &pMeshTmp );
  if( FAILED( hr ) )
  {
   goto e_Exit;
  }

  pMeshContainer->MeshData.pMesh->Release();
  pMeshContainer->MeshData.pMesh = pMeshTmp;
  pMeshTmp = NULL;
 }

e_Exit:
 return hr;
}

//class CAnimation
class CAnimation
{
private:
 IDirect3DDevice9* m_pDevice;
 LPD3DXFRAME m_pFrameRoot;
 ID3DXAnimationController* m_pAnimController;
 D3DXVECTOR3 m_vObjectCenter;
 FLOAT m_fObjectRadius;
private:
 HRESULT SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainerBase );
 HRESULT SetupBoneMatrixPointers(LPD3DXFRAME pFrame);
 void UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix );
 void DrawMeshContainer( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase );
 void DrawFrame( IDirect3DDevice9* pd3dDevice, LPD3DXFRAME pFrame );
public:
 friend class CAllocateHierarchy;
 void Init(IDirect3DDevice9* pDevice, char* sFileName);
 void Logic(float fElapsedTime);
 void Render(float fElapsedTime);
};
void CAnimation::Init(IDirect3DDevice9* pDevice, char* sFileName)
{
 m_pDevice = pDevice;
 CAllocateHierarchy Alloc;
 D3DXLoadMeshHierarchyFromXA( sFileName, D3DXMESH_MANAGED, m_pDevice,
  &Alloc, NULL, &m_pFrameRoot, &m_pAnimController );
 SetupBoneMatrixPointers( m_pFrameRoot );
 D3DXFrameCalculateBoundingSphere( m_pFrameRoot, &m_vObjectCenter, &m_fObjectRadius );
}
void CAnimation::Logic(float fElapsedTime)
{
 m_pAnimController->AdvanceTime(fElapsedTime, 0);
 D3DXMATRIX matWorld;
 D3DXMatrixScaling(&matWorld, 0.01f, 0.01f, 0.01f);
 UpdateFrameMatrices(m_pFrameRoot, &matWorld);
}
void CAnimation::Render(float fElapsedTime)
{
 DrawFrame(m_pDevice, m_pFrameRoot);
}
HRESULT CAnimation::SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainerBase )
{
 UINT iBone, cBones;
 D3DXFRAME_DERIVED* pFrame;

 D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;

 // if there is a skinmesh, then setup the bone matrices
 if( pMeshContainer->pSkinInfo != NULL )
 {
  cBones = pMeshContainer->pSkinInfo->GetNumBones();

  pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[cBones];
  if( pMeshContainer->ppBoneMatrixPtrs == NULL )
   return E_OUTOFMEMORY;

  for( iBone = 0; iBone < cBones; iBone++ )
  {
   pFrame = ( D3DXFRAME_DERIVED* )D3DXFrameFind( m_pFrameRoot,
    pMeshContainer->pSkinInfo->GetBoneName( iBone ) );
   if( pFrame == NULL )
    return E_FAIL;

   pMeshContainer->ppBoneMatrixPtrs[iBone] = &pFrame->CombinedTransformationMatrix;
  }
 }

 return S_OK;
}
HRESULT CAnimation::SetupBoneMatrixPointers( LPD3DXFRAME pFrame )
{
 HRESULT hr;

 if( pFrame->pMeshContainer != NULL )
 {
  hr = SetupBoneMatrixPointersOnMesh( pFrame->pMeshContainer );
  if( FAILED( hr ) )
   return hr;
 }

 if( pFrame->pFrameSibling != NULL )
 {
  hr = SetupBoneMatrixPointers( pFrame->pFrameSibling );
  if( FAILED( hr ) )
   return hr;
 }

 if( pFrame->pFrameFirstChild != NULL )
 {
  hr = SetupBoneMatrixPointers( pFrame->pFrameFirstChild );
  if( FAILED( hr ) )
   return hr;
 }

 return S_OK;
}
void CAnimation::UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix )
{
 D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )pFrameBase;

 if( pParentMatrix != NULL )
  D3DXMatrixMultiply( &pFrame->CombinedTransformationMatrix, &pFrame->TransformationMatrix, pParentMatrix );
 else
  pFrame->CombinedTransformationMatrix = pFrame->TransformationMatrix;

 if( pFrame->pFrameSibling != NULL )
 {
  UpdateFrameMatrices( pFrame->pFrameSibling, pParentMatrix );
 }

 if( pFrame->pFrameFirstChild != NULL )
 {
  UpdateFrameMatrices( pFrame->pFrameFirstChild, &pFrame->CombinedTransformationMatrix );
 }
}
void CAnimation::DrawMeshContainer( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase )
{
 HRESULT hr;
 D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;
 D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )pFrameBase;
 UINT iMaterial;
 UINT NumBlend;
 UINT iAttrib;
 DWORD AttribIdPrev;
 LPD3DXBONECOMBINATION pBoneComb;

 UINT iMatrixIndex;
 D3DXMATRIXA16 matTemp;
 D3DCAPS9 d3dCaps;
 pd3dDevice->GetDeviceCaps( &d3dCaps );

 // first check for skinning
 if( pMeshContainer->pSkinInfo != NULL )
 {
  AttribIdPrev = UNUSED32;
  pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer
   () );

  // Draw using default vtx processing of the device (typically HW)
  for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
  {
   NumBlend = 0;
   for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
   {
    if( pBoneComb[iAttrib].BoneId[i] != UINT_MAX )
    {
     NumBlend = i;
    }
   }

   if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 )
   {
    // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
    for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
    {
     iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
     if( iMatrixIndex != UINT_MAX )
     {
      D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
       pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
      V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) );
     }
    }

    V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) );

    // lookup the material used for this subset of faces
    if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) )
    {
     V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D )
      );
     V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
     AttribIdPrev = pBoneComb[iAttrib].AttribId;
    }

    // draw the subset now that the correct material and matrices are loaded
    V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
   }
  }

  // If necessary, draw parts that HW could not handle using SW
  if( pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups )
  {
   AttribIdPrev = UNUSED32;
   V( pd3dDevice->SetSoftwareVertexProcessing( TRUE ) );
   for( iAttrib = pMeshContainer->iAttributeSW; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
   {
    NumBlend = 0;
    for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
    {
     if( pBoneComb[iAttrib].BoneId[i] != UINT_MAX )
     {
      NumBlend = i;
     }
    }

    if( d3dCaps.MaxVertexBlendMatrices < NumBlend + 1 )
    {
     // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
     for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
     {
      iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
      if( iMatrixIndex != UINT_MAX )
      {
       D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
        pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
       V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) );
      }
     }

     V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) );

     // lookup the material used for this subset of faces
     if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) )
     {
      V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D
       ) );
      V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
      AttribIdPrev = pBoneComb[iAttrib].AttribId;
     }

     // draw the subset now that the correct material and matrices are loaded
     V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
    }
   }
   V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) );
  }

  V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, 0 ) );
 }
 else  // standard mesh, just draw it after setting material properties
 {
  V( pd3dDevice->SetTransform( D3DTS_WORLD, &pFrame->CombinedTransformationMatrix ) );

  for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ )
  {
   V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[iMaterial].MatD3D ) );
   V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[iMaterial] ) );
   V( pMeshContainer->MeshData.pMesh->DrawSubset( iMaterial ) );
  }
 }
}
void CAnimation::DrawFrame( IDirect3DDevice9* pd3dDevice, LPD3DXFRAME pFrame )
{
 LPD3DXMESHCONTAINER pMeshContainer;

 pMeshContainer = pFrame->pMeshContainer;
 while( pMeshContainer != NULL )
 {
  DrawMeshContainer( pd3dDevice, pMeshContainer, pFrame );

  pMeshContainer = pMeshContainer->pNextMeshContainer;
 }

 if( pFrame->pFrameSibling != NULL )
 {
  DrawFrame( pd3dDevice, pFrame->pFrameSibling );
 }

 if( pFrame->pFrameFirstChild != NULL )
 {
  DrawFrame( pd3dDevice, pFrame->pFrameFirstChild );
 }
}

#endif

//使用方法
//CAnimation Animation;
//Animation.Init(g_pDevice, "sFileName"); -->添加到游戏的初始化中
//Animation.Logic(fElaspedTime); -->添加到游戏的逻辑更新中
//Animation.Render(fElapsedTime); -->添加到游戏的绘制中

//对于其中Logic的具体实现可以更改

原文地址:https://www.cnblogs.com/ketmales/p/2466250.html