📄 skinmesh.cpp
字号:
// SkinMesh.cpp: 实现 CSkinMesh 类
//
//////////////////////////////////////////////////////////////////////
#include "SkinMesh.h"
// 构造函数
CSkinMesh::CSkinMesh(LPDIRECT3DDEVICE9 pD3DDevice)
:m_vMax(0,0,0),
m_vMin(0,0,0),
m_pID3dDevice(pD3DDevice),
m_fElapsedTime(0.0f),
m_bMoving(TRUE),
m_pIAnimController(NULL),
m_pFrameRoot(NULL),
m_SkinningMethod(D3DINDEXED),
m_pBoneMatrices(NULL),
m_NumBoneMatricesMax(0)
{
m_pID3dDevice->GetDeviceCaps( &m_d3dCaps );
}
// 析构函数
CSkinMesh::~CSkinMesh()
{
CAllocateHierarchy *pAlloc = new CAllocateHierarchy( this ); // 自定义数据容器
D3DXFrameDestroy( m_pFrameRoot, pAlloc ); // 释放所有框架
SAFE_DELETE( pAlloc ); // 释放自定义数据容器
SAFE_RELEASE( m_pIAnimController ); // 释放动画控制器
}
// 加载X文件
HRESULT CSkinMesh::LoadFromXFile( char *strFileName )
{
// 数据容器,该类的构造函数需要一个CShinMesh*类型的参数,所以传递this进去
HRESULT hr;
CAllocateHierarchy *pAlloc = new CAllocateHierarchy( this );
// 从.X文件加载层次框架和动画数据:自定义数据容器、根框架指针、动画控制器
// 函数调用后,m_pFrameRoot就已经保存了X文件的框架层次
hr = D3DXLoadMeshHierarchyFromX(
strFileName,
D3DXMESH_MANAGED,
m_pID3dDevice,
pAlloc, // ID3DXAllocateHierarchy接口(在这里提供了一个实现该接口的类对象指针)
NULL,
&m_pFrameRoot,
&m_pIAnimController ); // ID3DXAnimationController接口(在这里提供了一个实现该接口的类对象指针)
SAFE_DELETE( pAlloc ); // 释放对象
if (FAILED(hr))
return hr;
// 递归,设置每块骨骼的矩阵
hr = SetupBoneMatrixPointers( m_pFrameRoot );
if (FAILED(hr))
return hr;
// 计算包围球,m_vObjectCenter球的中心,m_fObjectRadius球的半径
//hr = D3DXFrameCalculateBoundingSphere( m_pFrameRoot, &m_vObjectCenter, &m_fObjectRadius );
//if (FAILED(hr))
//return hr;
if(m_pFrameRoot) // 创建包围盒,m_vMin最小点,m_vMax最大点
CalculateBondingBox(m_pFrameRoot, &m_vMin, &m_vMax );
// 以下代码没有用到 ??
//LPD3DXANIMATIONSET pAnim;
//m_pIAnimController->GetAnimationSet( 0, &pAnim );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: GenerateSkinnedMesh()
// Desc: 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 CSkinMesh::GenerateSkinnedMesh( D3DXMESHCONTAINER_EX *pMeshContainer )
{
HRESULT hr = S_OK;
if (pMeshContainer->pSkinInfo == NULL)
return hr;
SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
// 蒙皮模式
switch(m_SkinningMethod)
{
case D3DNONINDEXED: // 如果选择的模式是non-indexed(无索引),使用ConvertToBlendedMesh()函数生成可绘制模型
{
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 > m_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(),
m_pID3dDevice, &pMeshTmp);
if (FAILED(hr))
{
goto e_Exit;
}
pMeshContainer->MeshData.pMesh->Release();
pMeshContainer->MeshData.pMesh = pMeshTmp;
pMeshTmp = NULL;
}
}
break;
// if indexed skinning mode selected, use ConvertToIndexedsBlendedMesh to generate drawable mesh
// 如果选择了索引模式,使用ConvertToIndexedsBlendedMesh()函数生成可绘制模型
case D3DINDEXED:
{
DWORD NumMaxFaceInfl;
DWORD Flags = D3DXMESHOPT_VERTEXCACHE;
LPDIRECT3DINDEXBUFFER9 pIB;
hr = pMeshContainer->pOrigMesh->GetIndexBuffer(&pIB);
if (FAILED(hr))
goto e_Exit;
hr = pMeshContainer->pSkinInfo->GetMaxFaceInfluences(pIB, pMeshContainer->pOrigMesh->GetNumFaces(), &NumMaxFaceInfl);
pIB->Release();
if (FAILED(hr))
goto e_Exit;
// 12 entry palette guarantees that any triangle (4 independent influences per vertex of a tri)
// can be handled
NumMaxFaceInfl = min(NumMaxFaceInfl, 12);
if (m_d3dCaps.MaxVertexBlendMatrixIndex + 1 < NumMaxFaceInfl)
{
// HW does not support indexed vertex blending. Use SW instead
pMeshContainer->NumPaletteEntries = min(256, pMeshContainer->pSkinInfo->GetNumBones());
pMeshContainer->UseSoftwareVP = true;
Flags |= D3DXMESH_SYSTEMMEM;
}
else
{
// using hardware - determine palette size from caps and number of bones
// If normals are present in the vertex data that needs to be blended for lighting, then
// the number of matrices is half the number specified by MaxVertexBlendMatrixIndex.
pMeshContainer->NumPaletteEntries = min( ( m_d3dCaps.MaxVertexBlendMatrixIndex + 1 ) / 2,
pMeshContainer->pSkinInfo->GetNumBones() );
pMeshContainer->UseSoftwareVP = false;
Flags |= D3DXMESH_MANAGED;
}
hr = pMeshContainer->pSkinInfo->ConvertToIndexedBlendedMesh
(
pMeshContainer->pOrigMesh,
Flags,
pMeshContainer->NumPaletteEntries,
pMeshContainer->pAdjacency,
NULL, NULL, NULL,
&pMeshContainer->NumInfl,
&pMeshContainer->NumAttributeGroups,
&pMeshContainer->pBoneCombinationBuf,
&pMeshContainer->MeshData.pMesh);
if (FAILED(hr))
goto e_Exit;
}
break;
// if software skinning selected, use GenerateSkinnedMesh to create a mesh that can be used with UpdateSkinnedMesh
case SOFTWARE:
{
hr = pMeshContainer->pOrigMesh->CloneMeshFVF(D3DXMESH_MANAGED, pMeshContainer->pOrigMesh->GetFVF(),
m_pID3dDevice, &pMeshContainer->MeshData.pMesh);
if (FAILED(hr))
goto e_Exit;
hr = pMeshContainer->MeshData.pMesh->GetAttributeTable(NULL, &pMeshContainer->NumAttributeGroups);
if (FAILED(hr))
goto e_Exit;
delete[] pMeshContainer->pAttributeTable;
pMeshContainer->pAttributeTable = new D3DXATTRIBUTERANGE[pMeshContainer->NumAttributeGroups];
if (pMeshContainer->pAttributeTable == NULL)
{
hr = E_OUTOFMEMORY;
goto e_Exit;
}
hr = pMeshContainer->MeshData.pMesh->GetAttributeTable(pMeshContainer->pAttributeTable, NULL);
if (FAILED(hr))
goto e_Exit;
// allocate a buffer for bone matrices, but only if another mesh has not allocated one of the same size or larger
if (m_NumBoneMatricesMax < pMeshContainer->pSkinInfo->GetNumBones())
{
m_NumBoneMatricesMax = pMeshContainer->pSkinInfo->GetNumBones();
// Allocate space for blend matrices
delete []m_pBoneMatrices;
m_pBoneMatrices = new D3DXMATRIXA16[m_NumBoneMatricesMax];
if (m_pBoneMatrices == NULL)
{
hr = E_OUTOFMEMORY;
goto e_Exit;
}
}
}
break;
default: // invalid m_SkinningMethod value
{
// return failure due to invalid skinning method value
hr = E_INVALIDARG;
goto e_Exit;
}
} // switch()
e_Exit:
return hr;
}
HRESULT CSkinMesh::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;
}
// 设置指向网格模型的骨骼矩阵
HRESULT CSkinMesh::SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainerBase )
{
UINT i, iNumBone;
D3DXFRAME_EX *pFrame;
D3DXMESHCONTAINER_EX *pMeshContainer = (D3DXMESHCONTAINER_EX*)pMeshContainerBase;
//蒙皮动画,则设置骨骼的变换矩阵
if (pMeshContainer->pSkinInfo != NULL) // 如果有蒙皮信息
{
iNumBone = pMeshContainer->pSkinInfo->GetNumBones(); // 取得骨骼总数量
pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[iNumBone]; // 创建骨骼矩阵数组,ppBoneMatrixPtrs指向骨骼矩阵数组
if (pMeshContainer->ppBoneMatrixPtrs == NULL)
return E_OUTOFMEMORY;
for (i=0; i < iNumBone; i++)
{
// 返回指定名字的框架,骨骼名字与框架名字是一一对应的
pFrame = (D3DXFRAME_EX*)D3DXFrameFind( m_pFrameRoot, pMeshContainer->pSkinInfo->GetBoneName(i) );
if (pFrame == NULL)
return E_FAIL;
//初始化矩阵
pMeshContainer->ppBoneMatrixPtrs[i] = &(pFrame->CombinedTransformationMatrix); // 保存每块骨骼的组合变换矩阵
}
}
return S_OK;
}
VOID CSkinMesh::Render( float fElapsedAppTime, D3DXVECTOR3 vPos, float angle, float scale )
{
// 取得程序时间,单位秒,如果时间没变化就停止渲染
// FLOAT fElapsedAppTime = DXUtil_Timer( TIMER_GETELAPSEDTIME );
if( 0.00f == fElapsedAppTime )
return;
m_fElapsedTime = fElapsedAppTime;
// 设置世界矩阵
D3DXMATRIXA16 matWorld, matWorld2;
D3DXMatrixRotationY( &matWorld2, angle );
D3DXMatrixScaling( &matWorld, scale, scale, scale );
D3DXMatrixMultiply( &matWorld, &matWorld, &matWorld2); // 绽放 * 旋转
D3DXMatrixTranslation( &matWorld2, vPos.x, vPos.y, vPos.z ); // 平移
D3DXMatrixMultiply(&matWorld,&matWorld,&matWorld2); // 平移 * 绽放 * 旋转
if(m_pIAnimController)
{
#if ((D3D_SDK_VERSION & 0xFF)==31) // Directx9.0b
if (m_bMoving)
m_pIAnimController->SetTime( m_pIAnimController->GetTime()+m_fElapsedTime );
else
m_pIAnimController->SetTime( 0 );
#else // Directx9.0c
if (m_bMoving)
m_pIAnimController->AdvanceTime( m_fElapsedTime, NULL );
else
m_pIAnimController->ResetTime();
#endif
}
UpdateFrameMatrices( m_pFrameRoot, &matWorld ); // 递归更新全部框架的矩阵
DrawFrame( m_pFrameRoot ); // 递归绘制全部框架
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -