Note: While
D3DXMESHOPT_ATTRSORT is sufficient to set up the attribute tables, it does not provide further optimization to improve vertex cache performance. It is highly recommended that you instead use the
D3DXMESHOPT_VERTEXCACHE flag, which will create the attribute table and also reorder the vertices to make optimum use of the vertex cache.
if (FAILED(SetSubsets(pMesh,faceCount,subsetNum)) {
// handle error
...
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) {
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();
// 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;
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;
}
// 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.