蒙皮骨骼动画网格模型接口是对上一节骨骼动画网格模型接口的扩展,添加了处理蒙皮信息的功能。
进一步扩展结构体D3DXMESHCONTAINER
为了在网格模型中包含蒙皮信息,需要进一步扩展D3DXMESHCONTAINER_DERIVEED,其定义如下:
struct D3DXMESHCONTAINER_DERIVED : public D3DXMESHCONTAINER
{
IDirect3DTexture9** ppTextures;
ID3DXMesh* pOrgMesh;
DWORD MaxBonesInflPerVertex;
DWORD NumAttrGroups;
ID3DXBuffer* pBoneCombBuffer;
D3DXMATRIX** ppBoneMatrices;
D3DXMATRIX** ppBoneOffsetMatrices;
DWORD NumMatrixPalettes;
bool UseSoftwareVP;
};
当加载原网格模型并由此生成一个蒙皮网格时,会用D3DXMESHCONTAINER::MeshData::pMesh存储所生成的蒙皮网格模型,这时需要将初始网格模型保存下来,这就是pOrgMesh的作用。变量MaxBonesInflPerVertex表示每个顶点最多受多少骨骼的影响,指针变量pBoneCombBuffer指向骨骼结合表,骨骼结合表中的数据按属性组结构体D3DXBONECOMBINATION组织起来,该结构体定义如下:
Describes a subset of the mesh that has the same
attribute and bone combination.
typedef struct D3DXBONECOMBINATION {
DWORD AttribId;
DWORD FaceStart;
DWORD FaceCount;
DWORD VertexStart;
DWORD VertexCount;
DWORD * BoneId;
} D3DXBONECOMBINATION, *LPD3DXBONECOMBINATION;
Members
- AttribId
- Attribute table identifier.
- FaceStart
- Starting face.
- FaceCount
- Face count.
- VertexStart
- Starting vertex.
- VertexCount
- Vertex count.
- BoneId
Pointer to an array of values that identify each of
the bones that can be drawn in a single drawing call. Note that the array
can be of variable length to accommodate variable length bone combinations
of ID3DXSkinInfo::ConvertToIndexedBlendedMesh.
- The size of the array varies based on the type of
mesh generated. A non-indexed mesh array size is equal to the number of
weights per vertex (pMaxVertexInfl in ID3DXSkinInfo::ConvertToBlendedMesh).
An indexed mesh array size is equal to the number of bone matrix palette
entries (paletteSize in ID3DXSkinInfo::ConvertToIndexedBlendedMesh).
Remarks
The subset of the mesh described by
D3DXBONECOMBINATION can be rendered in a single drawing call.
结构体D3DXBONECOMBINATION用来描述网格中具有同样属性的部分,也就是网格模型的一个子集,这个网格模型子集也称为属性组。属性组实际上是用来标识网格模型中被指定的骨骼矩阵所影响的子网格,不同属性组所标识的子网格需要用不同的纹理、材质进行渲染,该子网格可以通过调用函数DrawIndexedPrimitive()或者DrawSubset()进行绘制。
成员变量BoneId指向一个数组,该数组表示的是在单独的一次绘制中,即一次DrawSubset()函数调用中所用到的全部骨骼矩阵,该数组的大小与将要生成的蒙皮网格类型有关,在索引顶点混合蒙皮网格中,它的大小等于函数ConvertToIndexedBlendedMesh()中的输入参数paletteSize,也就是结构体D3DXMESHCONTAINER_DERIVEED的成员变量NumMatrixPalettes。变量NumMatrixPalettes表示进行索引顶点混合时所需要的矩阵调色板的容量,它的数值需要根据硬件设备能力进行相应的设置。
cAllocateHierarchy类的设计实现
蒙皮骨骼动画网格模型接口中cAllocateHierarchy类和骨骼动画网格模型接口中的cAllocateHierarchy类基本相同,区别较大的是CreateMeshContainer()函数中增加了对蒙皮信息的处理:
// generate skin mesh
if(skin_info != NULL)
{
new_mesh_container->pSkinInfo = skin_info;
skin_info->AddRef();
new_mesh_container->pOrgMesh = mesh_ptr;
mesh_ptr->AddRef();
UINT num_bones = skin_info->GetNumBones();
new_mesh_container->ppBoneOffsetMatrices = new D3DXMATRIX*[num_bones];
if(new_mesh_container->ppBoneOffsetMatrices == NULL)
{
DestroyMeshContainer(new_mesh_container);
return E_OUTOFMEMORY;
}
for(UINT i = 0; i < num_bones; i++)
new_mesh_container->ppBoneOffsetMatrices[i] = new_mesh_container->pSkinInfo->GetBoneOffsetMatrix(i);
hr = GenerateSkinnedMesh(new_mesh_container);
if(FAILED(hr))
{
DestroyMeshContainer(new_mesh_container);
return hr;
}
}
CreateMeshContainer()函数中处理蒙皮信息的关键是调用自定义函数GenerateSkinnedMesh()来生成蒙皮网格模型,其定义如下:
HRESULT cAllocateHierarchy::GenerateSkinnedMesh(D3DXMESHCONTAINER_DERIVED* mesh_container)
{
ID3DXSkinInfo* skin_info = mesh_container->pSkinInfo;
if(skin_info == NULL)
return S_OK;
release_com(mesh_container->MeshData.pMesh);
release_com(mesh_container->pBoneCombBuffer);
HRESULT hr;
IDirect3DIndexBuffer9* index_buffer;
if(FAILED(hr = mesh_container->pOrgMesh->GetIndexBuffer(&index_buffer)))
return hr;
DWORD max_faces_infl_per_triangle;
hr = skin_info->GetMaxFaceInfluences(index_buffer, mesh_container->pOrgMesh->GetNumFaces(),
&max_faces_infl_per_triangle);
index_buffer->Release();
if(FAILED(hr))
return hr;
max_faces_infl_per_triangle = min(max_faces_infl_per_triangle, 12);
IDirect3DDevice9* device = DXUTGetD3DDevice();
D3DCAPS9 caps;
device->GetDeviceCaps(&caps);
if((caps.MaxVertexBlendMatrixIndex+1)/2 < max_faces_infl_per_triangle)
{
// use software vertex processing
mesh_container->NumMatrixPalettes = min(256, skin_info->GetNumBones());
mesh_container->UseSoftwareVP = true;
}
else
{
// use hardware verterx processing
mesh_container->NumMatrixPalettes = min((caps.MaxVertexBlendMatrixIndex+1)/2, skin_info->GetNumBones());
mesh_container->UseSoftwareVP = false;
}
hr = skin_info->ConvertToIndexedBlendedMesh(mesh_container->pOrgMesh, 0, mesh_container->NumMatrixPalettes,
mesh_container->pAdjacency, NULL, NULL, NULL, &mesh_container->MaxBonesInflPerVertex,
&mesh_container->NumAttrGroups, &mesh_container->pBoneCombBuffer, &mesh_container->MeshData.pMesh);
return hr;
}
函数GenerateSkinnedMesh()判断当前网格容器是否包含蒙皮信息,如果当前网格模型中不包含蒙皮信息,则直接退出该函数。接下来确定所需要的矩阵调色板的容量,最后调用函数ConvertToIndexedBlendedMesh()根据初始网格模型提供的相应参数生成索引蒙皮网格模型。函数ConvertToIndexedBlendedMesh()的声明如下:
Takes a mesh and returns a new mesh with per-vertex blend
weights, indices, and a bone combination table. The table describes which bone
palettes affect which subsets of the mesh.
HRESULT ConvertToIndexedBlendedMesh(
LPD3DXMESH pMesh,
DWORD Options,
DWORD paletteSize,
CONST DWORD * pAdjacencyIn,
LPDWORD pAdjacencyOut,
DWORD * pFaceRemap,
LPD3DXBUFFER * ppVertexRemap,
DWORD * pMaxVertexInfl,
DWORD * pNumBoneCombinations,
LPD3DXBUFFER * ppBoneCombinationTable,
LPD3DXMESH * ppMesh
);
Parameters
- pMesh
- [in] The input mesh.
- Options
- [in] Currently unused.
- paletteSize
- [in] Number of bone matrices available for matrix
palette skinning.
- pAdjacencyIn
- [in] Input mesh adjacency information.
- pAdjacencyOut
- [in] Output mesh adjacency information.
- pFaceRemap
- [out] An array of DWORDs, one per face, that
identifies the original mesh face that corresponds to each face in the
blended mesh. If the value supplied for this argument is NULL, face remap
data is not returned.
- ppVertexRemap
- [out] Address of a pointer to an ID3DXBuffer
interface, which contains a DWORD for each vertex that specifies how the new
vertices map to the old vertices. This remap is useful if you need to alter
external data based on the new vertex mapping. This parameter is optional;
NULL may be used.
- pMaxVertexInfl
- [out] Pointer to a DWORD that will contain the
maximum number of bone influences required per vertex for this skinning
method.
- pNumBoneCombinations
- [out] Pointer to the number of bones in the bone
combination table.
- ppBoneCombinationTable
- [out] Pointer to the bone combination table. The
data is organized in a D3DXBONECOMBINATION structure.
- ppMesh
- [out] Pointer to the new mesh.
Return Values
If the method succeeds, the return value is D3D_OK. If
the method fails, the return value can be D3DERR_INVALIDCALL.
Remarks
Each element in the remap arrays specifies the previous
index for that position. For example, if a vertex was in position 3 but has been
remapped to position 5, then the fifth element of pVertexRemap will contain 3.
This method does not run on hardware that does not
support fixed-function vertex blending.