chap 10 网格(前篇)
其实 ID3DXMesh 继承了其父类 ID3DXBaseMesh 的大部分功能,还有一些也继承自 ID3DXBaseMesh 的网格类,
比如 ID3DXPMesh(渐进网格)也拥有ID3DXBaseMesh的很多功能
1.几何信息
ID3DXBaseMesh接口包含一个顶点缓存和一个索引缓存
//eg:
IDirect3DVertexBuffer9 *vb = 0;
Mesh->GetVertexBuffer( &vb );
IDirect3DIndexBuffer9 *ib = 0;
Mesh->GetIndexBuffer( &ib );
如果想锁定缓存并且对他们进行读写操作,操作的方式类似Lock,但只有2个参数Lock是4个
HRESULT ID3DXMesh::LockVertexBuffer( DWORD Flags, BYTE** ppData );
HRESULT ID3DXMesh::LockIndexBuffer( DWORD Flags, BYTE** ppData );
//注意,锁定的是整个顶点缓存或者索引缓存
Unlock也与此相仿
HRESULT ID3DXMesh::UnLockVertexBuffer();
HRESULT ID3DXMesh::UnLockIndexBuffer();
除了以上的方法外,ID3DXMesh还提供了获取集合信息了一些其他很实用的方法:
DWORD GetFVF(); //返回一个描述了顶点格式的值
DWORD GetNumVertices(); //返回顶点缓存中的顶点个数
DWORD GetNumBytesPerVertex(); //返回每个顶点所占的字节数
DWORD GetNumFaces(); //返回网格中(三角形)面片的个数
2.子集和缓存属性
(1)一个网格由一个或多个 子集 组成, 一个子集是网格中一组可用 相同属性(材质、纹理、绘制状态)进行绘制的 三角形单元
(2)用来区分不同子集的办法是为每个子集都定义一个非负整数值,比如0,1,2...
(3)网格中为每个三角形单元赋予了 ID, 他代表该三角形单元所属的子集
(4)这些ID被存放在网格的 属性缓存 中,所以属性缓存的数目就等于三角形单元的数目
这四个概念一定要理解透彻!
访问属性缓存:
DWORD * buffer = 0;
Mesh->LockAttributeBuffer(lockingFlags, &buffer);
//读写操作...
Mesh->UnlockAttributeBuffer();
3.绘制
ID3DXMesh提供了DrawSubset(DWORD AttribID)来绘制由AttribID指定子集中的三角形单元组
//eg:
Mesh->DrawSubset(0); //绘制子集0中所有三角形单元组
//eg:
for(int i; i<numSubsets; i++) //绘制整个网格
{
Device->SetMaterial( mtrl[i] ); //这个子集里面的三角形们使用的纹理
Device->SetTexture( 0, textures[i] ); //这个子集里面的三角形们使用的材质
Mesh->DrawSubset(i); //绘制这个子集
}
4.网格优化(Optimize)
就是重组该网格中的 顶点和索引缓存
在ID3DXMesh中有对应的方法:
ID3DXMesh::OptimizeInplace(
DWORD Flags, //优化标记: D3DXMESHOPT_COMPACT "D3DXMESHOPT_ATTSORT"(重点!他依据属性对三角单元排序) D3DXMESHOPT_VERTEXCACHE...
CONST DWORD *pAdjacencyIn, //指向未经优化的网格的邻接数组的指针
DWORD *pAjacencyOut, //经网格优化后的邻接信息,ID3DXMesh::GetNumFaces() * 3 或者 0
DWORD *PFaceRemap //ID3DXMesh::GetNumFaces() 或者 0
LPD3DXBUFFER *ppVertexRemap //ID3DXMesh::GetNumVertices() 或者 0
);
//eg:
DWORD adjacencyInfo[Mesh->GetNumFaces()*3];
Mesh->GenerateAdjacency( 0.0f, adjacencyInfo );
DWORD optimizeAdjacencyInfo[Mesh->GetNumFaces()*3];
Mesh->OptimizeInplace( D3DXMESHOPT_ATTRSORT|D3DXMESHOPT_COMPACT|D3DXMESHOPT_VERTEXCACHE,
adjacencyInfo,
optimizeAdjacencyInfo,
0,0 );
若使用这个方法,则原网格会被修改,但可以使用 Optimize 方法,他输出优化后的版本,原版本不变
如果一个网格在优化时使用了D3DXMESHOPT_ATTSORT,则构成该网格的三角形面片 和 属性缓存中的内容 就会依据属性进行排序
(使属于同一子集的三角元被保存在 顶点缓存 或 索引缓存 连续的存储空间内)
除了排序外,这种优化还将创建一个属性表, 该属性表是一个 D3DXATTRIBUTERANGE 类型的数组结构
属性表中的每一项对应于网格的一个子集,并指定该子集中面片的几何信息被存储的位置
访问属性表:
ID3DXMesh::GetAttributeTable(
D3DXATTRIBUTERANGE *pAttribTable,
DWORD *pAttribTableSize
);
//获取属性表中的属性集的个数
DWORD numSubsets = 0;
Mesh->GetAttributeTable(0, &numSubsets);
//D3DXATTRIBUTERANGE对象可以存储 属性集,所以有多个属性集的话,可以通过创建这个对象的数组(或者用vector)来存储这些属性集
D3DXATTRIBUTERANGE table = new D3DXATTRIBUTERANGE[numSubsets];
Mesh->GetAttributeTable( table, &numSubsets );
6.邻接信息
//eg:
//获取网格的邻接信息
DWORD adjacencyInfo[Mesh->GetNumFaces() * 3];
Mesh->GenerateAdjacency(0.001f, adjacencyInfo);
7.克隆
有时需要创建网格数据的副本
//eg:
ID3DXMesh *clone = 0;
Mesh->CloneMeshFVF(
Mesh->GetOptions(),
D3DFVF_XYZ | D3DFVF_NORMAL,
Device,
&clone
);
8.创建网格 (D3DXCreateMeshFVF)
创建“空”网格对象
//指定网格的 面片数和顶点数, 然后为顶点缓存、索引缓存和属性缓存分配大小合适的内存,最后手工填入网格的数据
HRESULT WINAPI D3DXCreateMeshFVF(
DWORD NumFaces,
DWORD NumVertices,
DWORD Options, //D3DXMESH_MANAGED D3DXMESH_WRITEONLY D3DXMESH_DYNAMIC...
DWORD FVF,
LPDIRECT3DDEVICE9 pDevice,
LPD3DXMESH *ppMesh
);
另一个创建“空”网格对象的方法:D3DXCreateMesh,这个办法用到了顶点声明ID3DVERTEXELEMENT9,关于顶点声明会在hlsl里说到
9.实例
(1)创建空网格
(2)将立方体的面片数和顶点数写入网格缓存
(3)指定网格中每个面片所属的子集 //这就是填写属性缓存,记住一点:属性缓存用来存放每个面片所属的子集
(4)生成该网格的邻接信息 //优化网格的准备工作
(5)优化网格
(6)绘制网格
1.Setup部分:
//初始化用到的内容
ID3DXMesh* Mesh = 0;
const DWORD NumSubsets = 3;
IDirect3DTexture9* Textures[3] = {0, 0, 0};
//创建空网格
hr = D3DXCreateMeshFVF(
12, //12个面片
24, //24个顶点
D3DXMESH_MANAGED,
Vertex::FVF,
Device,
&Mesh);
//填写顶点缓存
Vertex* v = 0;
Mesh->LockVertexBuffer(0, (void**)&v);
v[0] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
//...
Mesh->UnlockVertexBuffer();
//填写索引缓存
WORD* i = 0;
Mesh->LockIndexBuffer(0, (void**)&i);
i[0] = 0; i[1] = 1; i[2] = 2;
//...
Mesh->UnlockIndexBuffer();
//填写属性缓存 (属性缓存的总数等于三角形单元的总数)
DWORD* attributeBuffer = 0;
Mesh->LockAttributeBuffer(0, &attributeBuffer);
for(int a = 0; a < 4; a++)
attributeBuffer[a] = 0;
//...
Mesh->UnlockAttributeBuffer();
//网格优化,先要计算邻接信息
std::Vector<DWORD> ajacencyBuffer(Mesh->GetNumFaces() * 3);
Mesh->GenerateAdjacency(0.0f, &adjacencyBuffer[0] );
hr = Mesh->OptimizeInplace(
D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE,
&adjacencyBuffer[0],
0,0,0
);
//读入纹理文件,关闭光照,设置 观察矩阵 和 投影矩阵
2.Display部分
旋转矩阵部分略过..
for(int i=0; i<NumSubsets; i++)
{
Device->SetTexture(0, Texture[i]);
Mesh->DrawSubset( i );
}