HRESULT LoadMesh(IDirect3DDevice9* pd3dDevice, WCHAR* strFileName, ID3DXMesh** ppMesh) { ID3DXMesh* pMesh = NULL; WCHAR str[MAX_PATH]; HRESULT hr; V_RETURN(DXUTFindDXSDKMediaFileCch(str, MAX_PATH, strFileName)); V_RETURN(D3DXLoadMeshFromX(str, D3DXMESH_MANAGED, pd3dDevice, NULL, NULL, NULL, NULL, &pMesh)); DWORD* rgdwAdjacency = NULL; if(!(pMesh->GetFVF() & D3DFVF_NORMAL)) { ID3DXMesh* pTempMesh; V(pMesh->CloneMeshFVF(pMesh->GetOptions(), pMesh->GetFVF() | D3DFVF_NORMAL, pd3dDevice, &pTempMesh)); V(D3DXComputeNormals(pTempMesh, NULL)); SAFE_RELEASE(pMesh); pMesh = pTempMesh; } rgdwAdjacency = new DWORD[pMesh->GetNumFaces() * 3]; if(rgdwAdjacency == NULL) return E_OUTOFMEMORY; V(pMesh->GenerateAdjacency(1e-6f, rgdwAdjacency)); V(pMesh->OptimizeInplace(D3DXMESHOPT_VERTEXCACHE, rgdwAdjacency, NULL, NULL, NULL)); delete []rgdwAdjacency; *ppMesh = pMesh; return S_OK; }
void MeshX::BuildSkinnedMesh() { ID3DXMesh* oldMesh = allocMeshHierarchy.GetMeshList().front()->MeshData.pMesh; //compute normal D3DVERTEXELEMENT9 elements[MAX_FVF_DECL_SIZE]; oldMesh->GetDeclaration(elements); ID3DXMesh* tempMesh = 0; ID3DXMesh* tempOpMesh = 0; oldMesh->CloneMesh(D3DXMESH_SYSTEMMEM, elements, pDevice, &tempMesh); if( !HasNormals(tempMesh) ) D3DXComputeNormals(tempMesh, 0); //optimize the mesh DWORD* adj = new DWORD[tempMesh->GetNumFaces()*3]; ID3DXBuffer* remap = 0; tempMesh->GenerateAdjacency(0.00001f, adj); tempMesh->Optimize(D3DXMESH_SYSTEMMEM | D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_ATTRSORT, adj, 0, 0, &remap, &tempOpMesh); SafeRelease(tempMesh); // In the .X file (specifically the array DWORD vertexIndices[nWeights] // data member of the SkinWeights template) each bone has an array of // indices which identify the vertices of the mesh that the bone influences. // Because we have just rearranged the vertices (from optimizing), the vertex // indices of a bone are obviously incorrect (i.e., they index to vertices the bone // does not influence since we moved vertices around). In order to update a bone's // vertex indices to the vertices the bone _does_ influence, we simply need to specify // where we remapped the vertices to, so that the vertex indices can be updated to // match. This is done with the ID3DXSkinInfo::Remap method. skinInfo->Remap(tempOpMesh->GetNumVertices(), (DWORD*)remap->GetBufferPointer()); SafeRelease(remap); // Done with remap info. DWORD numBoneComboEntries = 0; ID3DXBuffer* boneComboTable = 0; DWORD maxVertInfluences; skinInfo->ConvertToIndexedBlendedMesh(tempOpMesh, 0, 128, 0, 0, 0, 0, &maxVertInfluences, &numBoneComboEntries, &boneComboTable, &this->mesh); SafeRelease(tempOpMesh); SafeRelease(boneComboTable); delete[] adj; }
//-------------------------------------------------------------------------------------- // This function loads the mesh and ensures the mesh has normals; it also optimizes the // mesh for the graphics card's vertex cache, which improves performance by organizing // the internal triangle list for less cache misses. //-------------------------------------------------------------------------------------- HRESULT LoadMesh( IDirect3DDevice9* pd3dDevice, WCHAR* strFileName, ID3DXMesh** ppMesh ) { ID3DXMesh* pMesh = NULL; WCHAR str[MAX_PATH]; HRESULT hr; // Load the mesh with D3DX and get back a ID3DXMesh*. For this // sample we'll ignore the X file's embedded materials since we know // exactly the model we're loading. See the mesh samples such as // "OptimizedMesh" for a more generic mesh loading example. V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, strFileName ) ); V_RETURN( D3DXLoadMeshFromX( str, D3DXMESH_MANAGED, pd3dDevice, NULL, NULL, NULL, NULL, &pMesh ) ); DWORD* rgdwAdjacency = NULL; // Make sure there are normals which are required for lighting if( !( pMesh->GetFVF() & D3DFVF_NORMAL ) ) { ID3DXMesh* pTempMesh; V( pMesh->CloneMeshFVF( pMesh->GetOptions(), pMesh->GetFVF() | D3DFVF_NORMAL, pd3dDevice, &pTempMesh ) ); V( D3DXComputeNormals( pTempMesh, NULL ) ); SAFE_RELEASE( pMesh ); pMesh = pTempMesh; } // Optimize the mesh for this graphics card's vertex cache // so when rendering the mesh's triangle list the vertices will // cache hit more often so it won't have to re-execute the vertex shader // on those vertices so it will improve perf. rgdwAdjacency = new DWORD[pMesh->GetNumFaces() * 3]; if( rgdwAdjacency == NULL ) return E_OUTOFMEMORY; V( pMesh->GenerateAdjacency( 1e-6f, rgdwAdjacency ) ); V( pMesh->OptimizeInplace( D3DXMESHOPT_VERTEXCACHE, rgdwAdjacency, NULL, NULL, NULL ) ); delete []rgdwAdjacency; *ppMesh = pMesh; return S_OK; }
//-------------------------------------------------------------------------------------- //메쉬 불러오는 함수 //-------------------------------------------------------------------------------------- HRESULT LoadMesh( IDirect3DDevice9* pd3dDevice, WCHAR* strFileName, ID3DXMesh** ppMesh ) { ID3DXMesh* pMesh = NULL; WCHAR str[MAX_PATH]; HRESULT hr; V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, strFileName ) ); V_RETURN( D3DXLoadMeshFromX( str, D3DXMESH_MANAGED, pd3dDevice, NULL, NULL, NULL, NULL, &pMesh ) ); DWORD* rgdwAdjacency = NULL; // mesh에 노말벡터 생성하는 코드 if( !( pMesh->GetFVF() & D3DFVF_NORMAL ) ) { ID3DXMesh* pTempMesh; V( pMesh->CloneMeshFVF( pMesh->GetOptions(), pMesh->GetFVF() | D3DFVF_NORMAL, pd3dDevice, &pTempMesh ) ); V( D3DXComputeNormals( pTempMesh, NULL ) ); SAFE_RELEASE( pMesh ); pMesh = pTempMesh; } //성능 향상을 도모하고자 인접 정보를 기록 //각 mesh(삼각형) 정보 테이블을 가지는 형태 //해당 정보는 pMesh가 가지고 있음 //각 mesh 정보는 인접할 수 있는 점의 최대 개수가 3개 이내임을 활용해 효율적으로 vertex 운영 rgdwAdjacency = new DWORD[pMesh->GetNumFaces() * 3]; if( rgdwAdjacency == NULL ) return E_OUTOFMEMORY; V( pMesh->GenerateAdjacency( 1e-6f, rgdwAdjacency ) ); //버텍스 캐쉬를 활용하는 것 V( pMesh->OptimizeInplace( D3DXMESHOPT_VERTEXCACHE, rgdwAdjacency, NULL, NULL, NULL ) ); delete []rgdwAdjacency; //callback out 변수에 최종 값 저장 *ppMesh = pMesh; return S_OK; }
void GWater::recreateGraphInfo() { HRESULT hr = S_FALSE; SeaVertex *pVertextBuffer = NULL; //Mesh的顶点缓冲区 DWORD *pIndexBuffer = NULL; //Mesh的索引缓冲区 ID3DXMesh* mesh = 0; hr = D3DXCreateMeshFVF ( mCellCount * mCellCount * 2, ( mCellCount + 1 ) * ( mCellCount + 1 ), D3DXMESH_32BIT | D3DXMESH_MANAGED, FVFSea, Content::Device.getD9Device(), &mesh ); dDebugMsgBox ( hr, "创建海面Mesh失败!" ); DWORD dwIndex = 0; mesh->LockVertexBuffer ( D3DLOCK_DISCARD, ( void** ) &pVertextBuffer ); for ( int i = 0; i < mCellCount + 1; i++ ) { for ( int j = 0; j < mCellCount + 1; j++ ) { dwIndex = i * ( mCellCount + 1 ) + j; pVertextBuffer[dwIndex].vertex.x = ( j - mCellCount / 2.0f ) * mCellWidth; pVertextBuffer[dwIndex].vertex.y = 0; pVertextBuffer[dwIndex].vertex.z = ( i - mCellCount / 2.0f ) * mCellWidth; pVertextBuffer[dwIndex].u = j / 10.0f; pVertextBuffer[dwIndex].v = ( mCellCount - i ) / 10.0f; } } mesh->UnlockVertexBuffer(); mesh->LockIndexBuffer ( D3DLOCK_DISCARD, ( void** ) &pIndexBuffer ); DWORD dwBaseIndex = 0; for ( int i = 0; i < mCellCount; i++ ) { for ( int j = 0; j < mCellCount; j++ ) { pIndexBuffer[dwBaseIndex + 0] = i * ( mCellCount + 1 ) + j; pIndexBuffer[dwBaseIndex + 1] = ( i + 1 ) * ( mCellCount + 1 ) + j; pIndexBuffer[dwBaseIndex + 2] = ( i + 1 ) * ( mCellCount + 1 ) + j + 1; pIndexBuffer[dwBaseIndex + 3] = i * ( mCellCount + 1 ) + j; pIndexBuffer[dwBaseIndex + 4] = ( i + 1 ) * ( mCellCount + 1 ) + j + 1;; pIndexBuffer[dwBaseIndex + 5] = i * ( mCellCount + 1 ) + j + 1; dwBaseIndex += 6; } } mesh->UnlockIndexBuffer(); mMeshBufferNode->setMesh ( mesh ); mMeshBufferNode->setSubCount ( 1 ); DWORD *pAdj = new DWORD[mesh->GetNumFaces() * 3]; mesh->GenerateAdjacency ( 1.0f, pAdj ); delete []pAdj ; }
void Terrain::buildSubGridMesh(RECT& R, VertexPNT* gridVerts) { //=============================================================== // Create the subgrid mesh. ID3DXMesh* subMesh = 0; D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE]; UINT numElems = 0; HR(VertexPNT::Decl->GetDeclaration(elems, &numElems)); HR(D3DXCreateMesh(SubGrid::NUM_TRIS, SubGrid::NUM_VERTS, D3DXMESH_MANAGED, elems, gd3dDevice, &subMesh)); //=============================================================== // Build Vertex Buffer. Copy rectangle of vertices from the // grid into the subgrid structure. VertexPNT* v = 0; HR(subMesh->LockVertexBuffer(0, (void**)&v)); int k = 0; for(int i = R.top; i <= R.bottom; ++i) { for(int j = R.left; j <= R.right; ++j) { v[k++] = gridVerts[i*mVertCols+j]; } } //=============================================================== // Compute the bounding box before unlocking the vertex buffer. AABB bndBox; HR(D3DXComputeBoundingBox((D3DXVECTOR3*)v, subMesh->GetNumVertices(), sizeof(VertexPNT), &bndBox.minPt, &bndBox.maxPt)); HR(subMesh->UnlockVertexBuffer()); //=============================================================== // Build Index and Attribute Buffer. // Get indices for subgrid (we don't use the verts here--the verts // are given by the parameter gridVerts). std::vector<D3DXVECTOR3> tempVerts; std::vector<DWORD> tempIndices; GenTriGrid(SubGrid::NUM_ROWS, SubGrid::NUM_COLS, mDX, mDZ, D3DXVECTOR3(0.0f, 0.0f, 0.0f), tempVerts, tempIndices); WORD* indices = 0; DWORD* attBuff = 0; HR(subMesh->LockIndexBuffer(0, (void**)&indices)); HR(subMesh->LockAttributeBuffer(0, &attBuff)); for(int i = 0; i < SubGrid::NUM_TRIS; ++i) { indices[i*3+0] = (WORD)tempIndices[i*3+0]; indices[i*3+1] = (WORD)tempIndices[i*3+1]; indices[i*3+2] = (WORD)tempIndices[i*3+2]; attBuff[i] = 0; // All in subset 0. } HR(subMesh->UnlockIndexBuffer()); HR(subMesh->UnlockAttributeBuffer()); //=============================================================== // Optimize for the vertex cache and build attribute table. DWORD* adj = new DWORD[subMesh->GetNumFaces()*3]; HR(subMesh->GenerateAdjacency(EPSILON, adj)); HR(subMesh->OptimizeInplace(D3DXMESHOPT_VERTEXCACHE|D3DXMESHOPT_ATTRSORT, adj, 0, 0, 0)); delete[] adj; //=============================================================== // Save the mesh and bounding box. mSubGridMeshes.push_back(subMesh); mSubGridBndBoxes.push_back(bndBox); }
void SkinnedMesh::buildSkinnedMesh(ID3DXMesh* mesh) { //==================================================================== // First add a normal component and 2D texture coordinates component. D3DVERTEXELEMENT9 elements[64]; UINT numElements = 0; VertexPNT::Decl->GetDeclaration(elements, &numElements); ID3DXMesh* tempMesh = 0; HR(mesh->CloneMesh(D3DXMESH_SYSTEMMEM, elements, gd3dDevice, &tempMesh)); if( !hasNormals(tempMesh) ) HR(D3DXComputeNormals(tempMesh, 0)); //==================================================================== // Optimize the mesh; in particular, the vertex cache. DWORD* adj = new DWORD[tempMesh->GetNumFaces()*3]; ID3DXBuffer* remap = 0; HR(tempMesh->GenerateAdjacency(EPSILON, adj)); ID3DXMesh* optimizedTempMesh = 0; HR(tempMesh->Optimize(D3DXMESH_SYSTEMMEM | D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_ATTRSORT, adj, 0, 0, &remap, &optimizedTempMesh)); ReleaseCOM(tempMesh); // Done w/ this mesh. delete[] adj; // Done with buffer. // In the .X file (specifically the array DWORD vertexIndices[nWeights] // data member of the SkinWeights template) each bone has an array of // indices which identify the vertices of the mesh that the bone influences. // Because we have just rearranged the vertices (from optimizing), the vertex // indices of a bone are obviously incorrect (i.e., they index to vertices the bone // does not influence since we moved vertices around). In order to update a bone's // vertex indices to the vertices the bone _does_ influence, we simply need to specify // where we remapped the vertices to, so that the vertex indices can be updated to // match. This is done with the ID3DXSkinInfo::Remap method. HR(mSkinInfo->Remap(optimizedTempMesh->GetNumVertices(), (DWORD*)remap->GetBufferPointer())); ReleaseCOM(remap); // Done with remap info. //==================================================================== // The vertex format of the source mesh does not include vertex weights // nor bone index data, which are both needed for vertex blending. // Therefore, we must convert the source mesh to an "indexed-blended-mesh," // which does have the necessary data. DWORD numBoneComboEntries = 0; ID3DXBuffer* boneComboTable = 0; HR(mSkinInfo->ConvertToIndexedBlendedMesh(optimizedTempMesh, D3DXMESH_MANAGED | D3DXMESH_WRITEONLY, MAX_NUM_BONES_SUPPORTED, 0, 0, 0, 0, &mMaxVertInfluences, &numBoneComboEntries, &boneComboTable, &mSkinnedMesh)); ReleaseCOM(optimizedTempMesh); // Done with tempMesh. ReleaseCOM(boneComboTable); // Don't need bone table. #if defined(DEBUG) | defined(_DEBUG) // Output to the debug output the vertex declaration of the mesh at this point. // This is for insight only to see what exactly ConvertToIndexedBlendedMesh // does to the vertex declaration. D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE]; HR(mSkinnedMesh->GetDeclaration(elems)); OutputDebugString("\nVertex Format After ConvertToIndexedBlendedMesh\n"); int i = 0; while( elems[i].Stream != 0xff ) // While not D3DDECL_END() { if( elems[i].Type == D3DDECLTYPE_FLOAT1) OutputDebugString("Type = D3DDECLTYPE_FLOAT1; "); if( elems[i].Type == D3DDECLTYPE_FLOAT2) OutputDebugString("Type = D3DDECLTYPE_FLOAT2; "); if( elems[i].Type == D3DDECLTYPE_FLOAT3) OutputDebugString("Type = D3DDECLTYPE_FLOAT3; "); if( elems[i].Type == D3DDECLTYPE_UBYTE4) OutputDebugString("Type = D3DDECLTYPE_UBYTE4; "); if( elems[i].Usage == D3DDECLUSAGE_POSITION) OutputDebugString("Usage = D3DDECLUSAGE_POSITION\n"); if( elems[i].Usage == D3DDECLUSAGE_BLENDWEIGHT) OutputDebugString("Usage = D3DDECLUSAGE_BLENDWEIGHT\n"); if( elems[i].Usage == D3DDECLUSAGE_BLENDINDICES) OutputDebugString("Usage = D3DDECLUSAGE_BLENDINDICES\n"); if( elems[i].Usage == D3DDECLUSAGE_NORMAL) OutputDebugString("Usage = D3DDECLUSAGE_NORMAL\n"); if( elems[i].Usage == D3DDECLUSAGE_TEXCOORD) OutputDebugString("Usage = D3DDECLUSAGE_TEXCOORD\n"); ++i; } #endif }
void CMeshConverter::OptimiseGraphicsObject(void) { int i; int j; ID3DXMesh* pXMesh; CGraphicsPrimitive* pcPrimitive; CVertexBufferExtended* psVertexBuffer; void* pvDestIndexBuffer; void* pvDestVertexBuffer; void* pvSrcIndexBuffer; void* pvSrcVertexBuffer; int iVertSize; DWORD* pvAdjacency; int iPrimitiveStart; int iOldVertSize; int iNumIndices; int iOldVertexBufferIndex; int iNumTriangles; void* pvDestBaseIndexBuffer; SIndexBuffer* psIndexBuffer; DWORD iMeshOptions; mpcGraphicsObject->SortPrimitives(); mpcGraphicsObject->Lock(); psIndexBuffer = mpcGraphicsObject->GetIndexBuffer(); iVertSize = 0; iNumIndices = 0; iOldVertexBufferIndex = 0; iNumTriangles = 0; iOldVertSize = 0; iMeshOptions = D3DXMESH_SYSTEMMEM; SetFlag((int*)&iMeshOptions, D3DXMESH_32BIT, psIndexBuffer->iIndexSize == 4); for (i = 0; i < mpcGraphicsObject->GetNumPrimitives(); i++) { pcPrimitive = mpcGraphicsObject->GetPrimitive(i); psVertexBuffer = mpcGraphicsObject->GetVertexBufferForIndex(pcPrimitive->miVertexBufferIndex); if (iOldVertSize != psVertexBuffer->iVertexSize) { if (iNumIndices != 0) { gcD3D.CreateMesh(iNumTriangles, iNumIndices, iMeshOptions, psVertexBuffer->iVertexFormat, &pXMesh); iVertSize = pXMesh->GetNumBytesPerVertex(); pXMesh->LockIndexBuffer(D3DLOCK_NO_DIRTY_UPDATE, &pvDestBaseIndexBuffer); pXMesh->LockVertexBuffer(D3DLOCK_NO_DIRTY_UPDATE, &pvDestVertexBuffer); psVertexBuffer = mpcGraphicsObject->GetVertexBufferForIndex(iOldVertexBufferIndex); pvSrcVertexBuffer = psVertexBuffer->pvLockedBuffer; if (iVertSize != psVertexBuffer->iVertexSize) { gcLogger.Error("D3DX vertex size differs from expected size"); break; } memcpy(pvDestVertexBuffer, pvSrcVertexBuffer, psVertexBuffer->iVertexSize * psVertexBuffer->iNumVerticies); pvDestIndexBuffer = pvDestBaseIndexBuffer; for (j = iPrimitiveStart; j < i; j++) { pcPrimitive = mpcGraphicsObject->GetPrimitive(j); pvSrcIndexBuffer = RemapSinglePointer(psIndexBuffer->pvLockedBuffer, 2 * pcPrimitive->miStartIndex); memcpy(pvDestIndexBuffer, pvSrcIndexBuffer, psIndexBuffer->iIndexSize * pcPrimitive->miNumVertices); pvDestIndexBuffer = RemapSinglePointer(pvDestIndexBuffer, pcPrimitive->miNumVertices); } pvAdjacency = (DWORD*)malloc(pcPrimitive->miNumPrimitives * 3 * sizeof(DWORD)); pXMesh->GenerateAdjacency(0.0f, pvAdjacency); pXMesh->OptimizeInplace(D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_DONOTSPLIT, pvAdjacency, NULL, NULL, NULL); free(pvAdjacency); pvDestIndexBuffer = pvDestBaseIndexBuffer; for (j = iPrimitiveStart; j < i; j++) { pcPrimitive = mpcGraphicsObject->GetPrimitive(j); pvSrcIndexBuffer = RemapSinglePointer(psIndexBuffer->pvLockedBuffer, 2 * pcPrimitive->miStartIndex); memcpy(pvSrcIndexBuffer, pvDestIndexBuffer, psIndexBuffer->iIndexSize * pcPrimitive->miNumVertices); pvDestIndexBuffer = RemapSinglePointer(pvDestIndexBuffer, pcPrimitive->miNumVertices); } memcpy(pvSrcVertexBuffer, pvDestVertexBuffer, psVertexBuffer->iVertexSize * psVertexBuffer->iNumVerticies); pXMesh->UnlockIndexBuffer(); pXMesh->UnlockVertexBuffer(); pXMesh->Release(); } iPrimitiveStart = i; iNumIndices = 0; iOldVertexBufferIndex = pcPrimitive->miVertexBufferIndex; } else { iNumIndices += pcPrimitive->miNumVertices; iNumTriangles += pcPrimitive->miNumPrimitives; if (iOldVertexBufferIndex != pcPrimitive->miVertexBufferIndex) { gcUserError.Set("Primitive vertex buffer index is F****D!"); break; } } } mpcGraphicsObject->Unlock(); }
//-------------------------------------------------------------------------------------- HRESULT CMeshLoader::Create( IDirect3DDevice9* pd3dDevice, const WCHAR* strFilename ) { HRESULT hr; WCHAR str[ MAX_PATH ] = {0}; // Start clean Destroy(); // Store the device pointer m_pd3dDevice = pd3dDevice; // Load the vertex buffer, index buffer, and subset information from a file. In this case, // an .obj file was chosen for simplicity, but it's meant to illustrate that ID3DXMesh objects // can be filled from any mesh file format once the necessary data is extracted from file. //V_RETURN( LoadGeometryFromOBJ( strFilename ) ); V_RETURN( LoadGeometryFromOBJ_Fast( strFilename ) ); // Set the current directory based on where the mesh was found WCHAR wstrOldDir[MAX_PATH] = {0}; GetCurrentDirectory( MAX_PATH, wstrOldDir ); SetCurrentDirectory( m_strMediaDir ); // Load material textures for( int iMaterial = 0; iMaterial < m_Materials.GetSize(); iMaterial++ ) { Material* pMaterial = m_Materials.GetAt( iMaterial ); if( pMaterial->strTexture[0] ) { // Avoid loading the same texture twice bool bFound = false; for( int x = 0; x < iMaterial; x++ ) { Material* pCur = m_Materials.GetAt( x ); if( 0 == wcscmp( pCur->strTexture, pMaterial->strTexture ) ) { bFound = true; pMaterial->pTexture = pCur->pTexture; break; } } // Not found, load the texture if( !bFound ) { V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, pMaterial->strTexture ) ); V_RETURN( D3DXCreateTextureFromFile( pd3dDevice, pMaterial->strTexture, &( pMaterial->pTexture ) ) ); int a = 0; } } } // Restore the original current directory SetCurrentDirectory( wstrOldDir ); // Create the encapsulated mesh ID3DXMesh* pMesh = NULL; V_RETURN( D3DXCreateMesh( m_Indices.GetSize() / 3, m_Vertices.GetSize(), D3DXMESH_MANAGED | D3DXMESH_32BIT, VERTEX_DECL, pd3dDevice, &pMesh ) ); // Copy the vertex data VERTEX* pVertex; V_RETURN( pMesh->LockVertexBuffer( 0, ( void** )&pVertex ) ); memcpy( pVertex, m_Vertices.GetData(), m_Vertices.GetSize() * sizeof( VERTEX ) ); pMesh->UnlockVertexBuffer(); m_Vertices.RemoveAll(); // Copy the index data DWORD* pIndex; V_RETURN( pMesh->LockIndexBuffer( 0, ( void** )&pIndex ) ); memcpy( pIndex, m_Indices.GetData(), m_Indices.GetSize() * sizeof( DWORD ) ); pMesh->UnlockIndexBuffer(); m_Indices.RemoveAll(); // Copy the attribute data DWORD* pSubset; V_RETURN( pMesh->LockAttributeBuffer( 0, &pSubset ) ); memcpy( pSubset, m_Attributes.GetData(), m_Attributes.GetSize() * sizeof( DWORD ) ); pMesh->UnlockAttributeBuffer(); m_Attributes.RemoveAll(); // Reorder the vertices according to subset and optimize the mesh for this graphics // card's vertex cache. When rendering the mesh's triangle list the vertices will // cache hit more often so it won't have to re-execute the vertex shader. DWORD* aAdjacency = new DWORD[pMesh->GetNumFaces() * 3]; if( aAdjacency == NULL ) return E_OUTOFMEMORY; V( pMesh->GenerateAdjacency( 1e-6f, aAdjacency ) ); V( pMesh->OptimizeInplace( D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, aAdjacency, NULL, NULL, NULL ) ); SAFE_DELETE_ARRAY( aAdjacency ); m_pMesh = pMesh; return S_OK; }
void PropsDemo::buildGrass() { D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE]; UINT numElems = 0; HR(GrassVertex::Decl->GetDeclaration(elems, &numElems)); HR(D3DXCreateMesh(NUM_GRASS_BLOCKS*2, NUM_GRASS_BLOCKS*4, D3DXMESH_MANAGED, elems, gd3dDevice, &mGrassMesh)); GrassVertex* v = 0; WORD* k = 0; HR(mGrassMesh->LockVertexBuffer(0, (void**)&v)); HR(mGrassMesh->LockIndexBuffer(0, (void**)&k)); int indexOffset = 0; // Scale down the region in which we generate grass. int w = (int)(mTerrain->getWidth() * 0.15f); int d = (int)(mTerrain->getDepth() * 0.15f); // Randomly generate a grass block (three intersecting quads) around the // terrain in the height range [35, 50] (similar to the trees). for(int i = 0; i < NUM_GRASS_BLOCKS; ++i) { //============================================ // Construct vertices. // Generate random position in region. Note that we also shift // this region to place it in the world. float x = (float)((rand() % w) - (w*0.5f)) - 30.0f; float z = (float)((rand() % d) - (d*0.5f)) - 20.0f; float y = mTerrain->getHeight(x, z); // Only generate grass blocks in this height range. If the height // is outside this range, generate a new random position and // try again. if(y < 37.0f || y > 40.0f) { --i; // We are trying again, so decrement back the index. continue; } float sx = GetRandomFloat(0.75f, 1.25f); float sy = GetRandomFloat(0.75f, 1.25f); float sz = GetRandomFloat(0.75f, 1.25f); D3DXVECTOR3 pos(x, y, z); D3DXVECTOR3 scale(sx, sy, sz); buildGrassFin(v, k, indexOffset, pos, scale); v += 4; k += 6; } HR(mGrassMesh->UnlockVertexBuffer()); HR(mGrassMesh->UnlockIndexBuffer()); // Fill in the attribute buffer (everything in subset 0) DWORD* attributeBufferPtr = 0; HR(mGrassMesh->LockAttributeBuffer(0, &attributeBufferPtr)); for(UINT i = 0; i < mGrassMesh->GetNumFaces(); ++i) attributeBufferPtr[i] = 0; HR(mGrassMesh->UnlockAttributeBuffer()); DWORD* adj = new DWORD[mGrassMesh->GetNumFaces()*3]; HR(mGrassMesh->GenerateAdjacency(EPSILON, adj)); HR(mGrassMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT|D3DXMESHOPT_VERTEXCACHE, adj, 0, 0, 0)); delete [] adj; }