#include
#include
#include
#include
#include
#include
#include
#include "XFile.h"
#include
//这个不用多说,大家都应该明白,就是释放内存的
#define GXRELEASE(_p) do{if((_p)!= NULL){(_p)->Release();(_p) = NULL;}}while(0)
/*这个结构体用来储存X文件里的帧数据的,主要是用来存储帧的层次关系。
我曾在一本书上看过把'帧'和'动画集'以及'动画'数据放到一个Frame里的情况,这对初学者来说是很难理解的,所以我分开了,Frame只保存帧的层次结构以及一些必要数据,另外用专门的动画集结构体和动画结构体保存AnimationSet和Animation*/
struct Frame
{
char *Name; //帧名称。这是很重要的数据,应为我们从一帧的二叉树中查找指定的帧时是通过名
称的字符串的匹配来实现的。
Frame* pFrameSibling;
Frame* pFrameChild;
D3DXMATRIX matRot; //保存帧对应骨骼的插值矩阵
D3DXMATRIX matRotOrig; //原始矩阵。文件中每个Frame里面都会有一个矩阵,而且都是Frame里的第
一个元素,当我们的模型没有执行任何动作时我们往往用这些矩阵来让我们的模型处于一个默认的动作。
D3DXMATRIX matCombined; //混合矩阵。即我们求出每一段骨骼的插值矩阵之后,将其按父子关系累积
相乘得到的结果
Frame()
{
Name = NULL;
pFrameChild = pFrameSibling = NULL;
D3DXMatrixIdentity(&matCombined);
D3DXMatrixIdentity(&matRotOrig);
D3DXMatrixIdentity(&matRot);
}
~Frame ()
{
delete []Name;
delete pFrameSibling;
delete pFrameChild;
}
};
struct MeshContainer
{
char *Name;
D3DMATERIAL9 *pMaterials; //网格所用到的材质的指针(可能有多种材质)
DWORD NumMaterials;//材质数量,DrawSubet(i)时来用到的
LPDIRECT3DTEXTURE9 *pTextures;//纹理指针(X文件中只储存了纹理的文件名,而且是储存在材质结构体中的,所以要把它从材质结
构体的对象中读取出来,再利用这个文件名加载纹理
ID3DXMesh *pMesh; //利用矩阵将原始网格更新之后的网格,也就是要绘制的网格
ID3DXMesh *pSkinMesh; //储存从文件中读取出来的原始网格,这个网格不能改变,因为每次更新网格前
都要用它来更新
LPD3DXSKININFO pSkinInfo; //蒙皮信息,如果有的话(可以通过检测它是否为NULL来判定网格是否为蒙皮网格)
D3DXMATRIX** pBoneMatrix; //即指向Frame结构体中matCombined的指针,只是把这些矩阵的指针放到
了一个数组中以便于操作,而不用遍历二叉树才能找出这些 matCombined
D3DXMATRIX* pBoneOffsetMat;
DWORD* pAdjacency;
MeshContainer *pMeshContainerNext;
Frame* pFrame; //X文件中每个网格都是储存在Frame内部的,这就是那个对应的帧。
假设我们的蒙皮网格分成两部分,头部网格和其它部分的网格,那么我们画头部网格之前肯定要先设置头部网格在世界坐标系中的位置,即pd3dDevice->SetTransform(D3DTS_WORLD, &pmc->pFrame->matCombined),用对应帧的混合矩阵来确定网格在世界坐标系中的位置,朝向,和缩放等。
MeshContainer()
{
Name = NULL;
pMaterials = NULL;
pTextures = NULL;
NumMaterials = 0;
pMesh = NULL;
pSkinInfo = NULL;
pSkinMesh = NULL;
pBoneMatrix = NULL;
pBoneOffsetMat = NULL;
pAdjacency = NULL;
pMeshContainerNext = NULL;
pFrame = NULL;
}
~MeshContainer()
{
delete []pMaterials;
if (pTextures)
{
for(DWORD i = 0; i < NumMaterials; i++)
{
GXRELEASE(pTextures[i]);
}
delete []pTextures;
}
GXRELEASE(pMesh);
GXRELEASE(pSkinInfo);
GXRELEASE(pSkinMesh);
delete []pBoneMatrix;
delete []Name;
delete []pAdjacency;
delete []pMeshContainerNext;
delete pFrame;
}
};
//为方便读取文件中的动画键数据而创建的结构体
struct RotateKeyXFile
{
DWORD dwTime;
DWORD dwFloats;
float w;
float x;
float y;
float z;
};
struct D3DKeyXFile
{
DWORD dwTime;
DWORD dwFloats;
D3DXVECTOR3 vVec;
};
struct MatrixKeyXFile
{
DWORD dwTime;
DWORD dwFloats;
D3DXMATRIX mat;
};
//为储存从文件中读取出的动画键数据而创建的结构体
struct RotateKey
{
DWORD dwTime;
D3DXQUATERNION quatRotate;
};
struct PositionKey
{
DWORD dwTime;
D3DXVECTOR3 vPos;
};
struct ScaleKey
{
DWORD dwTime;
D3DXVECTOR3 vScale;
};
struct MatrixKey
{
DWORD dwTime;
D3DXMATRIX mat;
};
//骨骼的动画键(说白点就是旋转矩阵)就储存在这里面。每一个Animation都对应一个帧,即对应一个骨骼。Animation之所以能和帧对应起来是因为每个Animation里面都会有一个帧的引用。打开X文件,搜索Animation你会发现它里面有类似这样的一行字符“{ 帧名}”,这个帧名必定可以在前面的Frame中找到对应名称的帧。
struct Animation
{
char *Name;
Frame *pFrame;
Animation *pAnimationNext;
PositionKey *pPositionKeys;
UINT nPositionKeys;
RotateKey *pRotateKeys;
UINT nRotateKeys;
ScaleKey *pScaleKeys;
UINT nScaleKeys;
MatrixKey *pMatrixKeys;
UINT nMatrixKeys;
Animation()
{
Name = NULL;
pFrame = NULL;
pAnimationNext = NULL;
pPositionKeys = NULL;
nPositionKeys = 0;
pRotateKeys = NULL;
nRotateKeys = 0;
pScaleKeys = NULL;
nScaleKeys = 0;
pMatrixKeys = NULL;
nMatrixKeys = 0;
}
~Animation()
{
delete []Name;
delete pFrame;
delete pAnimationNext;
delete []pPositionKeys;
delete []pRotateKeys;
delete []pScaleKeys;
delete []pMatrixKeys;
}
};
//一个动画集就是一个动作。它里面储存了每根骨骼在执行这个动作时的动画键数据。
struct AnimationSet
{
char *Name;
DWORD NumAnimation;
Animation *pAnimation;
AnimationSet *pAnimationSetNext;
DWORD CurTime;
};
class SkinMesh
{
public:
SkinMesh(LPDIRECT3DDEVICE9 g_pd3dDevice);//主要作用是传递D3D设备进去。
~SkinMesh(void);
Frame* FindFrame(Frame* frame, char* Name);//从帧的二叉树中查找对应的帧。(比如根据Animation提供的帧引用的名称查找对应的帧,将帧里面的matCombined与网格里的pBoneMatrix一一映射时也要用到这个函数)
void AddFrame(Frame* parentFrame, Frame* pFrame);//每次从文件中读到一个帧时就要将它添加到帧的二叉树中。(要注意的是,文件中帧的层次不一定是二叉树的,所以要转化一下。将三叉树四叉树等结构转化成二叉树形式的。)
void AddMesh(Frame* frame, MeshContainer* pMeshContainer);//将网格连成一个链表。(绘制网格时只需要循环这个链表就可以了。我曾经看过一本书上的作法是把网格储存在Frame结构体中,结果每次要画网格时不得不递归的遍历二叉树才能把所有的网格都画出来)
HRESULT FindBones(void);//将网格的pBoneMatrix数组元素指向对应的帧里面的matCombined。
HRESULT FrameMove(void);//对每一段骨骼调用SetTime函数。
HRESULT DrawFrames(void);//即调用DrawMeshContainer函数画网格。画之前先用网格在文件中所处的那一帧的matCombined设置网格的世界矩阵。
void SetTime(Animation* pAnim, float fGlobalTime);//对对应的骨骼进行插值运算,求出插值矩阵。并会根据每个Animation里面对帧的引用关系,将求出的矩阵存放到帧结构体中去。
HRESULT DrawMeshContainer(MeshContainer* pmcMesh);//根据网格是否为蒙皮网格而做不同的处理。如果是蒙皮网格则将网格包含的pBoneMatrix与BoneOffsetMat相乘后去影响原始网格(pSkinMesh)得到最终网格(pMesh).
HRESULT Render();
HRESULT LoadFromXFile(TCHAR* pFileName);//加载X文件。因为文件最表层的数据只有Frame和AnimationSet,所以该函数根据数据类型(数据类型由type表示)的不同而调用不同的函数。
HRESULT LoadMesh(LPD3DXFILEDATA pFileData, Frame* pFrameParent);//该函数最重要的一句就是通过调用D3DXLoadSkinMeshFromXof()函数来加载蒙皮网格。
HRESULT UpdateFrames(Frame* pFrame, D3DXMATRIX& mat);//将帧里面的matRot累积相乘。(mat会传递给根结点,它将传递下去影响所有的骨骼。所以我们要想对模型整体进行平移,旋转,缩放等操作,只需要将对应矩阵传递给根结点就行了)。
HRESULT SetAnimationName(char* AnimateName);//根据字符串AnimateName在动画集链表中查找指定的动画集,并用一个全局变量pAnimSetCur保存起来。当你调用Render函数时,模型就会执行AnimateName所指定的动作。
HRESULT LoadFrame(LPD3DXFILEDATA pFileData, Frame* pFrameParent);
HRESULT LoadAnimationSet(LPD3DXFILEDATA pFileData);
HRESULT LoadAnimation(LPD3DXFILEDATA pFileData);
LPDIRECT3DDEVICE9 pd3dDevice;
AnimationSet* pAnimSetHead;
Frame* pFrameRoot;
MeshContainer* pMeshHead;
DWORD Options;
DWORD CurTime;
AnimationSet* pAnimSetCur;
};
SkinMesh.cpp
///////////////////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "./skinmesh.h"
SkinMesh::SkinMesh(LPDIRECT3DDEVICE9 g_pd3dDevice)
: pAnimSetHead(NULL)
, pFrameRoot(NULL)
, pMeshHead(NULL)
, Options(0)
, CurTime(0)
, pAnimSetCur(NULL)
{
pd3dDevice = g_pd3dDevice;
Options = 0;
pFrameRoot = new Frame();
pAnimSetHead = NULL;
pMeshHead = NULL;
CurTime = 0;
pAnimSetCur = pAnimSetHead;
}
SkinMesh::~SkinMesh(void)
{
}
//递归查找
Frame* SkinMesh::FindFrame(Frame* frame, char* Name)
{
Frame *pFrame;
if((frame->Name != NULL)&&strcmp(frame->Name, Name) == 0)
return frame;
if(frame->pFrameChild != NULL)
{
pFrame = FindFrame(frame->pFrameChild, Name);
if(pFrame != NULL)
return pFrame;
}
if(frame->pFrameSibling != NULL)
{
pFrame = FindFrame(frame->pFrameSibling, Name);
if(pFrame != NULL)
return pFrame;
}
return NULL;
}
void SkinMesh::AddFrame(Frame* parentFrame, Frame* pFrame)
{
if (parentFrame->pFrameChild == NULL)
{
parentFrame->pFrameChild = pFrame;
}else
{ //将多叉树结构转化成二叉树存储
Frame* frame = parentFrame->pFrameChild;
while (frame->pFrameSibling != NULL)
{
frame = frame->pFrameSibling;
}
frame->pFrameSibling = pFrame;
}
}
void SkinMesh::AddMesh(Frame* frame, MeshContainer* pMeshContainer)
{
pMeshContainer->pMeshContainerNext = pMeshHead;
pMeshHead = pMeshContainer;
pMeshContainer = NULL;
if(frame)
{
//将网格与它在文件中包含它的帧绑定,以便绘制前调用那一帧的matCombined设置世界矩阵,将网格画在正确的位置。
pMeshHead->pFrame = frame;
}
}
HRESULT SkinMesh::FindBones(void)
{
MeshContainer *pMeshContainer;
pMeshContainer = pMeshHead;
while(NULL != pMeshContainer)
{
if(pMeshContainer->pSkinInfo)
{
for( DWORD i = 0; i
{
Frame *frame = FindFrame(pFrameRoot, (char*)pMeshContainer->pSkinInfo->GetBoneName(i));
pMeshContainer->pBoneMatrix[i] = &(frame->matCombined);
}
}
pMeshContainer = pMeshContainer->pMeshContainerNext;
}
return E_NOTIMPL;
}
HRESULT SkinMesh::FrameMove(void)
{
CurTime = CurTime+100;
if(CurTime>1.0e15f)
CurTime = 0;
Animation *pAnimCur = pAnimSetCur->pAnimation;
while (pAnimCur != NULL)
{
SetTime(pAnimCur, CurTime);
pAnimCur = pAnimCur->pAnimationNext;
}
return E_NOTIMPL;
}
HRESULT SkinMesh::DrawFrames(void)
{
MeshContainer *pmc = pMeshHead;
while (pmc != NULL)
{
//注意下面这一句
pd3dDevice->SetTransform(D3DTS_WORLD, &pmc->pFrame->matCombined);
DrawMeshContainer(pmc);
pmc = pmc->pMeshContainerNext;
}
return E_NOTIMPL;
}
//计算每一段骨骼的插值矩阵。(根据矩阵类型不同代码略有不同。尤其要注意旋转矩阵的计算过程)
void SkinMesh::SetTime(Animation* pAnim, float fGlobalTime)
{
UINT iKey;
UINT dwp2;
UINT dwp3;
D3DXMATRIX matResult;
D3DXMATRIX matTemp;
float fTime1;
float fTime2;
float fLerpvalue;
D3DXVECTOR3 vScale;
D3DXVECTOR3 vPos;
D3DXQUATERNION quat;
float fTime;
if (pAnim->pMatrixKeys )
{
//将传递进来的时间模上最后一该的时间戳,得到fTime。随着fGlobalTime的不断增大,fTime会在0与最后时间戳中循环递增。所以模型的动作也是持续的。
fTime = (float)fmod(fGlobalTime, pAnim->pMatrixKeys[pAnim->nMatrixKeys-1].dwTime);
//找出离fTime最近的前后两个时间戳,利用这两个时间戳所处的动画键的矩阵值来计算插值矩阵的值。具体原理就是fTime与前后两个时间戳之间的比例关系等于所求插值矩阵与前后动画键的矩阵的比例。(只是当动画键类型为普通矩阵类型,即“dwKeyType == 4”时,我们不需要求插值,直接取离fTime最近的动画键的矩阵就行了。估计是当3DMAX导出X文件时,如果我们选择动画键类型为Matrix时3DMAX就为我们计算出了插值矩阵了。此外还要注意的是,用这种方法对旋转矩阵求插值其结果会不准确,所以要将数据转化为四元数再求值,求完了又转回矩阵去 )。
for (iKey = 0 ;iKey < pAnim->nMatrixKeys ; iKey++)
{
if ((float)pAnim->pMatrixKeys[iKey].dwTime > fTime)
{
dwp3 = iKey;
if (iKey > 0)
dwp2= iKey - 1;
else
dwp2 = iKey;
break;
}
}
fTime1 = (float)pAnim->pMatrixKeys[dwp2].dwTime;
fTime2 = (float)pAnim->pMatrixKeys[dwp3].dwTime;
if ((fTime2 - fTime1) ==0)
fLerpvalue = 0;
else
fLerpvalue = (fTime - fTime1) / (fTime2 - fTime1);
if (fLerpvalue > 0.5)
iKey = dwp3;
else
iKey = dwp2;
pAnim->pFrame->matRot = pAnim->pMatrixKeys[iKey].mat;
}
else
{
D3DXMatrixIdentity(&matResult);
if (pAnim->pScaleKeys)
{
dwp2 = dwp3 = 0;
fTime = (float)fmod(fGlobalTime, pAnim->pScaleKeys[pAnim->nScaleKeys-1].dwTime);
for (iKey = 0 ;iKey < pAnim->nScaleKeys ; iKey++)
{
if ((float)pAnim->pScaleKeys[iKey].dwTime > fTime)
{
dwp3 = iKey;
if (iKey > 0)
dwp2= iKey - 1;
else
dwp2 = iKey;
break;
}
}
fTime1 = (float)pAnim->pScaleKeys[dwp2].dwTime;
fTime2 = (float)pAnim->pScaleKeys[dwp3].dwTime;
if ((fTime2 - fTime1) ==0)
fLerpvalue = 0;
else
fLerpvalue = (fTime - fTime1) / (fTime2 - fTime1);
D3DXVec3Lerp(&vScale,
&pAnim->pScaleKeys[dwp2].vScale,
&pAnim->pScaleKeys[dwp3].vScale,
fLerpvalue);
D3DXMatrixScaling(&matTemp, vScale.x, vScale.y, vScale.z);
D3DXMatrixMultiply(&matResult, &matResult, &matTemp);
pAnim->pFrame->matRot = matResult;
}
else if (pAnim->pRotateKeys )
{
dwp2 = dwp3 = 0;
fTime = (float)fmod(fGlobalTime, pAnim->pRotateKeys[pAnim->nRotateKeys-1].dwTime);
for (iKey = 0 ;iKey < pAnim->nRotateKeys ; iKey++)
{
if ((float)pAnim->pRotateKeys[iKey].dwTime > fTime)
{
dwp3 = iKey;
if (iKey > 0)
dwp2= iKey - 1;
else
dwp2 = iKey;
break;
}
}
fTime1 = (float)pAnim->pRotateKeys[dwp2].dwTime;
fTime2 = (float)pAnim->pRotateKeys[dwp3].dwTime;
if ((fTime2 - fTime1) ==0)
fLerpvalue = 0;
else
fLerpvalue = (fTime - fTime1) / (fTime2 - fTime1);
D3DXQUATERNION q1,q2;
q1.x =-pAnim->pRotateKeys[dwp2].quatRotate.x;
q1.y =-pAnim->pRotateKeys[dwp2].quatRotate.y;
q1.z =-pAnim->pRotateKeys[dwp2].quatRotate.z;
q1.w =pAnim->pRotateKeys[dwp2].quatRotate.w;
q2.x =-pAnim->pRotateKeys[dwp3].quatRotate.x;
q2.y =-pAnim->pRotateKeys[dwp3].quatRotate.y;
q2.z =-pAnim->pRotateKeys[dwp3].quatRotate.z;
q2.w =pAnim->pRotateKeys[dwp3].quatRotate.w;
D3DXQuaternionSlerp(&quat,
&q1,
&q2,
fLerpvalue);
D3DXMatrixRotationQuaternion(&matTemp, &quat);
D3DXMatrixMultiply(&matResult, &matResult, &matTemp);
pAnim->pFrame->matRot = matResult;
}
else if (pAnim->pPositionKeys)
{
dwp2=dwp3=0;
fTime = (float)fmod(fGlobalTime, pAnim->pPositionKeys[pAnim->nRotateKeys-1].dwTime);
for (iKey = 0 ;iKey < pAnim->nPositionKeys ; iKey++)
{
if ((float)pAnim->pPositionKeys[iKey].dwTime > fTime)
{
dwp3 = iKey;
if (iKey > 0)
dwp2= iKey - 1;
else
dwp2 = iKey;
break;
}
}
fTime1 = (float)pAnim->pPositionKeys[dwp2].dwTime;
fTime2 = (float)pAnim->pPositionKeys[dwp3].dwTime;
if ((fTime2 - fTime1) ==0)
fLerpvalue = 0;
else
fLerpvalue = (fTime - fTime1)/ (fTime2 - fTime1);
D3DXVec3Lerp((D3DXVECTOR3*)&vPos,
&pAnim->pPositionKeys[dwp2].vPos, &pAnim->pPositionKeys[dwp3].vPos,
fLerpvalue);
D3DXMatrixTranslation(&matTemp, vPos.x, vPos.y, vPos.z);
D3DXMatrixMultiply(&matResult, &matResult, &matTemp);
pAnim->pFrame->matRot = matResult;
}
}
}
HRESULT SkinMesh::DrawMeshContainer(MeshContainer* pmcMesh)
{
UINT ipattr; PBYTE pbVerticesSrc;
PBYTE pbVerticesDest;
if( pmcMesh->pSkinInfo && pmcMesh->pSkinInfo->GetNumBones())
{
D3DCAPS9 caps;
pd3dDevice->GetDeviceCaps(&caps);
DWORD cBones = pmcMesh->pSkinInfo->GetNumBones();
D3DXMATRIX* BoneMatrices = NULL;
BoneMatrices = new D3DXMATRIX[cBones];
for(DWORD iBone = 0; iBone
D3DXMATRIX Indentity;
D3DXMatrixIdentity(&Indentity);
pd3dDevice->SetTransform(D3DTS_WORLD, &Indentity);
pmcMesh->pMesh->LockVertexBuffer(D3DLOCK_READONLY, (LPVOID*)&pbVerticesDest);
pmcMesh->pSkinMesh->LockVertexBuffer(0, (LPVOID*)&pbVerticesSrc);
//用BoneMatrices数组影响pSkinMesh之后储存在pMesh中。这就是我们最后努力的结果了
pmcMesh->pSkinInfo->UpdateSkinnedMesh(BoneMatrices, NULL, pbVerticesSrc, pbVerticesDest);
pmcMesh->pSkinMesh->UnlockVertexBuffer();
pmcMesh->pMesh->UnlockVertexBuffer();
//画网格,没什么好说的。
for(ipattr = 0; ipattr
{
pd3dDevice->SetMaterial(&(pmcMesh->pMaterials[ipattr]));
pd3dDevice->SetTexture(0, pmcMesh->pTextures[ipattr]);
pmcMesh->pMesh->DrawSubset(ipattr);
}
}else
{
for(ipattr = 0; ipattr
{
pd3dDevice->SetMaterial(&(pmcMesh->pMaterials[ipattr]));
pd3dDevice->SetTexture(0, pmcMesh->pTextures[ipattr]);
pmcMesh->pMesh->DrawSubset(ipattr);
}
}
return E_NOTIMPL;
}
HRESULT SkinMesh::Render()
{
D3DXMatrixIdentity(&pFrameRoot->matRot) ;
D3DXMATRIX mat;
D3DXMatrixIdentity(&mat);
FrameMove();
//你也可以传递一个世界矩阵代替mat以改变模型
UpdateFrames(pFrameRoot, mat);
DrawFrames();
return E_NOTIMPL;
}
HRESULT SkinMesh::LoadFromXFile(TCHAR* pFileName)
{
HRESULT hr = S_OK;
LPD3DXFILE pFile = NULL;
LPD3DXFILEENUMOBJECT pFileEnumObject = NULL;
LPD3DXFILEDATA pFileData = NULL;
GUID type;
hr = D3DXFileCreate(&pFile);
if (FAILED(hr))
return hr;
hr = pFile->RegisterTemplates((LPVOID)D3DRM_XTEMPLATES ,D3DRM_XTEMPLATE_BYTES);
if(FAILED(hr))
return hr;
hr = pFile->CreateEnumObject((LPVOID)pFileName, DXFILELOAD_FROMFILE , &pFileEnumObject);
//从前面到这里一直照抄就行,按规矩办事
if(FAILED(hr))
return hr;
DWORD Size;
pFileEnumObject->GetChildren(&Size);//pFileEnumObject里面有多少个数据,并将这个数量储存在Size中。
for(DWORD i = 0 ; i < Size ; i++)
{
pFileEnumObject->GetChild(i, &pFileData);//获取对应序号的数据对象,获取的对象储存在pFileData中。
pFileData->GetType(&type);//获取pFileData的类型
if (type == TID_D3DRMFrame)
{
LoadFrame(pFileData, pFrameRoot);
}
if (type == TID_D3DRMAnimationSet)
{
LoadAnimationSet(pFileData);
}
GXRELEASE(pFileData);
}
FindBones();
pFrameRoot->matRotOrig = pFrameRoot->matRot;
GXRELEASE(pFileEnumObject);
GXRELEASE(pFile);
return E_NOTIMPL;
}
HRESULT SkinMesh::LoadMesh(LPD3DXFILEDATA pFileData, Frame* pFrameParent)
{
HRESULT hr = S_OK;
MeshContainer *pmcMesh = NULL;
LPD3DXBUFFER pMaterialsbuf = NULL;
LPD3DXBUFFER pAdjacencybuf = NULL;
DWORD NumName;
UINT cFaces;
UINT iMaterial;
pmcMesh = new MeshContainer();
pFileData->GetName(NULL, &NumName);
if(NumName>0)
{
pmcMesh->Name = new char[NumName];
pFileData->GetName(pmcMesh->Name, &NumName);
}else
pmcMesh->Name = "NoNameMesh";
D3DXLoadSkinMeshFromXof(pFileData, Options,
pd3dDevice,&pAdjacencybuf, &pMaterialsbuf,
NULL,&pmcMesh->NumMaterials,&pmcMesh->pSkinInfo, &pmcMesh- >pSkinMesh);
pmcMesh->pSkinMesh->CloneMeshFVF(Options, pmcMesh->pSkinMesh->GetFVF(),pd3dDevice, &pmcMesh->pMesh);
DWORD NumBones = pmcMesh->pSkinInfo->GetNumBones();
if(NumBones>0)
{
pmcMesh->pBoneMatrix = new D3DXMATRIX*[NumBones];
pmcMesh->pBoneOffsetMat = new D3DXMATRIX[NumBones];
for(DWORD i = 0; i < NumBones; i++)
pmcMesh->pBoneOffsetMat[i] = (*pmcMesh->pSkinInfo->GetBoneOffsetMatrix(i));
LPDWORD pAdjacency = static_cast
cFaces = pmcMesh->pSkinMesh->GetNumFaces();
pmcMesh->pAdjacency = new DWORD[cFaces*3];
memcpy(pmcMesh->pAdjacency,pAdjacency,cFaces*3*sizeof(DWORD));
}else
{
pmcMesh->pSkinInfo = NULL;
}
if(pmcMesh->NumMaterials>0)
{
pmcMesh->pMaterials = new D3DMATERIAL9[pmcMesh->NumMaterials];
pmcMesh->pTextures = new LPDIRECT3DTEXTURE9[pmcMesh->NumMaterials];
LPD3DXMATERIAL pMaterials = (LPD3DXMATERIAL)pMaterialsbuf->GetBufferPointer();
for (iMaterial = 0; iMaterial
{
pmcMesh->pMaterials[iMaterial] = pMaterials[iMaterial].MatD3D;
pmcMesh->pTextures[iMaterial] = NULL;
if(pMaterials[iMaterial].pTextureFilename != NULL)
D3DXCreateTextureFromFile(pd3dDevice, pMaterials[iMaterial].pTextureFilename, &(pmcMesh->pTextures[iMaterial]));
}
}else
{
pmcMesh->pMaterials = new D3DMATERIAL9[1];
pmcMesh->pTextures = new LPDIRECT3DTEXTURE9[1];
memset(pmcMesh->pMaterials, 0, sizeof(D3DXMATERIAL));
pmcMesh->pMaterials[0].Diffuse.r = 0.5f;
pmcMesh->pMaterials[0].Diffuse.g = 0.5f;
pmcMesh->pMaterials[0].Diffuse.b = 0.5f;
pmcMesh->pMaterials[0].Specular = pmcMesh->pMaterials[0].Diffuse;
pmcMesh->pTextures[0] = NULL;
}
AddMesh(pFrameParent, pmcMesh);
pmcMesh = NULL; delete pmcMesh;
GXRELEASE(pAdjacencybuf);
GXRELEASE(pMaterialsbuf);
return E_NOTIMPL;
}
//将Frame里面的matRot累积相乘
HRESULT SkinMesh::UpdateFrames(Frame* pFrame, D3DXMATRIX& mat)
{
D3DXMatrixMultiply(&pFrame->matCombined, &pFrame->matRot, &mat);
Frame *pframeChild = pFrame->pFrameChild;
while(pframeChild != NULL)
{
UpdateFrames(pframeChild, pFrame->matCombined);
pframeChild = pframeChild->pFrameSibling;
}
return E_NOTIMPL;
}
HRESULT SkinMesh::SetAnimationName(char* AnimateName)
{
pAnimSetCur = pAnimSetHead;
while(pAnimSetCur && strcmp(pAnimSetCur->Name, AnimateName))
{
pAnimSetCur = pAnimSetCur->pAnimationSetNext;
}
return E_NOTIMPL;
}
HRESULT SkinMesh::LoadFrame(LPD3DXFILEDATA pFileData, Frame* pFrameParent)
{
LPD3DXFILEDATA pFileDataChild = NULL;
DWORD NumName = 0;
GUID type;
pFileData->GetType(&type);
if(type == TID_D3DRMMesh)//获取帧里面的网格(并非所有的帧都会包含网格,一般来说如果模型只有一个网格的话它就会包含在第一帧里面)
{
LoadMesh(pFileData, pFrameParent);
}
else if (type == TID_D3DRMFrameTransformMatrix)//获取初始矩阵
{
if(pFrameParent != NULL)
{
D3DXMATRIX *pMat;
DWORD Size;
pFileData->Lock(&Size, (LPCVOID*)&pMat);
pFrameParent->matRot = *pMat;
pFrameParent->matRotOrig = *pMat;
pFileData->Unlock();
}
}else if (type == TID_D3DRMFrame)//因为帧有包含关系(正是这种包含关系体现了父子关系),所以要在该函数里面再次判断数据类型(如果是LoadAnimationSet()则不需要再次判断数据类型,而是直接创建动画集的结构体,并将其添加到链表中等操作。而LoadFrames则需要在函数内再次判断数据类型以便执行正确的操作。注意是再次判断数据类型,请想明白这一点。)
{
Frame* pFrame = new Frame();
pFileData->GetName(NULL, &NumName);
if(NumName > 0)
{
pFrame->Name = new char[NumName];
pFileData->GetName(pFrame->Name, &NumName);
}else
pFrame->Name = "NoName";
AddFrame(pFrameParent, pFrame);
DWORD Size;
pFileData->GetChildren(&Size);
for (DWORD i = 0 ; i < Size ; i++)
{
pFileData->GetChild(i, &pFileDataChild);
LoadFrame(pFileDataChild, pFrame);
GXRELEASE(pFileDataChild);
}
}
return E_NOTIMPL;
}
HRESULT SkinMesh::LoadAnimationSet(LPD3DXFILEDATA pFileData)
{
LPD3DXFILEDATA pFileDataChild = NULL;
GUID type;
DWORD NumName;
AnimationSet* pAnimSetCur = new AnimationSet();
pFileData->GetName(NULL, &NumName); if(NumName>0)
{
pAnimSetCur->Name = new char[NumName];
pFileData->GetName(pAnimSetCur->Name, &NumName);
} else
pAnimSetCur->Name = "NoName";
pAnimSetCur->pAnimationSetNext = pAnimSetHead;
pAnimSetHead = pAnimSetCur;
DWORD Size;
pFileData->GetChildren(&Size);
for (DWORD i = 0 ; i < Size ; i++)
{
pFileData->GetChild(i , &pFileDataChild);
pFileDataChild->GetType(&type);
if (type == TID_D3DRMAnimation)
{
LoadAnimation(pFileDataChild);
}
}
return E_NOTIMPL;
}
HRESULT SkinMesh::LoadAnimation(LPD3DXFILEDATA pFileData)
{
GUID type;
RotateKeyXFile *pFileRotateKey;
D3DKeyXFile *pFileScaleKey;
D3DKeyXFile *pFilePosKey;
MatrixKeyXFile *pFileMatrixKey;
DWORD NumName;
PBYTE pData;
DWORD dwKeyType;
DWORD cKeys;
DWORD iKey;
Animation* pAnimCur = new Animation();
pFileData->GetName(NULL, &NumName);
if (NumName>0)
{
pAnimCur->Name = new char[NumName];
pFileData->GetName(pAnimCur->Name, &NumName);
}else
pAnimCur->Name = "NoName";
pAnimCur->pAnimationNext = pAnimSetHead->pAnimation;
pAnimSetHead->pAnimation = pAnimCur;
LPD3DXFILEDATA pFileDataChild = NULL;
DWORD Size;
pFileData->GetChildren(&Size);
for (DWORD i = 0 ; i < Size ; i++)
{
pFileData->GetChild(i , &pFileDataChild);
//如果动画里面的数据类型是引用,那么它必定是帧的引用。至于这个引用做什么用,上面已经说得非常清楚了。
if(pFileDataChild->IsReference())
{
pFileDataChild->GetType(&type);
if (type == TID_D3DRMFrame)
{
DWORD Size;
pFileDataChild->GetName(NULL, &Size);
char* Name = (char*)_alloca(Size);
pFileDataChild->GetName(Name, &Size);
获取帧的名字后就去二叉树中查找,并把找到的帧的地址保存在动画结构体中。
pAnimCur->pFrame = FindFrame(pFrameRoot, Name);
}
}
if(!pFileDataChild->IsReference())
{
pFileDataChild->GetType(&type);
if (TID_D3DRMAnimationKey == type)
{
Animation *pAnimCur = pAnimSetHead->pAnimation;
DWORD Size;
pFileDataChild->Lock(&Size, (LPCVOID*)&pData);
dwKeyType = ((DWORD*)pData)[0];
cKeys = ((DWORD*)pData)[1];
if(dwKeyType == 0)
{
pAnimCur->pRotateKeys = new RotateKey[cKeys];
pAnimCur->nRotateKeys = cKeys;
pFileRotateKey = (RotateKeyXFile*)(pData+(sizeof(DWORD)*2));
for(iKey = 0; iKey < cKeys; iKey++)
{
pAnimCur->pRotateKeys[iKey].dwTime = pFileRotateKey->dwTime;
pAnimCur->pRotateKeys[iKey].quatRotate.x = pFileRotateKey->x;
pAnimCur->pRotateKeys[iKey].quatRotate.y = pFileRotateKey->y;
pAnimCur->pRotateKeys[iKey].quatRotate.z = pFileRotateKey->z;
pAnimCur->pRotateKeys[iKey].quatRotate.w = pFileRotateKey->w;
pFileRotateKey += 1;
}
}
else if (dwKeyType == 1)
{
pAnimCur->pScaleKeys = new ScaleKey[cKeys];
pAnimCur->nScaleKeys = cKeys;
pFileScaleKey = (D3DKeyXFile*)(pData + (sizeof(DWORD)*2));
for(iKey = 0; iKey
pAnimCur->pScaleKeys[iKey].dwTime = pFileScaleKey->dwTime;
pAnimCur->pScaleKeys[iKey].vScale = pFileScaleKey->vVec;
pFileScaleKey += 1;
}
}
else if (dwKeyType == 2)
{
pAnimCur->pPositionKeys = new PositionKey[cKeys];
pAnimCur->nPositionKeys = cKeys;
pFilePosKey = (D3DKeyXFile*)(pData + (sizeof(DWORD)*2));
for (iKey = 0; iKey
pAnimCur->pPositionKeys[iKey].dwTime = pFilePosKey->dwTime;
pAnimCur->pPositionKeys[iKey].vPos = pFilePosKey->vVec;
pFilePosKey += 1;
}
}
else if(dwKeyType == 4)
{
pAnimCur->pMatrixKeys = new MatrixKey[cKeys];
pAnimCur->nMatrixKeys = cKeys;
pFileMatrixKey = (MatrixKeyXFile*)(pData + (sizeof(DWORD)*2));
for(iKey = 0; iKey < cKeys; iKey++)
{
pAnimCur->pMatrixKeys[iKey].dwTime = pFileMatrixKey->dwTime;
pAnimCur->pMatrixKeys[iKey].mat = pFileMatrixKey->mat;
pFileMatrixKey += 1;
}
}
}
}
GXRELEASE(pFileDataChild);
}
GXRELEASE(pFileData);
return E_NOTIMPL;
}
由于本人写代码时没有养成写注释的好习惯,所以如果你对这个类实在看不懂请尽管与我联系,我一定抽时间为你解答!
添加一句,头文件中添加的"XFile.h"是从DirectX高级动画制作中拿来用的.因为作者说直接导入rmxfguid.h和rmxftmpl.h容易出问题,所以稍微处理了一下.我现在把这两个文件也贴出来
XFile.h
////////////////////////////////////////////////////////////////////////////
#include "rmxfguid.h"
extern unsigned char D3DRM_XTEMPLATES[];
#define D3DRM_XTEMPLATE_BYTES 3278
XFile.cpp
////////////////////////////////////////////////////////////////////////////
#include "rmxftmpl.h"
我先声明一点,这个类是用于学习之用,通用性很差的,对于很多骨骼动画的X文件都不支持。(读取出来之后有问题。要么有网格没纹理,要么有网格有纹理却不动,要么就是有网格有纹理也动,却是一个骨骼在动不是网格动。总之总有一些问题,好像是3DMAX导出X文件时的一些参数设置,但具体应该怎么设置我也搞不清。但这个类读取DirectX9自带的tiny.x时是不会有问题的,这个你放心,所以如果你真的想用这个类的话请看懂后自己根据情况修改。)
类成员函数使用流程示例abc
SkinMesh *pSkin;
pSkin = new SkinMesh(g_pd3dDevice);
pSkin->LoadFromXFile(tiny.x);
pSkin->SetAnimationName(Anim_1);//忘记tiny文件中的那个动作叫什么了,暂且用这个名字凑数吧。
pSkin->Render();