天行健 君子当自强而不息

网格模型高级技术(17)

蒙皮骨骼动画网格模型接口是对上一节骨骼动画网格模型接口的扩展,添加了处理蒙皮信息的功能。

 

进一步扩展结构体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.


posted on 2008-06-13 12:39 lovedday 阅读(3277) 评论(2)  编辑 收藏 引用

评论

# re: 网格模型高级技术(17) 2009-05-30 15:55 拜读者

你写的内容不错,为什么不写书出版呢?  回复  更多评论   

# re: 网格模型高级技术(17)[未登录] 2010-09-14 15:31 1

这些就是书上的内容!  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论