Direct-X学习笔记--骨骼动画进阶

上一次,封装了一个简单的骨骼动画类,但是,这个类有很多问题。第一,只能播放一个默认的动画,第二,一个动画的实例里面包含了所有的资源,而我们只是绘制的时候需要资源,实例本身并不需要都包含资源,所以只需要一个指针指向资源,绘制的时候通过这个指针渲染一下就好了,所以,这次在进行一次封装,实现一个支持多实例,多动画的骨骼动画类。

一.AnimationSet

一个.X文件中可能包含多个动画,比如idle,walk等等,这些动画都存储在.x文件中。一个动画称为一个AnimationSet,一个AnimationSet包含一个或者多个Animation,而一个Animation包含一个或者几个AnimationKey,即关键帧。我们暂时不去了解过于细节的问题,只需要去控制我们的模型,播放哪个动画就好了。所以,我们关注的地方就是AnimationSet。
打开一个骨骼动画,微软自带的那个tiny.X,就是那个很丑的小人,Ctrl+F找到AnimationSet:

好像确实是上面的那种分类方式,没有什么特殊的。不过,这个是只有一个默认的动作的骨骼动画模型,所以AnimationSet并没有命名,我们也没有办法控制它播放哪一个动画。下面,我们打开DX为我们准备的另一个Sample,多动画的那个例子,这里面有个tiny_4anim.x文件,这里面包含了四个骨骼动画,我们再看一下:

看到AnimationSet后面跟了个名字,叫Walk。我们依次查看一下,这四个动画分别为Walk,Loiter,Jog,Wave。即AnimationSet都是有名字的。好了,既然知道了名字,我们就想到了,通过名字,来命令DX播放不同的骨骼动画,DX确实为我们提供了这样一个接口。不过,还得再等等,因为还有一个东西木有学会,就是AnimationController--动画控制器。

二.AnimationController、

AnimationController,即动画控制器。我们使用骨骼动画的时候,要通过这个动画控制器来控制动画的播放,切换等等。在上次的那个类中,其实已经写过一个动画控制器的。而且,这个动画控制器是会在模型载入时,通过DX给的API初始化完成的。但是,我们并不能用这个动画控制器!为什么呢?答案很简单,因为,一个动画控制器对应着一个动画的实例的当前状态,假如我们所有的实例内部都包含了骨骼动画资源的话,那么倒是无需考虑这个了。但是,游戏中有那么多实例,资源仅仅需要一份就够了,我们只是需要在绘制的时候,用一下这个骨骼动画模型的指针,把这个模型渲染一下就行了。但是,为什么动画控制器也需要多个呢?比如10个实例的话,这10个实例可能在做不同的动作,速度可能也不一样,如果我们都采用相同的动画控制器的话,那么,这10个实例的动作会高度统一...简直比阅兵都齐。
那么,我们要怎么样为每个动画实例创建一个动画控制器呢?其实这个东东DX也为我们准备好了,我们也是调用一下API就可以拷贝出一个独立的动画控制器,放在我们的实例中,这样,每个实例都有自己的动画控制器,就可以分别以不同的速度或者频道做不同的动作啦!

注意吼!这个AnimationControl貌似是浅复制的,所以,我们在销毁动画实例的时候是不需要delete掉这个AnimationController的。如果delete掉一个,程序会崩掉哦!

三.进一步封装的多实例多动画骨骼动画类

这里还是简单的贴上之前的代码:

  
  1. /*!
  2. * \file AllocateHierarchy.h
  3. *
  4. * \author puppet_master
  5. * \date 九月 2015
  6. *
  7. * \微软SDK自带的关于骨骼动画的类,用于骨骼动画的创建以及绘制更新
  8. * \注:程序不直接使用该类,而是将此类二次封装后再使用。
  9. */
  10. #ifndef __ALLOCATEHIERARCHY_H_
  11. #define __ALLOCATEHIERARCHY_H_
  12. //--------------------------------------------------------------------------------------
  13. // Name: struct D3DXFRAME_DERIVED
  14. // Desc:
  15. //--------------------------------------------------------------------------------------
  16. struct D3DXFRAME_DERIVED : public D3DXFRAME
  17. {
  18. D3DXMATRIXA16 CombinedTransformationMatrix;
  19. };
  20. //--------------------------------------------------------------------------------------
  21. // Name: struct D3DXMESHCONTAINER_DERIVED
  22. // Desc: Structure derived from D3DXMESHCONTAINER so we can add some app-specific
  23. // info that will be stored with each mesh
  24. //--------------------------------------------------------------------------------------
  25. struct D3DXMESHCONTAINER_DERIVED : public D3DXMESHCONTAINER
  26. {
  27. //纹理信息
  28. LPDIRECT3DTEXTURE9* ppTextures; //纹理数组
  29. //网格信息
  30. LPD3DXMESH pOrigMesh; //原始网格
  31. LPD3DXATTRIBUTERANGE pAttributeTable; //属性表
  32. DWORD NumAttributeGroups; //属性组数量(子网格数量)
  33. DWORD NumInfl; //每个顶点最多受几个骨骼影响
  34. LPD3DXBUFFER pBoneCombinationBuf; //骨骼结合缓存
  35. D3DXMATRIX** ppBoneMatrixPtrs; //骨骼组合变换矩阵
  36. D3DXMATRIX* pBoneOffsetMatrices; //骨骼初始变换矩阵
  37. DWORD NumPaletteEntries; //骨骼数量上限
  38. bool UseSoftwareVP; //是否使用软件顶点处理
  39. DWORD iAttributeSW; // used to denote the split between SW and HW if necessary for non-indexed skinning
  40. };
  41. //--------------------------------------------------------------------------------------
  42. // Name: class CAllocateHierarchy
  43. // Desc: Custom version of ID3DXAllocateHierarchy with custom methods to create
  44. // frames and meshcontainers.
  45. //用来从.X文件中加载网格以及动画数据
  46. //--------------------------------------------------------------------------------------
  47. class CAllocateHierarchy : public ID3DXAllocateHierarchy
  48. {
  49. private:
  50. HRESULT AllocateName( LPCSTR Name, LPSTR* pNewName );
  51. HRESULT GenerateSkinnedMesh( IDirect3DDevice9* pd3dDevice, D3DXMESHCONTAINER_DERIVED* pMeshContainer );
  52. public:
  53. STDMETHOD( CreateFrame )( THIS_ LPCSTR Name, LPD3DXFRAME *ppNewFrame );
  54. STDMETHOD( CreateMeshContainer )( THIS_
  55. LPCSTR Name,
  56. CONST D3DXMESHDATA *pMeshData,
  57. CONST D3DXMATERIAL *pMaterials,
  58. CONST D3DXEFFECTINSTANCE *pEffectInstances,
  59. DWORD NumMaterials,
  60. CONST DWORD *pAdjacency,
  61. LPD3DXSKININFO pSkinInfo,
  62. LPD3DXMESHCONTAINER *ppNewMeshContainer );
  63. STDMETHOD( DestroyFrame )( THIS_ LPD3DXFRAME pFrameToFree );
  64. STDMETHOD( DestroyMeshContainer )( THIS_ LPD3DXMESHCONTAINER pMeshContainerBase );
  65. CAllocateHierarchy()
  66. {
  67. }
  68. };
  69. #endif


.cpp文件

  
  1. #include "stdafx.h"
  2. #include "AllocateHierarchy.h"
  3. HRESULT CAllocateHierarchy::AllocateName( LPCSTR Name, LPSTR* pNewName )
  4. {
  5. UINT cbLength;
  6. if( Name != NULL )
  7. {
  8. cbLength = ( UINT ) strlen( Name ) + 1;
  9. *pNewName = new CHAR[cbLength];
  10. if( *pNewName == NULL )
  11. return E_OUTOFMEMORY;
  12. memcpy( *pNewName, Name, cbLength * sizeof( CHAR ) );
  13. }
  14. else
  15. {
  16. *pNewName = NULL;
  17. }
  18. return S_OK;
  19. }
  20. //--------------------------------------------------------------------------------------
  21. // Called either by CreateMeshContainer when loading a skin mesh, or when
  22. // changing methods. This function uses the pSkinInfo of the mesh
  23. // container to generate the desired drawable mesh and bone combination
  24. // table.
  25. //--------------------------------------------------------------------------------------
  26. HRESULT CAllocateHierarchy::GenerateSkinnedMesh( IDirect3DDevice9* pd3dDevice, D3DXMESHCONTAINER_DERIVED* pMeshContainer )
  27. {
  28. D3DCAPS9 d3dCaps;
  29. pd3dDevice->GetDeviceCaps( &d3dCaps );
  30. if( pMeshContainer->pSkinInfo == NULL )
  31. return S_OK;
  32. SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
  33. SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
  34. if (FAILED(pMeshContainer->pSkinInfo->ConvertToBlendedMesh(
  35. pMeshContainer->pOrigMesh,
  36. D3DXMESH_MANAGED | D3DXMESHOPT_VERTEXCACHE,
  37. pMeshContainer->pAdjacency,
  38. NULL, NULL, NULL,
  39. &pMeshContainer->NumInfl,
  40. &pMeshContainer->NumAttributeGroups,
  41. &pMeshContainer->pBoneCombinationBuf,
  42. &pMeshContainer->MeshData.pMesh)))
  43. return E_FAIL;
  44. return S_OK;
  45. }
  46. //--------------------------------------------------------------------------------------
  47. // Name: CAllocateHierarchy::CreateFrame()
  48. // Desc: 创建框架,分配内存&初始化
  49. //--------------------------------------------------------------------------------------
  50. HRESULT CAllocateHierarchy::CreateFrame( LPCSTR Name, LPD3DXFRAME* ppNewFrame )
  51. {
  52. HRESULT hr = S_OK;
  53. D3DXFRAME_DERIVED* pFrame;
  54. *ppNewFrame = NULL;
  55. pFrame = new D3DXFRAME_DERIVED;
  56. if( pFrame == NULL )
  57. {
  58. hr = E_OUTOFMEMORY;
  59. goto e_Exit;
  60. }
  61. hr = AllocateName( Name, &pFrame->Name );
  62. if( FAILED( hr ) )
  63. goto e_Exit;
  64. // initialize other data members of the frame
  65. D3DXMatrixIdentity( &pFrame->TransformationMatrix );
  66. D3DXMatrixIdentity( &pFrame->CombinedTransformationMatrix );
  67. pFrame->pMeshContainer = NULL;
  68. pFrame->pFrameSibling = NULL;
  69. pFrame->pFrameFirstChild = NULL;
  70. *ppNewFrame = pFrame;
  71. pFrame = NULL;
  72. e_Exit:
  73. delete pFrame;
  74. return hr;
  75. }
  76. //--------------------------------------------------------------------------------------
  77. // Name: CAllocateHierarchy::CreateMeshContainer()
  78. // Desc: 创建网格容器对象,保存网格模型数据
  79. //--------------------------------------------------------------------------------------
  80. HRESULT CAllocateHierarchy::CreateMeshContainer(
  81. LPCSTR Name,
  82. CONST D3DXMESHDATA *pMeshData,
  83. CONST D3DXMATERIAL *pMaterials,
  84. CONST D3DXEFFECTINSTANCE *pEffectInstances,
  85. DWORD NumMaterials,
  86. CONST DWORD *pAdjacency,
  87. LPD3DXSKININFO pSkinInfo,
  88. LPD3DXMESHCONTAINER *ppNewMeshContainer )
  89. {
  90. HRESULT hr;
  91. D3DXMESHCONTAINER_DERIVED *pMeshContainer = NULL;
  92. UINT NumFaces;
  93. UINT iMaterial;
  94. UINT iBone, cBones;
  95. LPDIRECT3DDEVICE9 pd3dDevice = NULL;
  96. LPD3DXMESH pMesh = NULL;
  97. *ppNewMeshContainer = NULL;
  98. // this sample does not handle patch meshes, so fail when one is found
  99. if( pMeshData->Type != D3DXMESHTYPE_MESH )
  100. {
  101. hr = E_FAIL;
  102. goto e_Exit;
  103. }
  104. // get the pMesh interface pointer out of the mesh data structure
  105. pMesh = pMeshData->pMesh;
  106. // this sample does not FVF compatible meshes, so fail when one is found
  107. if( pMesh->GetFVF() == 0 )
  108. {
  109. hr = E_FAIL;
  110. goto e_Exit;
  111. }
  112. // allocate the overloaded structure to return as a D3DXMESHCONTAINER
  113. pMeshContainer = new D3DXMESHCONTAINER_DERIVED;
  114. if( pMeshContainer == NULL )
  115. {
  116. hr = E_OUTOFMEMORY;
  117. goto e_Exit;
  118. }
  119. memset( pMeshContainer, 0, sizeof( D3DXMESHCONTAINER_DERIVED ) );
  120. // make sure and copy the name. All memory as input belongs to caller, interfaces can be addref'd though
  121. hr = AllocateName( Name, &pMeshContainer->Name );
  122. if( FAILED( hr ) )
  123. goto e_Exit;
  124. pMesh->GetDevice( &pd3dDevice );
  125. NumFaces = pMesh->GetNumFaces();
  126. // if no normals are in the mesh, add them
  127. if( !( pMesh->GetFVF() & D3DFVF_NORMAL ) )
  128. {
  129. pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;
  130. // clone the mesh to make room for the normals
  131. hr = pMesh->CloneMeshFVF( pMesh->GetOptions(),
  132. pMesh->GetFVF() | D3DFVF_NORMAL,
  133. pd3dDevice, &pMeshContainer->MeshData.pMesh );
  134. if( FAILED( hr ) )
  135. goto e_Exit;
  136. // get the new pMesh pointer back out of the mesh container to use
  137. // NOTE: we do not release pMesh because we do not have a reference to it yet
  138. pMesh = pMeshContainer->MeshData.pMesh;
  139. // now generate the normals for the pmesh
  140. D3DXComputeNormals( pMesh, NULL );
  141. }
  142. else // if no normals, just add a reference to the mesh for the mesh container
  143. {
  144. pMeshContainer->MeshData.pMesh = pMesh;
  145. pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;
  146. pMesh->AddRef();
  147. }
  148. // allocate memory to contain the material information. This sample uses
  149. // the D3D9 materials and texture names instead of the EffectInstance style materials
  150. pMeshContainer->NumMaterials = max( 1, NumMaterials );
  151. pMeshContainer->pMaterials = new D3DXMATERIAL[pMeshContainer->NumMaterials];
  152. pMeshContainer->ppTextures = new LPDIRECT3DTEXTURE9[pMeshContainer->NumMaterials];
  153. pMeshContainer->pAdjacency = new DWORD[NumFaces* 3];
  154. if( ( pMeshContainer->pAdjacency == NULL ) || ( pMeshContainer->pMaterials == NULL ) )
  155. {
  156. hr = E_OUTOFMEMORY;
  157. goto e_Exit;
  158. }
  159. memcpy( pMeshContainer->pAdjacency, pAdjacency, sizeof( DWORD ) * NumFaces* 3 );
  160. memset( pMeshContainer->ppTextures, 0, sizeof( LPDIRECT3DTEXTURE9 ) * pMeshContainer->NumMaterials );
  161. // if materials provided, copy them
  162. if( NumMaterials > 0 )
  163. {
  164. memcpy( pMeshContainer->pMaterials, pMaterials, sizeof( D3DXMATERIAL ) * NumMaterials );
  165. for( iMaterial = 0; iMaterial < NumMaterials; iMaterial++ )
  166. {
  167. if( pMeshContainer->pMaterials[iMaterial].pTextureFilename != NULL )
  168. {
  169. if( FAILED( D3DXCreateTextureFromFile( pd3dDevice, pMeshContainer->pMaterials[iMaterial].pTextureFilename,
  170. &pMeshContainer->ppTextures[iMaterial] ) ) )
  171. pMeshContainer->ppTextures[iMaterial] = NULL;
  172. // don't remember a pointer into the dynamic memory, just forget the name after loading
  173. pMeshContainer->pMaterials[iMaterial].pTextureFilename = NULL;
  174. }
  175. }
  176. }
  177. else // if no materials provided, use a default one
  178. {
  179. pMeshContainer->pMaterials[ 0].pTextureFilename = NULL;
  180. memset( &pMeshContainer->pMaterials[ 0].MatD3D, 0, sizeof( D3DMATERIAL9 ) );
  181. pMeshContainer->pMaterials[ 0].MatD3D.Diffuse.r = 0.5f;
  182. pMeshContainer->pMaterials[ 0].MatD3D.Diffuse.g = 0.5f;
  183. pMeshContainer->pMaterials[ 0].MatD3D.Diffuse.b = 0.5f;
  184. pMeshContainer->pMaterials[ 0].MatD3D.Specular = pMeshContainer->pMaterials[ 0].MatD3D.Diffuse;
  185. }
  186. // if there is skinning information, save off the required data and then setup for HW skinning
  187. if( pSkinInfo != NULL )
  188. {
  189. // first save off the SkinInfo and original mesh data
  190. pMeshContainer->pSkinInfo = pSkinInfo;
  191. pSkinInfo->AddRef();
  192. pMeshContainer->pOrigMesh = pMesh;
  193. pMesh->AddRef();
  194. // Will need an array of offset matrices to move the vertices from the figure space to the bone's space
  195. cBones = pSkinInfo->GetNumBones();
  196. pMeshContainer->pBoneOffsetMatrices = new D3DXMATRIX[cBones];
  197. if( pMeshContainer->pBoneOffsetMatrices == NULL )
  198. {
  199. hr = E_OUTOFMEMORY;
  200. goto e_Exit;
  201. }
  202. // get each of the bone offset matrices so that we don't need to get them later
  203. for( iBone = 0; iBone < cBones; iBone++ )
  204. {
  205. pMeshContainer->pBoneOffsetMatrices[iBone] = *( pMeshContainer->pSkinInfo->GetBoneOffsetMatrix( iBone ) );
  206. }
  207. // GenerateSkinnedMesh will take the general skinning information and transform it to a HW friendly version
  208. hr = GenerateSkinnedMesh( pd3dDevice, pMeshContainer );
  209. if( FAILED( hr ) )
  210. goto e_Exit;
  211. }
  212. *ppNewMeshContainer = pMeshContainer;
  213. pMeshContainer = NULL;
  214. e_Exit:
  215. SAFE_RELEASE( pd3dDevice );
  216. // call Destroy function to properly clean up the memory allocated
  217. if( pMeshContainer != NULL )
  218. {
  219. DestroyMeshContainer( pMeshContainer );
  220. }
  221. return hr;
  222. }
  223. //--------------------------------------------------------------------------------------
  224. // Name: CAllocateHierarchy::DestroyFrame()
  225. // Desc: 释放框架
  226. //--------------------------------------------------------------------------------------
  227. HRESULT CAllocateHierarchy::DestroyFrame( LPD3DXFRAME pFrameToFree )
  228. {
  229. SAFE_DELETE_ARRAY( pFrameToFree->Name );
  230. SAFE_DELETE( pFrameToFree );
  231. return S_OK;
  232. }
  233. //--------------------------------------------------------------------------------------
  234. // Name: CAllocateHierarchy::DestroyMeshContainer()
  235. // Desc: 释放网格容器
  236. //--------------------------------------------------------------------------------------
  237. HRESULT CAllocateHierarchy::DestroyMeshContainer( LPD3DXMESHCONTAINER pMeshContainerBase )
  238. {
  239. UINT iMaterial;
  240. D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;
  241. SAFE_DELETE_ARRAY( pMeshContainer->Name );
  242. SAFE_DELETE_ARRAY( pMeshContainer->pAdjacency );
  243. SAFE_DELETE_ARRAY( pMeshContainer->pMaterials );
  244. SAFE_DELETE_ARRAY( pMeshContainer->pBoneOffsetMatrices );
  245. // release all the allocated textures
  246. if( pMeshContainer->ppTextures != NULL )
  247. {
  248. for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ )
  249. {
  250. SAFE_RELEASE( pMeshContainer->ppTextures[iMaterial] );
  251. }
  252. }
  253. SAFE_DELETE_ARRAY( pMeshContainer->ppTextures );
  254. SAFE_DELETE_ARRAY( pMeshContainer->ppBoneMatrixPtrs );
  255. SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
  256. SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
  257. SAFE_RELEASE( pMeshContainer->pSkinInfo );
  258. SAFE_RELEASE( pMeshContainer->pOrigMesh );
  259. SAFE_DELETE( pMeshContainer );
  260. return S_OK;
  261. }

D3DXAnimation.h修改版本:

  
  1. /*!
  2. * \file D3DXAnimation.h
  3. *
  4. * \author puppet_master
  5. * \date 九月 2015
  6. *
  7. * \封装了微软自带的骨骼动画相关功能,内部包含读取骨骼动画,播放等功能。
  8. * \不可以直接使用,需要克隆一个动画控制器,创建单独的实例才可以使用
  9. */
  10. #ifndef __D3DXANIMATION_H_
  11. #define __D3DXANIMATION_H_
  12. #include "AllocateHierarchy.h"
  13. class CD3DXAnimation
  14. {
  15. private:
  16. IDirect3DDevice9* m_pDevice; //D3D设备对象
  17. CAllocateHierarchy* m_pAllocateHier; //骨骼动画网格模型指针
  18. LPD3DXFRAME m_pFrameRoot; //帧
  19. LPD3DXANIMATIONCONTROLLER m_pAnimController; //动画控制器
  20. private:
  21. //一些微软自带函数,关于骨骼动画加载与绘制更新的函数,将其封装,不使用这些接口
  22. void DrawMeshContainer( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase );
  23. void DrawFrame( IDirect3DDevice9* pd3dDevice, LPD3DXFRAME pFrame );
  24. HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrameBase, LPD3DXFRAME pFrameRoot );
  25. void UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix );
  26. public:
  27. CD3DXAnimation(IDirect3DDevice9* device);
  28. ~CD3DXAnimation( void);
  29. //提供给外界的接口
  30. //创建骨骼动画
  31. bool Init(LPCTSTR filename);
  32. //复制骨骼动画控制器
  33. LPD3DXANIMATIONCONTROLLER CloneAnimCtrl(void);
  34. //绘制骨骼动画
  35. void Render(const LPD3DXMATRIX matrix);
  36. };
  37. #endif

.cpp文件:


  
  1. #include "stdafx.h"
  2. #include "D3DXAnimation.h"
  3. #include "TempDebug.h"
  4. CD3DXAnimation::CD3DXAnimation(IDirect3DDevice9* device)
  5. :m_pDevice(device),
  6. m_pAllocateHier( NULL),
  7. m_pAnimController( NULL),
  8. m_pFrameRoot( NULL)
  9. // m_BoneMatrix(NULL)
  10. {
  11. }
  12. CD3DXAnimation::~CD3DXAnimation( void)
  13. {
  14. D3DXFrameDestroy(m_pFrameRoot, m_pAllocateHier);
  15. SAFE_RELEASE(m_pAnimController);
  16. SAFE_DELETE(m_pAllocateHier);
  17. }
  18. //--------------------------------------------------------------------------------------
  19. // Name: SetupBoneMatrixPointers()
  20. // Desc: 设置好各级框架的组合变换矩阵。
  21. //--------------------------------------------------------------------------------------
  22. HRESULT CD3DXAnimation::SetupBoneMatrixPointers( LPD3DXFRAME pFrameBase, LPD3DXFRAME pFrameRoot )
  23. {
  24. if( pFrameBase->pMeshContainer != NULL )
  25. {
  26. D3DXFRAME_DERIVED* pFrame = NULL;
  27. D3DXMESHCONTAINER_DERIVED* pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pFrameBase->pMeshContainer;
  28. // if there is a skinmesh, then setup the bone matrices
  29. if (pMeshContainer->pSkinInfo != NULL)
  30. {
  31. UINT cBones = pMeshContainer->pSkinInfo->GetNumBones();
  32. pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[cBones];
  33. for (UINT iBone = 0; iBone < cBones; iBone++)
  34. {
  35. pFrame = (D3DXFRAME_DERIVED*)D3DXFrameFind(pFrameRoot, pMeshContainer->pSkinInfo->GetBoneName(iBone));
  36. if (pFrame == NULL) return E_FAIL;
  37. pMeshContainer->ppBoneMatrixPtrs[iBone] = &pFrame->CombinedTransformationMatrix;
  38. }
  39. }
  40. }
  41. if (pFrameBase->pFrameSibling != NULL)
  42. {
  43. if (FAILED(SetupBoneMatrixPointers(pFrameBase->pFrameSibling, pFrameRoot)))
  44. return E_FAIL;
  45. }
  46. if (pFrameBase->pFrameFirstChild != NULL)
  47. {
  48. if (FAILED(SetupBoneMatrixPointers(pFrameBase->pFrameFirstChild, pFrameRoot)))
  49. return E_FAIL;
  50. }
  51. return S_OK;
  52. }
  53. //--------------------------------------------------------------------------------------
  54. // Name: DrawFrame()
  55. // Desc: 绘制骨骼
  56. //--------------------------------------------------------------------------------------
  57. void CD3DXAnimation::DrawFrame( IDirect3DDevice9* pd3dDevice, LPD3DXFRAME pFrame )
  58. {
  59. if (pFrame == NULL) return;
  60. LPD3DXMESHCONTAINER pMeshContainer;
  61. pMeshContainer = pFrame->pMeshContainer; // 取得网格容器
  62. while( pMeshContainer != NULL )
  63. {
  64. DrawMeshContainer(pd3dDevice, pMeshContainer, pFrame); // 绘制非空蒙皮网格
  65. pMeshContainer = pMeshContainer->pNextMeshContainer; // 遍历所有网格容器
  66. }
  67. DrawFrame(pd3dDevice, pFrame->pFrameSibling); // 绘制兄弟框架
  68. DrawFrame(pd3dDevice, pFrame->pFrameFirstChild); // 绘制子框架
  69. }
  70. //--------------------------------------------------------------------------------------
  71. // Name: DrawMeshContainer()
  72. // Desc: 绘制蒙皮容器中的蒙皮网格
  73. //--------------------------------------------------------------------------------------
  74. void CD3DXAnimation::DrawMeshContainer( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase )
  75. {
  76. D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;
  77. D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )pFrameBase;
  78. UINT iMaterial;
  79. UINT NumBlend;
  80. UINT iAttrib;
  81. DWORD AttribIdPrev;
  82. LPD3DXBONECOMBINATION pBoneComb;
  83. UINT iMatrixIndex;
  84. D3DXMATRIXA16 matTemp;
  85. D3DCAPS9 d3dCaps;
  86. pd3dDevice->GetDeviceCaps( &d3dCaps );
  87. // first check for skinning
  88. if( pMeshContainer->pSkinInfo != NULL )
  89. {
  90. AttribIdPrev = UNUSED32;
  91. pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );
  92. // Draw using default vtx processing of the device (typically HW)
  93. for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
  94. {
  95. NumBlend = 0;
  96. for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
  97. {
  98. if( pBoneComb[iAttrib].BoneId[i] != UINT_MAX )
  99. {
  100. NumBlend = i;
  101. }
  102. }
  103. if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 )
  104. {
  105. // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
  106. for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
  107. {
  108. iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
  109. if( iMatrixIndex != UINT_MAX )
  110. {
  111. D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
  112. pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
  113. pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp );
  114. }
  115. }
  116. pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend );
  117. // lookup the material used for this subset of faces
  118. if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) )
  119. {
  120. pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D );
  121. pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] );
  122. AttribIdPrev = pBoneComb[iAttrib].AttribId;
  123. }
  124. // draw the subset now that the correct material and matrices are loaded
  125. pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib );
  126. }
  127. }
  128. pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, 0 );
  129. }
  130. else // standard mesh, just draw it after setting material properties
  131. {
  132. pd3dDevice->SetTransform( D3DTS_WORLD, &pFrame->CombinedTransformationMatrix );
  133. for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ )
  134. {
  135. pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[iMaterial].MatD3D );
  136. pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[iMaterial] );
  137. pMeshContainer->MeshData.pMesh->DrawSubset( iMaterial );
  138. }
  139. }
  140. }
  141. //--------------------------------------------------------------------------------------
  142. // Name: UpdateFrameMatrics()
  143. // Desc: 更新框架中的变换矩阵
  144. //--------------------------------------------------------------------------------------
  145. void CD3DXAnimation::UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix )
  146. {
  147. if (pFrameBase == NULL || pParentMatrix == NULL) return;
  148. D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )pFrameBase;
  149. // 将当前骨骼的相对于父骨骼的偏移矩阵作累积运算
  150. D3DXMatrixMultiply(&pFrame->CombinedTransformationMatrix, &pFrame->TransformationMatrix, pParentMatrix);
  151. UpdateFrameMatrices(pFrame->pFrameSibling, pParentMatrix); // 更新兄弟骨骼
  152. UpdateFrameMatrices(pFrame->pFrameFirstChild, &pFrame->CombinedTransformationMatrix); // 更新子骨骼
  153. }
  154. //---------------------------------------------------------
  155. //Name:真正暴露给外部调用的函数
  156. //Desc:关于动画的创建,更新,绘制
  157. //---------------------------------------------------------
  158. bool CD3DXAnimation::Init(LPCTSTR filename)
  159. {
  160. m_pAllocateHier = new CAllocateHierarchy();
  161. D3DXLoadMeshHierarchyFromX(filename, D3DXMESH_MANAGED, m_pDevice, m_pAllocateHier, NULL, &m_pFrameRoot, &m_pAnimController);
  162. SetupBoneMatrixPointers(m_pFrameRoot, m_pFrameRoot);
  163. return true;
  164. }
  165. void CD3DXAnimation::Render( const LPD3DXMATRIX matrix)
  166. {
  167. //m_pDevice->SetTransform(D3DTS_WORLD, matrix);
  168. UpdateFrameMatrices(m_pFrameRoot, matrix);
  169. DrawFrame(m_pDevice, m_pFrameRoot);
  170. }
  171. LPD3DXANIMATIONCONTROLLER CD3DXAnimation::CloneAnimCtrl( void)
  172. {
  173. //克隆一个动画控制器,真正使用的是克隆的动画控制器,而并非原始的动画控制器,原始的动画控制器仅供克隆。
  174. LPD3DXANIMATIONCONTROLLER pControl = NULL;
  175. if(FAILED(m_pAnimController->CloneAnimationController(
  176. m_pAnimController->GetMaxNumAnimationOutputs(),
  177. m_pAnimController->GetMaxNumAnimationSets(),
  178. m_pAnimController->GetMaxNumTracks(),
  179. m_pAnimController->GetMaxNumEvents(),
  180. &pControl )))
  181. {
  182. return NULL;
  183. }
  184. return pControl;
  185. }

这次最主要的类--骨骼动画实例类,
AnimInstance.h:

  
  1. /*!
  2. * \file AnimInstance.h
  3. *
  4. * \author puppet_master
  5. * \date 九月 2015
  6. * \骨骼动画实例类:可以直接使用的骨骼动画实例,内部包含骨骼动画绘制指针与单独的控制器。
  7. */
  8. #ifndef __ANIMINSTANCE_H_
  9. #define __ANIMINSTANCE_H_
  10. #include "D3DXAnimation.h"
  11. class CAnimInstance
  12. {
  13. private:
  14. CD3DXAnimation* m_pAnimMesh;
  15. LPD3DXANIMATIONCONTROLLER m_pAnimController;
  16. D3DXMATRIX m_Matrix;
  17. float m_fSpeed;
  18. bool m_bIsLoop;
  19. public:
  20. CAnimInstance( void);
  21. ~CAnimInstance( void);
  22. //初始化一个动画网格实例,获得动画网格资源指针,并拷贝出一个单独的动画控制器
  23. bool Init(CD3DXAnimation* mesh);
  24. //绘制实例
  25. void Render();
  26. //根据名称播放动画
  27. bool PlayAnimation(LPCTSTR name, bool isLoop = true);
  28. //更新动画
  29. void Update(float delayTime);
  30. //设置动画速度
  31. void SetSpeed(float speed);
  32. //获得动画速度
  33. float GetSpeed()const{ return m_fSpeed;}
  34. //更新矩阵
  35. void SetMatrix(const LPD3DXMATRIX matrix);
  36. //获得矩阵
  37. D3DMATRIX GetMatrix(){ return m_Matrix;}
  38. //获得骨骼动画个数
  39. int GetAnimationNum() const;
  40. //根据动画编号获得动画集
  41. LPD3DXANIMATIONSET GetAnimationSet(int index) const;
  42. };
  43. #endif
.cpp:

  
  1. #include "stdafx.h"
  2. #include "AnimInstance.h"
  3. CAnimInstance::CAnimInstance( void)
  4. {
  5. }
  6. CAnimInstance::~CAnimInstance( void)
  7. {
  8. }
  9. bool CAnimInstance::Init(CD3DXAnimation* mesh)
  10. {
  11. m_pAnimMesh = mesh;
  12. return m_pAnimController = mesh->CloneAnimCtrl();
  13. }
  14. void CAnimInstance::Render()
  15. {
  16. m_pAnimMesh->Render(&m_Matrix);
  17. }
  18. bool CAnimInstance::PlayAnimation(LPCTSTR name, bool isLoop)
  19. {
  20. LPD3DXANIMATIONSET pAnimationSet = NULL;
  21. m_pAnimController->GetAnimationSetByName(name, &pAnimationSet);
  22. m_pAnimController->SetTrackAnimationSet( 0, pAnimationSet);
  23. return true;
  24. }
  25. void CAnimInstance::Update( float delayTime)
  26. {
  27. m_pAnimController->AdvanceTime(delayTime, NULL);
  28. }
  29. void CAnimInstance::SetSpeed( float speed)
  30. {
  31. m_fSpeed = speed;
  32. }
  33. void CAnimInstance::SetMatrix( const LPD3DXMATRIX matrix)
  34. {
  35. m_Matrix = *matrix;
  36. }
  37. int CAnimInstance::GetAnimationNum() const
  38. {
  39. return m_pAnimController->GetMaxNumAnimationSets();
  40. }
  41. LPD3DXANIMATIONSET CAnimInstance::GetAnimationSet( int index) const
  42. {
  43. if (index >= 0 && index < GetAnimationNum())
  44. {
  45. LPD3DXANIMATIONSET pAnimSet = NULL;
  46. m_pAnimController->GetAnimationSet(index, &pAnimSet);
  47. return pAnimSet;
  48. }
  49. return NULL;
  50. }


好了,来简单说明一下这个类。在使用这个类之前,需要先加载骨骼动画类,即第二个类,通过那个骨骼动画类,加载.x文件内部的资源。AnimInstance初始化的时候,需要一个D3DXAnimation的指针。并且,调用D3DXAnimation类的CloneAnimCtrl方法,克隆一个动画控制器给当前Instance。然后我们就可以放心大胆的使用这个Instance来创建骨骼动画实例了。想建几个建几个,因为资源只需要一份,实例仅需要一个资源的指针就可以搞定一切啦。

这次,我们读取一下tiny_4anim.x这个.X文件,然后创建10个实例,让他们随机的播放动画:

不过瘾,来50个看看:


posted @ 2022-04-06 23:53  szmtjs10  阅读(166)  评论(0)    收藏  举报