学了几个月DX了,终于到了骨骼动画这一步了,好激动!之前导入过一些静态的模型,还是挺帅的。不过不能动,实在是太遗憾了。今天学习了骨骼动画,终于让偶的模型动起来啦!!!

一.简介

说到动画,其实本人又想起了最早开始写小游戏的时候从网上找的那些关键帧素材,设置一个定时器,或者根据游戏循环,不断切换图片,就形成了动态的效果。不过这样实在是太麻烦了,需要美术一张一张的画图或者用PS调整。到了3D时代,骨骼动画这个神器,可以大大的缩短动画的制作周期。
3D中的动画,就是我们常说的模型动画。一共有三种:
1.关节动画:将网格拆分为多个刚体,按照关键帧插值运算,能够实现复杂的动画效果。但是,模型各部分交接的地方容易出现缝隙。
2.渐变动画:与2D游戏中的关键帧类似,每一帧的所有顶点位置等等都记录下来,效果很好,但是由于记录的数据太多,文件很大,载入很慢,还占地方。
3.骨骼蒙皮动画:这种就是现在最常用的方法,取上两者之精华,去之糟粕。占用空间小,但是表现力还很好。
骨骼蒙皮模型中含有按照一定层次组织起来的骨骼,还含有单一网格模型的蒙皮。骨骼层次仿照关节动画的组织结构将模型组织成一个整体,相邻骨骼之间通过关节相连,通过关键帧确定骨骼位置朝向等信息,然后再动画序列中相邻两个关键帧之间做差值运算,获得完整的动画信息。这样就可以很好的表现出动画,而又不至于占用过大空间。

二.X文件

.X文件,之前也有用过,LoadMeshFromFile。就是简单的把网格载入程序中,读取其中的顶点信息,纹理材质信息,然后调用DX的API将它画出来。不过,这只是普通的网格,其实X文件中还可以包含更加深层次的东东,就是骨骼动画。
这个是微软自带的例子:SkinMesh中的那个人物,自带了骨骼动画。看一下.X文件中记录了什么东东:
AnimationSet 动画的组合,包括一个或者多个Animation。
  Animation 描述一个动画,包含一个或几个AnimationKey
  AnimationKey 动画关键帧,定义具体的动作数据,包括一些列旋转、移动、放缩、矩阵变换。
  ColorRGB 定义RGB对象,包括三个Float的值,分别是R、G、B。
  ColorRGBA 定义RGBA对象。包括四个Float的值,分别是R、G、B、alpha。
  Coords2d 定义纹理坐标向量,包括两个Float值,分别是u、v。
  FloatKeys 定义浮点数组,用来定义动画键数值,包括两个部分:浮点值个数,浮点值列表。
  Material 定义材质信息,可以被应用到一个完整的Mesh对象,也可以应用到其中的一个面。包含:
           1.FaceColor环境光
           2.Power镜面反射的强度
           3.specularcolor镜面反射等等。
  Matrix4X4 定义4X4矩阵,16个浮点数值。
  Mesh 定义个Mesh对象,共有9个部分组成:
1、包含的顶点数
2、顶点列表,一个顶点包含三个浮点值
3、面数
4、面的顶点索引列表,每个面包含三个顶点
5、MeshFaceWraps 结构,暂时无用
6、MeshTextureCoords纹理坐标,可选
7、MeshNormals 法向,可选
8、MeshVertexColors 顶点颜色,默认为白色
9、MeshMaterialList 材质,不提供的话默认为白色。
  MeshFace 面索引,包含两部分:面数,定点索引构成的面数组。
  MeshTextureCoords 定义纹理坐标,包括:纹理坐标的个数,纹理坐标(每个纹理坐标有两个浮点值)。
  MeshMaterialList 定义材质的应用,包括:多少个材质被使用,材质影响面的个数,面索引。
  MeshNormals 定义Mesh的法向量,包括4部分:
1.nNormals法向量的个数=顶点数
2.Normals顶点法向量列表
3.nFaceNormals面的个数
4.FaceNormals面对应的法向量。
  MeshVertexColors  指定顶点的颜色代替原来的材质,包含:顶点数目,颜色索引
  TextureFilename  纹理的名称,字符串类型。
  VertexDuplicationIndices  保留副本,用于精简Mesh的操作,包含:顶点数,原始顶点数,实际顶点数。
  XSkinMeshHeader  描述被导出的SkinMesh相关信息,影响一个顶点的最多变换数目,影响每个面三个顶点的最大变换数目,影响一个顶点的骨骼数。
  TimedFloatKeys 时间值,用于Animaterkey中定义时间间隔。
  Vector 三维向量,三个浮点值。
  SkinWeights 定义骨骼影响权重。包括以下几个部分:骨骼的名字,有多少个权重值,顶点的索引列表等等

三.封装一个容易使用的骨骼动画类

DX这个.X文件也看不太懂,不过,我们也没必要看懂这个,关键还是怎么用。给我们一个带动画的.X文件,我们能够把它读取,并播放出来才是最重要的。这个骨骼动画相关的内容貌似还是比较难,不过好在网上有一些教程,最重要的是DX官方文档给的那个例子,其实已经封装了一个可以使用的骨骼动画类。不过他还需要几个全局函数和全局变量才能读取并绘制出骨骼动画,还是没有彻底封装。所以,我们进一步把这几个函数和全局变量加上DX给的那个类,进行再次封装,写一个简单粗暴的骨骼动画类。只用几步就可以播放骨骼动画。
AllocateHierarchy.h文件(DX官方给出的类):
/*!* \file AllocateHierarchy.h** \author puppet_master* \date 九月 2015** \微软SDK自带的关于骨骼动画的类,用于骨骼动画的创建以及绘制更新* \注:程序不直接使用该类,而是将此类二次封装后再使用。*/#ifndef __ALLOCATEHIERARCHY_H_
#define __ALLOCATEHIERARCHY_H_//--------------------------------------------------------------------------------------
// Name: struct D3DXFRAME_DERIVED
// Desc:
//--------------------------------------------------------------------------------------
struct D3DXFRAME_DERIVED : public D3DXFRAME
{D3DXMATRIXA16 CombinedTransformationMatrix;
};//--------------------------------------------------------------------------------------
// Name: struct D3DXMESHCONTAINER_DERIVED
// Desc: Structure derived from D3DXMESHCONTAINER so we can add some app-specific
//       info that will be stored with each mesh
//--------------------------------------------------------------------------------------
struct D3DXMESHCONTAINER_DERIVED : public D3DXMESHCONTAINER
{//纹理信息LPDIRECT3DTEXTURE9* ppTextures;       //纹理数组  //网格信息             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
};//--------------------------------------------------------------------------------------
// Name: class CAllocateHierarchy
// Desc: Custom version of ID3DXAllocateHierarchy with custom methods to create
//       frames and meshcontainers.
//用来从.X文件中加载网格以及动画数据
//--------------------------------------------------------------------------------------
class CAllocateHierarchy : public ID3DXAllocateHierarchy
{
private:HRESULT AllocateName( LPCSTR Name, LPSTR* pNewName );HRESULT GenerateSkinnedMesh( IDirect3DDevice9* pd3dDevice, D3DXMESHCONTAINER_DERIVED* pMeshContainer );
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 );CAllocateHierarchy(){}
};#endif
AllocateHierarchy.cpp文件:
#include "stdafx.h"
#include "AllocateHierarchy.h"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;
}//--------------------------------------------------------------------------------------
// Called either by CreateMeshContainer when loading a skin mesh, or when
// changing methods.  This function uses the pSkinInfo of the mesh
// container to generate the desired drawable mesh and bone combination
// table.
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::GenerateSkinnedMesh( IDirect3DDevice9* pd3dDevice, D3DXMESHCONTAINER_DERIVED* pMeshContainer )
{D3DCAPS9 d3dCaps;pd3dDevice->GetDeviceCaps( &d3dCaps );if( pMeshContainer->pSkinInfo == NULL )return S_OK;SAFE_RELEASE( pMeshContainer->MeshData.pMesh );SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );if (FAILED(pMeshContainer->pSkinInfo->ConvertToBlendedMesh(pMeshContainer->pOrigMesh, D3DXMESH_MANAGED | D3DXMESHOPT_VERTEXCACHE,pMeshContainer->pAdjacency,NULL, NULL, NULL,&pMeshContainer->NumInfl,&pMeshContainer->NumAttributeGroups,&pMeshContainer->pBoneCombinationBuf,&pMeshContainer->MeshData.pMesh)))return E_FAIL;return S_OK;
}//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::CreateFrame()
// Desc: 创建框架,分配内存&初始化
//--------------------------------------------------------------------------------------
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 frameD3DXMatrixIdentity( &pFrame->TransformationMatrix );D3DXMatrixIdentity( &pFrame->CombinedTransformationMatrix );pFrame->pMeshContainer = NULL;pFrame->pFrameSibling = NULL;pFrame->pFrameFirstChild = NULL;*ppNewFrame = pFrame;pFrame = NULL;e_Exit:delete pFrame;return hr;
}//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::CreateMeshContainer()
// Desc: 创建网格容器对象,保存网格模型数据
//--------------------------------------------------------------------------------------
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 foundif( pMeshData->Type != D3DXMESHTYPE_MESH ){hr = E_FAIL;goto e_Exit;}// get the pMesh interface pointer out of the mesh data structurepMesh = pMeshData->pMesh;// this sample does not FVF compatible meshes, so fail when one is foundif( pMesh->GetFVF() == 0 ){hr = E_FAIL;goto e_Exit;}// allocate the overloaded structure to return as a D3DXMESHCONTAINERpMeshContainer = 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 thoughhr = AllocateName( Name, &pMeshContainer->Name );if( FAILED( hr ) )goto e_Exit;pMesh->GetDevice( &pd3dDevice );NumFaces = pMesh->GetNumFaces();// if no normals are in the mesh, add themif( !( pMesh->GetFVF() & D3DFVF_NORMAL ) ){pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;// clone the mesh to make room for the normalshr = 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 yetpMesh = pMeshContainer->MeshData.pMesh;// now generate the normals for the pmeshD3DXComputeNormals( 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 materialspMeshContainer->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 themif( NumMaterials > 0 ){memcpy( pMeshContainer->pMaterials, pMaterials, sizeof( D3DXMATERIAL ) * NumMaterials );for( iMaterial = 0; iMaterial < NumMaterials; iMaterial++ ){if( pMeshContainer->pMaterials[iMaterial].pTextureFilename != NULL ){if( FAILED( D3DXCreateTextureFromFile( pd3dDevice, pMeshContainer->pMaterials[iMaterial].pTextureFilename,&pMeshContainer->ppTextures[iMaterial] ) ) )pMeshContainer->ppTextures[iMaterial] = NULL;// don't remember a pointer into the dynamic memory, just forget the name after loadingpMeshContainer->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 skinningif( pSkinInfo != NULL ){// first save off the SkinInfo and original mesh datapMeshContainer->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 spacecBones = 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 laterfor( 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 versionhr = 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;
}//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::DestroyFrame()
// Desc: 释放框架
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::DestroyFrame( LPD3DXFRAME pFrameToFree )
{SAFE_DELETE_ARRAY( pFrameToFree->Name );SAFE_DELETE( pFrameToFree );return S_OK;
}//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::DestroyMeshContainer()
// Desc: 释放网格容器
//--------------------------------------------------------------------------------------
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 texturesif( 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;
}
D3DXAnimation.h文件(我们封装的类):
/*!* \file D3DXAnimation.h** \author puppet_master* \date 九月 2015** \封装了微软自带的骨骼动画相关功能,提供一个简单的接口供使用*/#ifndef __D3DXANIMATION_H_
#define __D3DXANIMATION_H_#include "AllocateHierarchy.h"class CD3DXAnimation
{
private:IDirect3DDevice9*   m_pDevice;                  //D3D设备对象CAllocateHierarchy* m_pAllocateHier;           //骨骼动画网格模型指针LPD3DXFRAME         m_pFrameRoot;               //帧LPD3DXANIMATIONCONTROLLER m_pAnimController; //动画控制器D3DXMATRIX*          m_pBoneMatrix;              //骨骼矩阵
private://一些微软自带函数,关于骨骼动画加载与绘制更新的函数,将其封装,不使用这些接口void DrawMeshContainer( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase );void DrawFrame( IDirect3DDevice9* pd3dDevice, LPD3DXFRAME pFrame );HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrameBase, LPD3DXFRAME pFrameRoot );void UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix );
public:CD3DXAnimation(IDirect3DDevice9* device);~CD3DXAnimation(void);//提供给外界的接口//创建骨骼动画bool Init(LPCTSTR filename);//通过名字设置要播放的骨骼动画void SetAnimationByName(LPCTSTR name);//更新位置void SetMatrix(LPD3DXMATRIX mtrix);//更新动画void UpdateAnimation(double timeDelay);//绘制骨骼动画void Render();
};#endif

D3DXAnimation.cpp文件:
#include "stdafx.h"
#include "D3DXAnimation.h"CD3DXAnimation::CD3DXAnimation(IDirect3DDevice9* device):m_pDevice(device),m_pAllocateHier(NULL),m_pAnimController(NULL),m_pFrameRoot(NULL),m_pBoneMatrix(NULL)
{
}CD3DXAnimation::~CD3DXAnimation(void)
{D3DXFrameDestroy(m_pFrameRoot, m_pAllocateHier);SAFE_RELEASE(m_pAnimController);SAFE_DELETE(m_pAllocateHier);
}//--------------------------------------------------------------------------------------
// Name: SetupBoneMatrixPointers()
// Desc: 设置好各级框架的组合变换矩阵。
//--------------------------------------------------------------------------------------
HRESULT CD3DXAnimation::SetupBoneMatrixPointers( LPD3DXFRAME pFrameBase, LPD3DXFRAME pFrameRoot )
{if( pFrameBase->pMeshContainer != NULL ){D3DXFRAME_DERIVED* pFrame = NULL;D3DXMESHCONTAINER_DERIVED* pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pFrameBase->pMeshContainer;// if there is a skinmesh, then setup the bone matricesif (pMeshContainer->pSkinInfo != NULL){UINT cBones = pMeshContainer->pSkinInfo->GetNumBones();pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[cBones];for (UINT iBone = 0; iBone < cBones; iBone++){pFrame = (D3DXFRAME_DERIVED*)D3DXFrameFind(pFrameRoot, pMeshContainer->pSkinInfo->GetBoneName(iBone));if (pFrame == NULL) return E_FAIL;pMeshContainer->ppBoneMatrixPtrs[iBone] = &pFrame->CombinedTransformationMatrix;}}}if (pFrameBase->pFrameSibling != NULL){if (FAILED(SetupBoneMatrixPointers(pFrameBase->pFrameSibling, pFrameRoot)))return E_FAIL;}if (pFrameBase->pFrameFirstChild != NULL){if (FAILED(SetupBoneMatrixPointers(pFrameBase->pFrameFirstChild, pFrameRoot)))return E_FAIL;}return S_OK;
}//--------------------------------------------------------------------------------------
// Name: DrawFrame()
// Desc: 绘制骨骼
//--------------------------------------------------------------------------------------
void CD3DXAnimation::DrawFrame( IDirect3DDevice9* pd3dDevice, LPD3DXFRAME pFrame )
{if (pFrame == NULL) return;LPD3DXMESHCONTAINER pMeshContainer;pMeshContainer = pFrame->pMeshContainer;                    // 取得网格容器while( pMeshContainer != NULL )                      {DrawMeshContainer(pd3dDevice, pMeshContainer, pFrame);  // 绘制非空蒙皮网格pMeshContainer = pMeshContainer->pNextMeshContainer;    // 遍历所有网格容器}DrawFrame(pd3dDevice, pFrame->pFrameSibling);               // 绘制兄弟框架DrawFrame(pd3dDevice, pFrame->pFrameFirstChild);            // 绘制子框架
}//--------------------------------------------------------------------------------------
// Name: DrawMeshContainer()
// Desc: 绘制蒙皮容器中的蒙皮网格
//--------------------------------------------------------------------------------------
void CD3DXAnimation::DrawMeshContainer( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase )
{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 skinningif( 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 blendsfor( DWORD i = 0; i < pMeshContainer->NumInfl; ++i ){iMatrixIndex = pBoneComb[iAttrib].BoneId[i];if( iMatrixIndex != UINT_MAX ){D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp );}}pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend );// lookup the material used for this subset of facesif( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) ){pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D );pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] );AttribIdPrev = pBoneComb[iAttrib].AttribId;}// draw the subset now that the correct material and matrices are loadedpMeshContainer->MeshData.pMesh->DrawSubset( iAttrib );}}pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, 0 );}else  // standard mesh, just draw it after setting material properties{pd3dDevice->SetTransform( D3DTS_WORLD, &pFrame->CombinedTransformationMatrix );for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ ){pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[iMaterial].MatD3D );pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[iMaterial] );pMeshContainer->MeshData.pMesh->DrawSubset( iMaterial );}}
}//--------------------------------------------------------------------------------------
// Name: UpdateFrameMatrics()
// Desc: 更新框架中的变换矩阵
//--------------------------------------------------------------------------------------
void CD3DXAnimation::UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix )
{if (pFrameBase == NULL || pParentMatrix == NULL) return;D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )pFrameBase;// 将当前骨骼的相对于父骨骼的偏移矩阵作累积运算D3DXMatrixMultiply(&pFrame->CombinedTransformationMatrix, &pFrame->TransformationMatrix, pParentMatrix);UpdateFrameMatrices(pFrame->pFrameSibling, pParentMatrix);                              // 更新兄弟骨骼UpdateFrameMatrices(pFrame->pFrameFirstChild, &pFrame->CombinedTransformationMatrix);   // 更新子骨骼
}//---------------------------------------------------------
//Name:真正暴露给外部调用的函数
//Desc:关于动画的创建,更新,绘制
//---------------------------------------------------------bool CD3DXAnimation::Init(LPCTSTR filename)
{m_pAllocateHier = new CAllocateHierarchy();D3DXLoadMeshHierarchyFromX(filename, D3DXMESH_MANAGED, m_pDevice, m_pAllocateHier, NULL, &m_pFrameRoot, &m_pAnimController);SetupBoneMatrixPointers(m_pFrameRoot, m_pFrameRoot);return true;
}void CD3DXAnimation::SetAnimationByName(LPCTSTR name)
{LPD3DXANIMATIONSET pAnimationSet = NULL;m_pAnimController->GetAnimationSetByName(name, &pAnimationSet);m_pAnimController->SetTrackAnimationSet((UINT)1.0, pAnimationSet);
}void CD3DXAnimation::SetMatrix(LPD3DXMATRIX mtrix)
{UpdateFrameMatrices(m_pFrameRoot, mtrix);
}void CD3DXAnimation::UpdateAnimation(double timeDelay)
{m_pAnimController->AdvanceTime(timeDelay, NULL);
}void CD3DXAnimation::Render()
{DrawFrame(m_pDevice, m_pFrameRoot);
}

使用骨骼动画类:

1.创建骨骼动画:
<span style="white-space:pre">  </span>g_pAnimation1 = new CD3DXAnimation(g_pDevice);g_pAnimation1->Init("tiny.x");

2.逻辑更新&绘制(两步暂时放在了一起):

    g_pAnimation->SetMatrix(&matWorld1);g_pAnimation->UpdateAnimation(fElasedTime * 1);g_pAnimation->Render();

好了,这样就大功告成了。封装好了的骨骼动画类使用起来灰常方便,只要导入一个骨骼动画.X文件,就可以创建模型并让它动起来啦。

run一下,模型使用的还是DX官方给出的那个长得很丑的小人:


这样,这个小人就走起来啦,终于看到模型动起来了,好激动。。。

Direct-X学习笔记--骨骼动画相关推荐

  1. Direct 3D学习笔记(三)——光照与材质

    Direct 3D学习笔记(三)--光照与材质 现实世界中物体的颜色是一个复杂的系统,物体在不同的光照下,可能呈现在我们面前的就是不同的颜色.根据物理中的光学知识,物体在各种环境光照下,根据物体自身特 ...

  2. android学习笔记---55_frame动画的实现,Java技术qq交流群:JavaDream:251572072

    android学习笔记---55_frame动画的实现,Java技术qq交流群:JavaDream:251572072 Java技术qq交流群:JavaDream:251572072 2013/5/1 ...

  3. unity2D学习笔记-角色动画

    unity2D学习笔记-角色动画 角色移动 动画效果(重点!!!!!) 创建:Animator与Animation 状态转换 跳跃 从fall到idle Hierarchy中创建一个Sprite作为载 ...

  4. 学习笔记 JavaScript 动画

    学习笔记 JavaScript 动画 结果 代码里用到的图片 <html><head><style>#imageDiv {height: 100px;width: ...

  5. 学习笔记 JavaScript 动画 加速

    学习笔记 JavaScript 动画 加速 效果 代码中用到的图片 <html><head><style>#imageDiv {position: absolute ...

  6. Mr.J-- jQuery学习笔记(十七)--动画淡入淡出弹窗广告

    之前写过动画的隐藏与显示:Mr.J-- jQuery学习笔记(十四)--动画显示隐藏 动画隐藏与显示的一个小demo--对联广告:Mr.J-- jQuery学习笔记(十五)--实现页面的对联广告 展开 ...

  7. 【Unity 学习笔记】动画组件和动画控制器

    动画是一款游戏不可缺少的一部分.在Unity中,我们可以直接使用Unity自带的动画系统来制作我们想要的动画,其操作相当便捷. 帧动画是2D像素游戏的主流,我将以帧动画为例记录我最近学习动画系统的一些 ...

  8. react native学习笔记29——动画篇 Animated高级动画

    1.前言 上一节我们学习了全局的布局动画api--LayoutAnimation,体验到其流畅柔和的动画效果,但有时我们需要实现一些更精细化的动画,或者完成一些组合动画,这时我们可以使用React N ...

  9. 单目深度估计 | Learning Depth from Monocular Videos using Direct Methods 学习笔记

    文章目录 摘要 1. 论文主要贡献: 2. 从视频中学习预测深度 2.1 尺度模糊 2.2 建模姿态估计预测器 3. 可微分直接视觉测距法 3.1 直接视觉测距法(DVO) 3.2 可微分的实现 4 ...

最新文章

  1. Vue开发使用Axios遇到了大坑!
  2. 一位美国前辈工程师的十大职业发展忠告
  3. 宝贝,来,讲讲spring事务有哪些坑?
  4. 子图同构问题与Ullmann Algorithm 算法(一)
  5. System.PlatformNotSupportedException
  6. php 数组格式的字符串转为数组_php将字符串转换为数组实例讲解
  7. SAP Spartacus Unit List树形数据的加载
  8. Rust 让人奔溃的那些特性!
  9. python 异步api ThreadPoolExecutor 、ProcessPoolExecutor(多线程、多进程)
  10. 使用ASP.NET MVC构建HTML5离线web应用程序
  11. FAQ 工作薄及工作表
  12. 鸿蒙2.0手机交互体验,鸿蒙 2.0手机应用开发者Beta,来了!全新交互体验
  13. IDEA设置背景颜色
  14. React 可视化开发工具 shadow-widget 最佳实践(上)
  15. Vue开发环境搭建,Vue.js安装,浏览器辅助工具Vue-devtools
  16. 即将到来的Xcode8 都更新了什么?
  17. awb数据怎么计算_自动白平衡(AWB)算法
  18. 分享几个图床网址,便于大家分享图片
  19. 2021-11-03如何删除文件夹名中间有【空格】及后边带有“..”的文件夹
  20. Vue控制表格列的显示隐藏

热门文章

  1. java安装失败错误代码1603_Java安装未成功错误代码1603?
  2. python画大象_[python实现设计模式]-2.模板方法模式---把大象关进冰箱.
  3. 单量子比特的布洛赫球(Bloch Sphere)分析
  4. iphone手机里的计算机没有了怎么办,iPhone连到系统上的设备没有发挥作用怎么办...
  5. this和super关键字详解
  6. “星际穿越”观后感(宇宙只是界面,科技永远触摸不到世界的本原)
  7. 如何判断数组为null还是为空?
  8. java类加载器以及spi
  9. pg数据类型及数据类型转换
  10. Android studio相对布局