骨骼蒙皮动画
.x文件结构
1.模型结构: 包含顶点、纹理、面的索引,每个骨骼中各顶点受影响数
2.骨骼结构: 包含各骨骼层级关系和描述此继承关系的偏移量
3.动画数据: 包含多个动画的数据
template XSkinMeshHeader {
<3cf169ce-ff7c-44ab-93c0-f78f62d172e2>
WORD nMaxSkinWeightsPerVertex; //影响顶点的最大骨骼数(矩阵数)
WORD nMaxSkinWeightsPerFace; //影响面的最大骨骼数(矩阵数)
WORD nBones; //骨骼数量
}
template SkinWeights {
<6f0d123b-bad2-4167-a0d0-80224f25fabb>
STRING transformNodeName; //骨骼的字符串
DWORD nWeights; //有多少顶点受此骨骼影响
array DWORD vertexIndices[nWeights]; //受此骨骼影响的顶点
array FLOAT weights[nWeights]; //受此骨骼影响的顶点的权值
Matrix4x4 matrixOffset; //模型空间相对骨骼坐标空间的偏移
}
AnimationSet {
Animation {
AnimationKey {
4; //关键帧的类型(0,1,2,3表示 旋转,缩放,位置,矩阵)
2; //有多少个动画关键帧,然后下面是时间,数据个数,及具体数据
0;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;,
4960;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;;
}
{ Scene_Root }
}
Animation {
AnimationKey {
4;
2;
0;16;1.278853,0.000000,-0.000000,0.000000,0.000000,0.000000,1.123165,0.000000,0.000000,-1.470235,0.000000,0.000000,0.135977,2.027985,133.967667,1.000000;;,
4960;16;1.278853,0.000000,-0.000000,0.000000,0.000000,0.000000,1.123165,0.000000,0.000000,-1.470235,0.000000,0.000000,0.135977,2.027985,133.967667,1.000000;;;
}
{ body }
}
回到D3D
1.描述骨骼层级关系的数据结构,即与Frame结构对应
typedef struct _D3DXFRAME
{
LPSTR Name; //描述此节点的名称
D3DXMATRIX TransformationMatrix;//对应.x文件FrameTransformMatrix
LPD3DXMESHCONTAINER pMeshContainer; //此节点的网格模型结构
struct _D3DXFRAME *pFrameSibling; //兄弟节点
struct _D3DXFRAME *pFrameFirstChild; //父亲节点
} D3DXFRAME, *LPD3DXFRAME;
2.记录模型顶点信息结构,与.x文件中的Mesh,Material,Skin等信息对应
typedef struct _D3DXMESHCONTAINER
{
LPSTR Name; //描述此节点的名称
D3DXMESHDATA MeshData; //节点模型的网格数据
LPD3DXMATERIAL pMaterials; //材质信息
LPD3DXEFFECTINSTANCE pEffects; //实例效果信息
DWORD NumMaterials; //材质数
DWORD *pAdjacency; //邻接缓冲区指针
LPD3DXSKININFO pSkinInfo; //骨骼皮肤信息
struct _D3DXMESHCONTAINER *pNextMeshContainer;
} D3DXMESHCONTAINER, *LPD3DXMESHCONTAINER;
3.网格数据(上面的_D3DXMESHCONTAINER中有用到)
typedef struct _D3DXMESHDATA
{
D3DXMESHDATATYPE Type; //Mesh的类型
// current mesh data interface
union
{
LPD3DXMESH pMesh; //正常的Mesh
LPD3DXPMESH pPMesh; //渐进的Mesh (可以实现LOD)
LPD3DXPATCHMESH pPatchMesh; //面片式的Mesh (这种Mesh暂时没有用过)
};
} D3DXMESHDATA, *LPD3DXMESHDATA;
代码结构
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 );
CAllocateHierarchy()
{
}
};
D3DXLoadMeshHierarchyFromX 的内部的回调依赖于接口ID3DXAllocateHierarchy的几个函数实现,D3DXLoadMeshHierarchyFromX 其中一个参数就是CAllocateHierarchy 实例化的指针
D3DXLoadMeshHierarchyFromX() //此函数内部基本过程
{
CreateFrame() //创建骨骼的回调函数
CreateMeshContainer() //创建模型的回调函数
{
GenerateSkinnedMesh() //生成蒙皮信息
{
ConvertToIndexedBlendedMesh() //转换顶点格式,将模型转换成显卡可以渲染的模型
}
}
}
OnCreateDevice()
{
D3DXLoadMeshHierarchyFromX() //加载模型和骨骼的继承结构
SetupBoneMatrxPointers() //设置骨骼矩阵
{
SetupBoneMatrixPointersOnMesh()
}
D3DXFrameCalculateBoundingSphere() //计算骨骼的范围
}
Update()
{
UpdateFrameMatrices() //更新每个骨骼
}
OnFrameRender()
{
DrawFrame()
{
DrawMeshContainer() //渲染模型顶点
DrawFrame() //递归渲染
}
}
具体实现
首先,需要扩展D3DXFRAME结构和D3DXMESHCONTAINER
struct D3DXFRAME_DERIVED : public D3DXFRAME
{
D3DXMATRIXA16 CombinedTransformationMatrix;
};
(子骨骼)CombinedTransformationMatrix = (子骨骼)TransformationMatrix * (父骨骼)CombinedTransformationMatrix
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
};
类的头文件及部分函数的实现
class CAnimation
{
private:
CAllocateHierarchy *m_pAlloc;
LPDIRECT3DDEVICE9 m_pDevice;
LPD3DXFRAME m_pFrameRoot;
public:
bool m_bPlayAnim;
LPD3DXANIMATIONCONTROLLER m_pAnimController;
private:
HRESULT LoadFromXFile(LPCSTR strFileName);
void UpdateFrameMatrices(LPD3DXFRAME pFrameBase,LPD3DXMATRIX pParentMatrix);
void DrawFrame(LPD3DXFRAME pFrame);
void DrawMeshContainer(LPD3DXMESHCONTAINER pMeshContainerBase,LPD3DXFRAME pFrameBase);
public:
HRESULT OnCreate(LPDIRECT3DDEVICE9 pDevice,WCHAR *strFileName);
HRESULT Render(D3DXMATRIXA16 matWorld,float fElapsed);
HRESULT OnDestroy();
CAnimation();
virtual ~CAnimation();
};
void CAnimation::UpdateFrameMatrices( LPD3DXFRAME pFrameBase,LPD3DXMATRIX pParentMatrix )
{
D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED*)pFrameBase;
if (pParentMatrix)
{
D3DXMatrixMultiply(&pFrame->CombinedTransfromationMatrix,&pFrame->TransformationMatrix,pParentMatrix);
}
else
pFrame->CombinedTransfromationMatrix = pFrame->TransformationMatrix;
if (pFrame->pFrameSibling)
{
UpdateFrameMatrices(pFrame->pFrameSibling,pParentMatrix);
}
if (pFrame->pFrameFirstChild)
{
UpdateFrameMatrices(pFrame->pFrameFirstChild,&pFrame->CombinedTransfromationMatrix);
}
}
void CAnimation::DrawFrame( LPD3DXFRAME pFrame )
{
LPD3DXMESHCONTAINER pMeshContainer = pFrame->pMeshContainer;
while (pMeshContainer)
{
DrawMeshContainer(pMeshContainer,pFrame);
pMeshContainer = pMeshContainer->pNextMeshContainer;
}
if (pFrame->pFrameSibling)
{
DrawFrame(pFrame->pFrameSibling);
}
if (pFrame->pFrameFirstChild)
{
DrawFrame(pFrame->pFrameFirstChild);
}
}
void CAnimation::DrawMeshContainer( LPD3DXMESHCONTAINER pMeshContainerBase,LPD3DXFRAME pFrameBase )
{
D3DXMESHCONTAINER_DERIVED *pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pMeshContainerBase;
D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED*)pFrameBase;
UINT iMaterial = 0;
UINT iAtrib = 0;
LPD3DXBONECOMBINATION pBoneComb;
UINT iMatrixIndex;
UINT iPaletteEntry;
D3DXMATRIXA16 matTemp;
if (pMeshContainer->pSkinInfo != NULL)
{
HRESULT hr;
if (pMeshContainer->UseSoftwareVP)
m_pDevice->SetSoftwareVertexProcessing(TRUE);
if (pMeshContainer->NumInfl)
m_pDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE,TRUE);
if (pMeshContainer->NumInfl == 1)
m_pDevice->SetRenderState(D3DRS_VERTEXBLEND,D3DVBF_0WEIGHTS);
else if (pMeshContainer->NumInfl == 2)
m_pDevice->SetRenderState(D3DRS_VERTEXBLEND,D3DVBF_1WEIGHTS);
else if (pMeshContainer->NumInfl == 3)
m_pDevice->SetRenderState(D3DRS_VERTEXBLEND,D3DVBF_1WEIGHTS);
else if (pMeshContainer->NumInfl == 4)
m_pDevice->SetRenderState(D3DRS_VERTEXBLEND,D3DVBF_1WEIGHTS);
pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>(pMeshContainer->pBoneCombinationBuf->GetBufferPointer());
for (iAtrib = 0; iAtrib < pMeshContainer->NumAttributeGroups; iAtrib++)
{
//设置混合矩阵
for (iPaletteEntry = 0; iPaletteEntry < pMeshContainer->NumPaletteEntries;iPaletteEntry++)
{
iMatrixIndex = pBoneComb[iAtrib].BoneId[iPaletteEntry];
if (iMatrixIndex != UINT_MAX)
{
D3DXMatrixMultiply(&matTemp,&pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex]);
m_pDevice->SetTransform(D3DTS_WORLDMATRIX(iPaletteEntry),&matTemp);
}
}
//设置材质和纹理
m_pDevice->SetMaterial(&pMeshContainer->pMaterials[pBoneComb[iAtrib].AttribId].MatD3D);
m_pDevice->SetTexture(0,pMeshContainer->ppTextures[pBoneComb[iAtrib].AttribId]);
pMeshContainer->MeshData.pMesh->DrawSubset(iAtrib);
}
//恢复顶点混合状态 m_pDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE,FALSE);
m_pDevice->SetRenderState(D3DRS_VERTEXBLEND,D3DVBF_DISABLE);
//恢复顶点处理模式
if (pMeshContainer->UseSoftwareVP)
m_pDevice->SetSoftwareVertexProcessing(FALSE);
}
else
{
m_pDevice->SetTransform(D3DTS_WORLD,&pFrame->CombinedTransfromationMatrix);
for (iMaterial = 0; iMaterial < pMeshContainer->NumMaterials;iMaterial++)
{
m_pDevice->SetMaterial(&pMeshContainer->pMaterials[iMaterial].MatD3D);
m_pDevice->SetTexture(0,pMeshContainer->ppTextures[iMaterial]);
pMeshContainer->MeshData.pMesh->DrawSubset(iMaterial);
}
}
}