|
楼主 |
发表于 2009-4-12 10:23:53
|
显示全部楼层
10.6. 邻接信息对于Mesh的某些操作,如优化,需要知道三角形间的邻接信息,而Mesh的邻接数组就存储这样的信息。 邻接数组是DWORD类型数组,其中的每一项对应Mesh中的一个三角形。例如,邻接数组的第I项对应的三角形有以下三个索引值定义: A=I*3 B=I*3 + 1 C=I*3 +2 这里,使用ULONG_MAX=4294967295表示该边没有邻接三角形。其实,这个数就是-1。 由于每个三角形有三条边,所以,他有三个邻接三角形。
因此,每个三角形可能有三个邻接三角形,邻接数组必须有(ID3DXMesh::GetNumFaces() * 3)个元素。 在D3DX中,有很多函数可以输出Mesh的邻接信息,如: HRESULT ID3DXMesh::GenerateAdjacency( FLOAT fEpsilon, DWORD* pAdjacency );
l fEpsilon –指示当两点距离有多近时,可以认为是一个点。当两点间的距离小于epsilon时,可认为他们是同一个点 l pAdjacency –用于存储邻接信息的邻接数组 例如: DWORD adjacencyInfo[Mesh->GetNumFaces() * 3]; Mesh->GenerateAdjacency(0.001f, adjacencyInfo);
10.7. 克隆有时,需要将Mesh的数据另外复制一份,可以使用ID3DXMesh::CloneMeshFVF方法: HRESULT ID3DXMesh::CloneMeshFVF( DWORD Options, DWORD FVF, LPDIRECT3DDEVICE9 pDevice, LPD3DXMESH *ppCloneMesh );
l Options –创建Mesh的标志。完整信息可参考SDK文档。下面的几个很常用: n D3DXMESH_32BIT –使用32位顶点索引 n D3DXMESH_MANAGED –Mesh数据将被放在受控的内存缓冲池中 n D3DXMESH_WRITEONLY –Mesh数据只能执行写操作,不能执行读操作 n D3DXMESH_DYNAMIC –Mesh缓冲将是动态的 l FVF –创建新Mesh的灵活定点格式 l pDevice –与克隆Mesh相关联的D3D设备 l ppCloneMesh –输出新Mesh 这个方法允许指定与原Mesh不同的Options和FVF。例如,现在有一个定点格式为D3DXFVF_XYZ的Mesh,我们想复制一个顶点格式为D3DXFVF_XYZ|D3DXFVF_NORMAL的Mesh,可以这样做: // assume _mesh and device are valid ID3DXMesh* clone = 0; Mesh->CloneMeshFVF( Mesh->GetOptions(), // use same options as source mesh D3DFVF_XYZ | D3DFVF_NORMAL,// specify clones FVF Device, &clone);
10.8. 创建Mesh(D3DXCreateMeshFVF)我们可以使用D3DXCreateMeshFVF函数创建一个空的Mesh对象。所谓空,是指我们已经指定了顶点数和面数(也就是三角形数),函数D3DXCreateMeshFVF也分配了适当大小的内存给顶点、定点索引、属性缓冲区。有了这些缓冲区后,就可以手动填写上下文数据了(需要分别向定点缓冲区、索引缓冲区、属性缓冲区提供定点、索引、属性数据)。 HRESULT WINAPI D3DXCreateMeshFVF( DWORD NumFaces, DWORD NumVertices, DWORD Options, DWORD FVF, LPDIRECT3DDEVICE9 pD3DDevice, LPD3DXMESH *ppMesh );
l NumFaces –Mesh中的三角形数,必须指定一个大于0的数 l NumVertices –定点数,也必须是一个大于0的数 l Options –创建Mesh的选项标志,常用的标志如下: n D3DXMESH_32BIT –Mesh使用32位顶点索引 n D3DXMESH_MANAGED –使用受控内存池 n D3DXMESH_WRITEONLY –Mesh的数据只能被写,不能被读 n D3DXMESH_DYNAMIC –使用动态缓冲 l FVF –Mesh的顶点格式 l pD3DDevice –与Mesh相关联的D3D设备 l ppMesh –输出的Mesh指针 在下一节,将举例说明该函数的用法,到时将手动填充Mesh对象的数据。 另外,还可以使用D3DXCreateMesh函数创建空的Mesh对象。其原形如下: HRESULT WINAPI D3DXCreateMesh( DWORD NumFaces, DWORD NumVertices, DWORD Options, const LPD3DVERTEXELEMENT9 *pDeclaration, LPDIRECT3DDEVICE9 pD3DDevice, LPD3DXMESH *ppMesh );
其中的参数的含义就不需要再解释了,他们与D3DXCreateMeshFVF的参数相似。但是,第四个参数是D3DVERTEXELEMENT9的数组,而不是先前的FVF。 10.9. 应用举例:创建并渲染Mesh 我们手动创建一个立方体,它将使用本章讨论的大部分知识点: l 创建一个空的Mesh l 向Mesh中填写立方体的几何信息 l 划分子集 l 生成Mesh的邻接信息 l 优化 l 渲染 这里,给出代码的框架,这个例子使用D3DXCreateMeshFVF函数创建Mesh对象。 在代码中,定义如下的全局变量: ID3DXMesh* Mesh = 0; const DWORD NumSubsets = 3; IDirect3DTexture9* Textures[3] = {0, 0, 0};// texture for each subset
第一个是Mesh对象的指针;Mesh对象中有三个子集。每个子集用不同的纹理渲染,数组Textures中存储每个子集的纹理。 主要的代码在Setup函数中,他首先创建一个空的Mesh对象: bool Setup() { HRESULT hr = 0; hr = D3DXCreateMeshFVF( 12, 24, D3DXMESH_MANAGED, Vertex::FVF, Device, &Mesh);
这里,创建的Mesh具有12个面(三角形),24个顶点,使用他们来描述立方体。 现在,Mesh对象还是空的,需要为其填充适当的顶点/索引数据。 // Fill in vertices of a box Vertex* v = 0; Mesh->LockVertexBuffer(0, (void**)&v); // fill in the front face vertex data v[0] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); v[1] = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); . . . v[22] = Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f); v[23] = Vertex( 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f); Mesh->UnlockVertexBuffer();
// Define the triangles of the box WORD* i = 0; Mesh->LockIndexBuffer(0, (void**)&i); // fill in the front face index data i[0] = 0; i[1] = 1; i[2] = 2; i[3] = 0; i[4] = 2; i[5] = 3; . . . // fill in the right face index data i[30] = 20; i[31] = 21; i[32] = 22; i[33] = 20; i[34] = 22; i[35] = 23; Mesh->UnlockIndexBuffer();
立方体的几何信息输入完毕后,还需要为各个三角形划分子集。可以通过修改Mesh对象的属性缓冲区,指定每个三角形所属的子集。在这个例子中,由索引缓冲定义的12个三角形中,前四个属于子集0,再下面四个属于子集1,最后四个属于子集2。 DWORD* attributeBuffer = 0; Mesh->LockAttributeBuffer(0, &attributeBuffer); for(int a = 0; a < 4; a++) // triangles 1-4 attributeBuffer[a] = 0; // subset 0 for(int b = 4; b < 8; b++) // triangles 5-8 attributeBuffer = 1; // subset 1 for(int c = 8; c < 12; c++) // triangles 9-12 attributeBuffer[c] = 2; // subset 2 Mesh->UnlockAttributeBuffer();
到这里,这个Mesh对象已经创建完毕了,可以渲染了。但是,它还没有经过优化。当然,对于这个微不足道的立方体,优化得不到任何好处,但是,我们还是用它演示ID3DXMesh接口方法的用法。在优化之前,需要计算Mesh对象的邻接信息: std::vector<DWORD> adjacencyBuffer(Mesh->GetNumFaces() * 3); Mesh->GenerateAdjacency(0.0f, &adjacencyBuffer[0]);
然后,执行优化: hr = Mesh->OptimizeInplace( D3DXMESHOPT_ATTRSORT|D3DXMESHOPT_COMPACT|D3DXMESHOPT_VERTEXCACHE, &adjacencyBuffer[0], 0, 0, 0);
到这里,创建Mesh对象的工作已经完成了。最后,如果一切顺利,函数Setup应该返回true: return true; } // end Setup()
上面说过,经过优化,Mesh对象将建立属性表。使用下面的代码验证一下。 // number of entries in the attribute table DWORD numEntries = 0; mesh->GetAttributeTable(0, &numEntries); vector<D3DXATTRIBUTERANGE> table(numEntries); mesh->GetAttributeTable(&table[0], &numEntries); for(int i = 0; i < numEntries; i++) { cout << "Entry " << i << endl; cout << "------" << endl; cout << "Subset ID: " << table.AttribId << endl; cout << "Face Start: " << table.FaceStart << endl; cout << "Face Count: " << table.FaceCount << endl; cout << "Vertex Start: " << table.VertexStart << endl; cout << "Vertex Count: " << table.VertexCount << endl; cout <<endl; }
下面是部分输出信息: Entry 0 ------------ Subset ID: 0 Face Start: 0 Face Count: 4 Vertex Start: 0 Vertex Count: 8 Entry 1 ------------ Subset ID: 1 Face Start: 4 Face Count: 4 Vertex Start: 8 Vertex Count: 8 Entry 2 ------------ Subset ID: 2 Face Start: 8 Face Count: 4 Vertex Start: 16 Vertex Count: 8
与前面的设计相符。最后,渲染Mesh对象: Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,0x00000000, 1.0f, 0); Device->BeginScene(); for(int i = 0; i < NumSubsets; i++) { Device->SetTexture( 0, Textures ); Mesh->DrawSubset( i ); } Device->EndScene(); Device->resent(0, 0, 0, 0);
10.10. 总结 l Mesh对象包含顶点、索引、属性缓冲区。顶点和索引缓冲区包含Mesh对象的几何信息。属性缓冲区的每一项对应一个Mesh对象的一个三角形,指示该三角形所属的子集。 l Mesh对象可以使用OptimizeInplace和Optimize方法优化。为了更加有效的渲染,优化将重组Mesh的几何信息。使用D3DXMESHOPT_ATTRSORT标志进行优化,会产生属性表,据此,访问一次属性表,就可以完整的渲染一个子集。 l Mesh对象的邻接信息是一个DWORD数组,每个三角形有三个邻接项,代表与该三角形相邻接的三角形。 l 可以使用D3DXCreateMeshFVF函数创建空的Mesh对象。然后使用相应的Lock*方法填写适当的数据组成Mesh的几何信息和属性信息。
|
|