网格模型
一:定义
二:子集和属性缓存
2.0:子集
2.1:属性
2.2:操作
三:邻接信息
四:属性表
五:优化
六:网格的创建与绘制
6.1:创建
6.2:绘制
网格模型是一种将物体的顶点数据,纹理,材质等信息存储在一个外部文件中的3D物体模型。网格模型由一个或多个子集subset组成,其中每个子集都具有一组相同材质、纹理和绘制状态等属性的三角形集合。
一个mesh由一个或数个子集组成,一个子集是在mesh中的使用相同属性渲染的一组三角形,这里的属性是指材质,纹理和渲染状态。如下图组成.
(1)网格属性是指物体面具有相同材质,纹理和渲染状态的性质。
属性的级别的是面,比喻A面和B面的属性相同。
(2)Direct3D属性缓存:属性缓存是一个DWORD类型的数组。用于保存每个三角形面的的属性ID。所以属性缓存中的元数个数与网格中的三角形个数相同。如下图,
Attribute Buffer:
Attribute--------- Begin
Triangle lives in subset 0: 0
Triangle lives in subset 1: 0
Triangle lives in subset 2: 0
Triangle lives in subset 3: 0
Triangle lives in subset 4: 1
Triangle lives in subset 5: 1
Triangle lives in subset 6: 1
Triangle lives in subset 7: 1
Triangle lives in subset 8: 2
Triangle lives in subset 9: 2
Triangle lives in subset 10: 2
Triangle lives in subset 11: 2
Attribute--------- End
写出了12个面,每个面的属性ID。
(1)设置属性缓存中的属性
设置属性缓存中的属性,每个面具有自己的属性之后,就等于进行了子集分类。
当需要访问网格模型中的属性缓存时,通过调用LockAttributeBuffer锁定属性缓存,该方法保存一个缓存的首地址在参数二中。通过该地址读取或设置属性缓存中的数据。当该问结束之后,就需要UnlockAttributeBuffer解锁。
设置12个面的属性。
DWORD* pAttrib = NULL;
MpCubeMesh->LockAttributeBuffer(0, &pAttrib);
for(int a = 0; a < 4; a++) pAttrib[a] = 0;
for(int b = 4; b < 8; b++) pAttrib[b] = 1;
for(int c = 8; c < 12; c++) pAttrib[c] = 2;
mpCubeMesh->UnlockAttributeBuffer();
(2)读取属性缓存
输出所有面的属性。
DWORD *pAtrribuf = NULL;
mpCubeMesh->LockAttributeBuffer(D3DLOCK_READONLY, & pAtrribuf);
for (int i = 0; i < mFaceNums; i++)
{
outfile << "Triangle lives in subset " << i << ": ";
outfile << pAtrribuf[i] << std::endl;
}
mpCubeMesh->UnlockAttributeBuffer();
网格的邻接信息用于指定网格中任意一个三角形与其它三角形的邻接情况,由于每个三角形只有3条边,所以每个三角形也最多和3个三角形相邻。
在ID3DXMesh接口提供了一个GenerateAdjacency方法用于生存网格的邻接信息。并将网格的邻接信息保存到一个DWORD数组中,
邻接数组是一个DWORD数组,其中的每一项对应了Mesh中的一个三角形。
mFaceNums*3:由于每个三角形最多和3个三角形相邻,所以该邻接数组必须为网格中的三角形面数的3倍。
邻接信息将会被用到网格优化,且作为里面的参数.
生存网格的邻接信息,并输出
DWORD *AdjacencyInfo = new DWORD[mFaceNums*3];
mpCubeMesh->GenerateAdjacency(0.001f, AdjacencyInfo);
for(int i = 0; i < mFaceNums; i++)
{
outfile << "Triangle's adjacent to triangle " << i << ": ";
outfile << AdjacencyInfo[i * 3 ] << " ";
outfile << AdjacencyInfo[i * 3 + 1] << " ";
outfile << AdjacencyInfo[i * 3 + 2] << std::endl;
}
输出:
Adjacency Buffer:
AdjacencyBuffer--------- Begin
Triangle's adjacent to triangle 0: 1 4 9
Triangle's adjacent to triangle 1: 7 10 0
Triangle's adjacent to triangle 2: 3 5 11
Triangle's adjacent to triangle 3: 6 8 2
Triangle's adjacent to triangle 4: 5 0 10
Triangle's adjacent to triangle 5: 2 8 4
Triangle's adjacent to triangle 6: 7 3 11
Triangle's adjacent to triangle 7: 1 9 6
Triangle's adjacent to triangle 8: 9 5 3
Triangle's adjacent to triangle 9: 7 0 8
Triangle's adjacent to triangle 10: 11 4 1
Triangle's adjacent to triangle 11: 6 2 10
AdjacencyBuffer--------- End
每个面的邻接面索引。第0个面相邻接面的索引是第1,4,9号面。
属性表是一个D3DXATTRIBUTERANGE结构的一个数组。属性表对应的是网格子集。
D3DXATTRIBUTERANGE
{
DWORD AttribId; // 网格子集ID
DWORD FaceStart; //子集面的面的起始值。
DWORD FaceCount; //在子集中的面(三角形)数
DWORD VertexStart; //该子集的起始顶点在顶点缓存中的序号
DWORD VertexCount; //在子集中的顶点数
}
GetAttributeTable(D3DXATTRIBUTERANGE tab, DWORD TableSize);
这个函数可以生成 tablesize属性表的属性数。 属性表结构数组tab。
//获取属性表的属性数
DWORD numSubsets = 0;
mpCubeMesh->GetAttributeTable(0, &numSubsets);
//利用属性数创新Talbe
D3DXATTRIBUTERANGE *table = new D3DXATTRIBUTERANGE[numSubsets];
mpCubeMesh->GetAttributeTable(table, &numSubsets);
输出数组成员
Cout << Table[0] << endl;
Subset ID: 0
Face Start: 0
Face Count: 4
Vertex Start: 0
Vertex Count: 8
不论是使用手工还是使用3D建模软件所创建的网格模型,其中都可能包含一些无效的顶点和索引,并且网格中属性缓存中的子集顺序也并不一定是有序的,因为,通常在创建或加载网格模型后,需要对网格进行优化以便重组网格中的顶点和索引。
ID3DXMesh接口提供了OptimizeInplace 和Optimze方对网格进行优化。
他们的不同之处在于,前者是在原网格对象上进行优化,而后者则返回一个优化后的新网格对象。但原网格对象不会发生改变。
优化需要前面所创建的属性表,
pOldAdjacency 优化前的网格邻接信息表
pNewAdjacency 优化后的网格邻接信息表
//优化网格数据
std::vector<DWORD> pOldAdjacency(mpCubeMesh->GetNumFaces() * 3);
std::vector<DWORD> pNewAdjacency(mpCubeMesh->GetNumFaces() * 3);
mpCubeMesh->GenerateAdjacency(0.0001f, &pOldAdjacency[0]);
mpCubeMesh->OptimizeInplace(
D3DXMESHOPT_ATTRSORT|D3DXMESHOPT_COMPACT|D3DXMESHOPT_VERTEXCACHE,
&pOldAdjacency[0],
&pNewAdjacency[0],
0,0);
通过D3DXCreateMeshFVF 函数来创建一个空的mesh,所谓空mesh是指我们己经指定了顶点数,面数。而只需要向顶点缓存,索引缓存,属性缓存,提供顶点,索引,属性数据就可以了。
HRESULT hr = D3DXCreateMeshFVF(mFaceNums, mVertexNums, D3DXMESH_MANAGED, MeshVertex::FVF, apD9Device, &mpCubeMesh ); 注:即这里的mpCubeMesh还是个空的mesh
D3DXCreateMeshFV(创建空的mesh)-LockVertexBuffer(填充顶点数据)-LockIndexBuffer(设置索引数据)-LockAttributeBuffer(设置属性数据,用作子集分类)-OptimizeInplace(优化)-最后绘制
通过调用DrawSubset(i)来进行绘制子集图形
for(vector<LPDIRECT3DTEXTURE9*>::size_type i = 0; i != mpTextures.size(); i++)
{
apD9Device->SetTexture(0, mpTextures[i]);
mpCubeMesh->DrawSubset(i);
}