Loading an .X File导入.X格式的文件
这个是微软格式的模型文件。存储各种任务,树木,花草等模型信息的文件,可以导入并显示与程序屏幕中,导入函数:
HRESULT D3DXLoadMeshFromX( LPCSTR pFilename, DWORD Options, LPDIRECT3DDEVICE9 pDevice, LPD3DXBUFFER *ppAdjacency, LPD3DXBUFFER *ppMaterials, LPD3DXBUFFER* ppEffectInstances, PDWORD pNumMaterials, LPD3DXMESH *ppMesh );
例子: // Step 1: Load the .X file from file into a system memory mesh. ID3DXMesh* meshSys = 0; ID3DXBuffer* adjBuffer = 0; ID3DXBuffer* mtrlBuffer = 0; DWORD numMtrls = 0; HR(D3DXLoadMeshFromX(filename.c_str(), D3DXMESH_SYSTEMMEM, gd3dDevice, &adjBuffer, &mtrlBuffer, 0, &numMtrls, &meshSys));
测试顶点单位法向量 Vertex Normals
当一个.X 文件导入之后,ID3DXMesh的顶点属性一定有位置,但是不一定有单位向量,纹理坐标,我们可以手动地添加这些信息。可以colone到一个新的ID3DXMesh中,指定格式为我们想要的格式,就可以添加相应的属性了。
也可以把拥有不同属性的.x文件统一格式化成有相同格式属性的Mesh,然后统一渲染,可以加快渲染速度。
如下面代码检查是否有单位化向量,没有就才加,以免覆盖:
// Step 2: Find out if the mesh already has normal info. D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE]; HR(meshSys->GetDeclaration(elems)); bool hasNormals = false; for(int i = 0; i < MAX_FVF_DECL_SIZE; ++i) { // Did we reach D3DDECL_END() {0xFF,0,D3DDECLTYPE_UNUSED, 0,0,0}? if(elems[i].Stream == 0xff) break; if( elems[i].Type == D3DDECLTYPE_FLOAT3 && elems[i].Usage == D3DDECLUSAGE_NORMAL &&//如果我们找到一个3D顶点有normal usage elems[i].UsageIndex == 0 )//且其索引为0,那么就可以判定单位化向量存在。 { hasNormals = true; break; } }
该表顶点格式
colone mesh,然后格式化为VertexPNT (position, normal, 2D texture coordinates) 格式,就可以采用我们自己写的shader来处理,当然这个shader是自己写的,格式也随自己需要而定。例如:
// Step 3: Change vertex format to VertexPNT. D3DVERTEXELEMENT9 elements[64]; UINT numElements = 0; VertexPNT::Decl->GetDeclaration(elements, &numElements); ID3DXMesh* temp = 0; HR(meshSys->CloneMesh(D3DXMESH_SYSTEMMEM,//这里一个函数就可以clone并格式化,非常方便,就是不太好理解。 elements, gd3dDevice, &temp)); ReleaseCOM(meshSys); meshSys = temp;
创造单位法向量信息Generating Normals
函数自动为新的增加的信息增加空间的,所以也不用我们自己管理其内存。:
// Step 4: If the mesh did not have normals, generate them. if( hasNormals == false) HR(D3DXComputeNormals(meshSys, 0));//自动计算新的单位化信息 的函数
第二个参数可以为零,是输入mesh的邻接矩阵的信息。
优化Optimizing
可以提高渲染速度.
// Step 5: Optimize the mesh. HR(meshSys->Optimize(D3DXMESH_MANAGED | D3DXMESHOPT_COMPACT |//一个优化函数就搞定了 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, (DWORD*)adjBuffer->GetBufferPointer(), 0, 0, 0, meshOut)); ReleaseCOM(meshSys); // Done w/ system mesh. ReleaseCOM(adjBuffer); // Done with buffer.
X 文件材质
D3DXLoadMeshFromX第七个参数是包含在mesh中的材质数量,第五个参数是一个D3DXMATERIAL数组:
typedef struct D3DXMATERIAL { D3DMATERIAL9 MatD3D;//各种材质,如下结构体 LPSTR pTextureFilename;//自己指定的指向需要文件纹理图像 } D3DXMATERIAL; typedef struct _D3DMATERIAL9 { D3DCOLORVALUE Diffuse; D3DCOLORVALUE Ambient; D3DCOLORVALUE Specular; D3DCOLORVALUE Emissive;//自发射光 float Power; } D3DMATERIAL9;
.x文件的可以通过循环子网subset来渲染。
如下面例子:
void LoadXFile( const std::string& filename, ID3DXMesh** meshOut, std::vector<Mtrl>& mtrls, std::vector<IDirect3DTexture9*>& texs);
设置材质和纹理:
// Step 6: Extract the materials and load the textures. if( mtrlBuffer != 0 && numMtrls != 0 ) { D3DXMATERIAL* d3dxmtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer(); for(DWORD i = 0; i < numMtrls; ++i) { // Save the ith material. Note that the MatD3D property // does not have an ambient value set when it's loaded, so // just set it to the diffuse value. Mtrl m; m.ambient = d3dxmtrls[i].MatD3D.Diffuse; m.diffuse = d3dxmtrls[i].MatD3D.Diffuse; m.spec = d3dxmtrls[i].MatD3D.Specular; m.specPower = d3dxmtrls[i].MatD3D.Power; mtrls.push_back( m ); // Check if the ith material has an associative texture if( d3dxmtrls[i].pTextureFilename != 0 ) { // Yes, load the texture for the ith subset IDirect3DTexture9* tex = 0; char* texFN = d3dxmtrls[i].pTextureFilename; HR(D3DXCreateTextureFromFile(gd3dDevice, texFN, &tex)); // Save the loaded texture texs.push_back( tex ); } else { // No texture for the ith subset texs.push_back( 0 ); } } } ReleaseCOM(mtrlBuffer); // done w/ buffer
渲染:
// Application data members. ID3DXMesh* mMesh; std::vector<Mtrl> mMtrl; std::vector<IDirect3DTexture9*> mTex; // In application constructor: LoadXFile("skullocc.x", &mMesh, mMtrl, mTex); // Rendering code: HR(mFX->BeginPass(0)); for(int j = 0; j < mMtrl.size(); ++j) { HR(mFX->SetValue(mhMtrl, &mMtrl[j], sizeof(Mtrl))); // If there is a texture, then use. if(mTex[j] != 0) { HR(mFX->SetTexture(mhTex, mTex[j])); } // But if not, then set a pure white texture. else { HR(mFX->SetTexture(mhTex, mWhiteTex)); } HR(mFX->CommitChanges()); HR(mMesh->DrawSubset(j)); } HR(mFX->EndPass());
清理代码:
ReleaseCOM(mMesh);
for(int i = 0; i < mTex.size(); ++i)
ReleaseCOM(mTex[i]);