|
The ID3DXMesh class provided in DX 8 allows you to divide a mesh into several parts, or subsets, that can be rendered independently of each other using the ID3DXMesh::DrawSubset() method. This allows you to apply different materials, textures, or renderstates to different portions of a mesh, or to selectively render only certain portions of a mesh. To do this, you must set the attribute ID for each face, to define which subset the face belongs to. First, call ID3DXMesh::LockAttributeBuffer() to get a pointer to the attribute buffer, which is an array of DWORDs that defines the subset number for each face. The subset numbers can be any 32 bit value you choose, though it is typical to use values 0 to n-1 to define n subsets. Once you have locked the attribute buffers, you can set the desired attribute values using the buffer pointer, then unlock the attribute buffer using ID3DXMesh::UnlockAttributeBuffer() when the changes are complete. At this point, you can use ID3DXMesh::DrawSubset()
to render a subset, but you will
first want to perform one more step for the sake of performance. If you were
to call ID3DXMesh::DrawSubset() at this point, it would have to scan through
the attribute buffer to determine which faces belong to the requested subset.
Instead, if you call ID3DXMesh::OptimizeInPlace() or
ID3DXMesh::Optimize()
with the D3DXMESHOPT_ATTRSORT flag (see
note below), the index and vertex buffers will be
sorted such that each subset represents a contiguous sequence of faces. This
call will also generate an "attribute table", which is an array of one
D3DXATTRIBUTERANGE structure per subset defining the starting and ending face
numbers for each range, and the range of vertices used. Once this is
performed, ID3DXMesh::DrawSubset() merely has to look up the range of faces
and vertices to draw from the attribute table, providing far greater
performance. Here is an example of a function that divides a mesh into a series of irregularly sized subsets, allowing subsets to consist of non-contiguous series of faces:
// define subset info we will assign
DWORD faceCount[] = { 50, 100, 50, 100, 200, 0};
DWORD subsetNum[] = { 0, 1, 0, 2, 3 };
// First 50 faces (0-49) belong to subset 0
// Next 100 faces (50-149) belong to subset 1
// Next 50 faces (150-199) also belong to subset 0
// Next 100 faces (200-299) belong to subset 2
// Next 200 faces (300-499) belong to subset 3
// 0 faces in final faceCount[] index denotes end of
list
if (FAILED(SetSubsets(pMesh,faceCount,subsetNum)) { // handle error ...
}
DWORD SetSubsets(ID3DXMesh *pMesh,DWORD *pFaceCount,DWORD *pSubsetNum) {
// Get face count, used to ensure we don't overrun
attribute buffer
DWORD numFaces=pMesh->GetFaceCount();
// lock the attribute buffer
DWORD *attribBuf;
HRESULT hr;
if (SUCCEEDED(hr=pMesh->LockAttributeBuffer(D3DLOCK_DISCARD,&attribBuf)))
{
// initialize face counter DWORD faceNum=0; // loop through the subsets for (int i=0;pFaceCount[i];i++) { // make sure there are enough faces for this subset if (faceNum+pFaceCount[i]>=numFaces) { // not enough faces, unlock the attribute buffer and return error code pMesh->UnlockAttributeBuffer();
return E_INVALIDARG;
}
// loop through the faces for this range
for (int j=0;j<pFaceCount[i];i++) { // set the subset number of each face in this range attribBuf[faceNum]=pSubsetNum[i]; // increment face counter faceNum++; } } // unlock the attribute buffer pMesh->UnlockAttributeBuffer(); // allocate storage and generate adjacency data DWORD *pAdj=new DWORD[numFaces*3]; if (!pAdj) return E_OUTOFMEMORY; if (FAILED(hr=pMesh->GenerateAdjacency(0.0f,pAdj) { delete pAdj; return hr; } // optimize the mesh with attribute sorting // D3DXMESHOPT_ATTRSORT if (FAILED(hr=pMesh->OptimizeInPlace(D3DXMESHOPT_VERTEXCACHE,pAdj,NULL,NULL,NULL)) { delete pAdj;
return hr;
} // de-allocate adjacency data storage delete pAdj; } else // return error code on failure to lock attribute buffer return hr; // return success return S_OK;
}
Note that if you already have adjacency data for the mesh, you won't have to re-compute it in the function as I have here - I included that for simplicity. If you already have adjacency data you can pass it to the optimization function, and may also need to provide a pointer to an array to contain the new adjacency information, as a parameter to the optimization function, if you have further use for this data. For more information on using the D3DX Mesh interfaces, see Using the ID3DXMesh Class.
|
Visitors Since 1/1/2000:
|