void D3D9Mesh::Clean(float Epsilon, DWORD* &AdjDataOut) { Assert(_Mesh != NULL, "Clean called on empty _Mesh."); LPD3DXMESH NewMesh; DWORD *AdjData = new DWORD[3 * _Mesh->GetNumFaces()]; AdjDataOut = new DWORD[3 * _Mesh->GetNumFaces()]; DWORD *FaceRemap = new DWORD[3 * _Mesh->GetNumFaces()]; /*D3DXWELDEPSILONS Eps; Eps.Position = Epsilon; Eps.BlendWeights = 1.0f; Eps.Normal = 1.0f; Eps.PSize = 1.0f; Eps.Specular = 1.0f; Eps.Diffuse = 1.0f; Eps.Tangent = 1.0f; Eps.Binormal = 1.0f; Eps.TessFactor = 1.0f; for(UINT _Indices = 0; _Indices < 8; _Indices++) { Eps.Texcoord[_Indices] = 1.0f; }*/ CleanVerticesAndTriangles(); Unlock(); //D3DAlwaysValidate(_Mesh->GenerateAdjacency(Epsilon, AdjDataOut)); //D3DAlwaysValidate(D3DXWeldVertices(_Mesh, D3DXWELDEPSILONS_WELDPARTIALMATCHES, &Eps, AdjDataOut, AdjData, FaceRemap, NULL)); //D3DAlwaysValidate(D3DXWeldVertices(_Mesh, D3DXWELDEPSILONS_WELDALL | D3DXWELDEPSILONS_WELDPARTIALMATCHES, NULL, AdjDataOut, AdjData, FaceRemap, NULL)); //CleanTriangles(); D3DAlwaysValidate(_Mesh->GenerateAdjacency(Epsilon, AdjData), "GenerateAdjacency"); D3DAlwaysValidate(D3DXCleanMesh(D3DXCLEAN_SIMPLIFICATION, _Mesh, AdjData, &NewMesh, AdjDataOut, NULL), "D3DXCleanMesh"); _Mesh->Release(); D3DAlwaysValidate(NewMesh->CloneMeshFVF(D3DMeshOptions, D3DMeshFVF, GetD3DDevice(), &_Mesh), "CloneMeshFVF"); NewMesh->Release(); Lock(); Unlock(); delete[] AdjData; delete[] FaceRemap; }
bool FD3D9MeshUtilities::GenerateUVs( struct FRawMesh& RawMesh, uint32 TexCoordIndex, float MinChartSpacingPercent, float BorderSpacingPercent, bool bUseMaxStretch, const TArray< int32 >* InFalseEdgeIndices, uint32& MaxCharts, float& MaxDesiredStretch, FText& OutError ) { OutError = FText(); if(!IsValid()) { OutError = LOCTEXT("GenerateUVs_FailedInvalid", "GenerateUVs failed, mesh was invalid."); return false; } int32 NumTexCoords = 0; for (int32 i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i) { if (RawMesh.WedgeTexCoords[i].Num() != RawMesh.WedgeIndices.Num()) { break; } NumTexCoords++; } if (TexCoordIndex > (uint32)NumTexCoords) { OutError = LOCTEXT("GenerateUVs_FailedUVs", "GenerateUVs failed, incorrect number of texcoords."); return false; } TRefCountPtr<ID3DXMesh> ChartMesh; TArray<uint32> AtlasAndChartAdjacency; TArray<int32> AtlasAndChartTriangleCharts; { const bool bUseFalseEdges = InFalseEdgeIndices != NULL; // When using false edges we don't remove degenerates as we want our incoming selected edge list to map // correctly to the D3DXMesh. const bool bRemoveDegenerateTriangles = !bUseFalseEdges; // Create a D3DXMesh for the triangles being charted. TRefCountPtr<ID3DXMesh> SourceMesh; if (!ConvertRawMeshToD3DXMesh(Device, RawMesh,bRemoveDegenerateTriangles,SourceMesh)) { OutError = LOCTEXT("GenerateUVs_FailedConvert", "GenerateUVs failed, couldn't convert to a D3DXMesh."); return false; } //generate adjacency info for the mesh, which is needed later TArray<uint32> Adjacency; GenerateAdjacency(SourceMesh,Adjacency,FFragmentedAdjacencyFilter()); // We don't clean the mesh as this can collapse vertices or delete degenerate triangles, and // we want our incoming selected edge list to map correctly to the D3DXMesh. if( !bUseFalseEdges ) { //clean the mesh TRefCountPtr<ID3DXMesh> TempMesh; TArray<uint32> CleanedAdjacency; CleanedAdjacency.AddUninitialized(SourceMesh->GetNumFaces() * 3); if( FAILED(D3DXCleanMesh( D3DXCLEAN_SIMPLIFICATION, SourceMesh, (::DWORD *)Adjacency.GetTypedData(), TempMesh.GetInitReference(), (::DWORD *)CleanedAdjacency.GetTypedData(), NULL ) ) ) { OutError = LOCTEXT("GenerateUVs_FailedClean", "GenerateUVs failed, couldn't clean mesh."); return false; } SourceMesh = TempMesh; Adjacency = CleanedAdjacency; } // Setup the D3DX "false edge" array. This is three DWORDS per face that define properties of the // face's edges. Values of -1 indicates that the edge may be used as a UV seam in a the chart. Any // other value indicates that the edge should never be a UV seam. This essentially allows us to // provide a precise list of edges to be used as UV seams in the new charts. uint32* FalseEdgeArray = NULL; TArray<uint32> FalseEdges; if( bUseFalseEdges ) { // -1 means "always use this edge as a chart UV seam" to D3DX FalseEdges.AddUninitialized( SourceMesh->GetNumFaces() * 3 ); for( int32 CurFalseEdgeIndex = 0; CurFalseEdgeIndex < (int32)SourceMesh->GetNumFaces() * 3; ++CurFalseEdgeIndex ) { FalseEdges[ CurFalseEdgeIndex ] = -1; } // For each tagged edge for( int32 CurTaggedEdgeIndex = 0; CurTaggedEdgeIndex < InFalseEdgeIndices->Num(); ++CurTaggedEdgeIndex ) { const int32 EdgeIndex = ( *InFalseEdgeIndices )[ CurTaggedEdgeIndex ]; // Mark this as a false edge by setting it to a value other than negative one FalseEdges[ EdgeIndex ] = Adjacency[ CurTaggedEdgeIndex ]; } FalseEdgeArray = (uint32*)FalseEdges.GetTypedData(); } // Partition the mesh's triangles into charts. TRefCountPtr<ID3DXBuffer> PartitionResultAdjacencyBuffer; TRefCountPtr<ID3DXBuffer> FacePartitionBuffer; HRESULT Result = D3DXUVAtlasPartition( SourceMesh, bUseMaxStretch ? 0 : MaxCharts, // Max charts (0 = use max stretch instead) MaxDesiredStretch, TexCoordIndex, (::DWORD *)Adjacency.GetTypedData(), (::DWORD *)FalseEdgeArray, // False edges NULL, // IMT data &GenerateUVsStatusCallback, 0.01f, // Callback frequency NULL, // Callback user data D3DXUVATLAS_GEODESIC_QUALITY, ChartMesh.GetInitReference(), FacePartitionBuffer.GetInitReference(), NULL, PartitionResultAdjacencyBuffer.GetInitReference(), &MaxDesiredStretch, &MaxCharts ); if (FAILED(Result)) { UE_LOG(LogD3D9MeshUtils, Warning, TEXT("D3DXUVAtlasPartition() returned %u with MaxDesiredStretch=%.2f, TexCoordIndex=%u."), Result, MaxDesiredStretch, TexCoordIndex ); OutError = LOCTEXT("GenerateUVs_FailedPartition", "GenerateUVs failed, D3DXUVAtlasPartition failed."); return false; } // Extract the chart adjacency data from the D3DX buffer into an array. for(uint32 TriangleIndex = 0;TriangleIndex < ChartMesh->GetNumFaces();TriangleIndex++) { for(int32 EdgeIndex = 0;EdgeIndex < 3;EdgeIndex++) { AtlasAndChartAdjacency.Add(*((uint32*)PartitionResultAdjacencyBuffer->GetBufferPointer()+TriangleIndex*3+EdgeIndex)); } } // Extract the triangle chart data from the D3DX buffer into an array. uint32* FacePartitionBufferPointer = (uint32*)FacePartitionBuffer->GetBufferPointer(); for(uint32 TriangleIndex = 0;TriangleIndex < ChartMesh->GetNumFaces();TriangleIndex++) { AtlasAndChartTriangleCharts.Add(*FacePartitionBufferPointer++); } // Scale the partitioned UVs down. FUtilVertex* LockedVertices; ChartMesh->LockVertexBuffer(0,(LPVOID*)&LockedVertices); for(uint32 VertexIndex = 0;VertexIndex < ChartMesh->GetNumVertices();VertexIndex++) { LockedVertices[VertexIndex].UVs[TexCoordIndex] /= 2048.0f; } ChartMesh->UnlockVertexBuffer(); } if(ChartMesh) { // Create a buffer to hold the triangle chart data. TRefCountPtr<ID3DXBuffer> MergedTriangleChartsBuffer; VERIFYD3D9RESULT(D3DXCreateBuffer( AtlasAndChartTriangleCharts.Num() * sizeof(int32), MergedTriangleChartsBuffer.GetInitReference() )); uint32* MergedTriangleChartsBufferPointer = (uint32*)MergedTriangleChartsBuffer->GetBufferPointer(); for(int32 TriangleIndex = 0;TriangleIndex < AtlasAndChartTriangleCharts.Num();TriangleIndex++) { *MergedTriangleChartsBufferPointer++ = AtlasAndChartTriangleCharts[TriangleIndex]; } const uint32 FakeTexSize = 1024; const float GutterSize = ( float )FakeTexSize * MinChartSpacingPercent * 0.01f; // Pack the charts into a unified atlas. HRESULT Result = D3DXUVAtlasPack( ChartMesh, FakeTexSize, FakeTexSize, GutterSize, TexCoordIndex, (::DWORD *)AtlasAndChartAdjacency.GetTypedData(), &GenerateUVsStatusCallback, 0.01f, // Callback frequency NULL, 0, MergedTriangleChartsBuffer ); if (FAILED(Result)) { UE_LOG(LogD3D9MeshUtils, Warning, TEXT("D3DXUVAtlasPack() returned %u."), Result ); OutError = LOCTEXT("GenerateUVs_FailedPack", "GenerateUVs failed, D3DXUVAtlasPack failed."); return false; } int32 NewNumTexCoords = FMath::Max<int32>(NumTexCoords, TexCoordIndex + 1); FRawMesh FinalMesh; if (!ConvertD3DXMeshToRawMesh(ChartMesh, FinalMesh, NewNumTexCoords)) { OutError = LOCTEXT("GenerateUVs_FailedSimple", "GenerateUVs failed, couldn't convert the simplified D3DXMesh back to a UStaticMesh."); return false; } // Scale/offset the UVs appropriately to ensure there is empty space around the border { const float BorderSize = BorderSpacingPercent * 0.01f; const float ScaleAmount = 1.0f - BorderSize * 2.0f; for( int32 CurUVIndex = 0; CurUVIndex < MAX_MESH_TEXTURE_COORDS; ++CurUVIndex ) { int32 NumWedges = FinalMesh.WedgeTexCoords[CurUVIndex].Num(); for( int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex ) { FVector2D& UV = FinalMesh.WedgeTexCoords[CurUVIndex][WedgeIndex]; UV.X = BorderSize + UV.X * ScaleAmount; UV.Y = BorderSize + UV.Y * ScaleAmount; } } } RawMesh = FinalMesh; } return true; }
bool FD3D9MeshUtilities::LayoutUVs( struct FRawMesh& RawMesh, uint32 TextureResolution, uint32 TexCoordIndex, FText& OutError ) { OutError = FText(); if(!IsValid() || !RawMesh.IsValid()) { OutError = LOCTEXT("LayoutUVs_FailedInvalid", "LayoutUVs failed, mesh was invalid."); return false; } int32 NumTexCoords = 0; for (int32 i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i) { if (RawMesh.WedgeTexCoords[i].Num() != RawMesh.WedgeIndices.Num()) { break; } NumTexCoords++; } if (TexCoordIndex > (uint32)NumTexCoords) { OutError = LOCTEXT("LayoutUVs_FailedUVs", "LayoutUVs failed, incorrect number of texcoords."); return false; } // Sort the mesh's triangles by whether they need to be charted, or just to be packed into the atlas. FRawMesh MeshToAtlas = RawMesh; if (TexCoordIndex > 0) { MeshToAtlas.WedgeTexCoords[TexCoordIndex] = MeshToAtlas.WedgeTexCoords[0]; } TRefCountPtr<ID3DXMesh> ChartMesh; TArray<uint32> AtlasAndChartAdjacency; TArray<int32> AtlasAndChartTriangleCharts; TRefCountPtr<ID3DXMesh> MergedMesh; TArray<uint32> MergedAdjacency; TArray<int32> MergedTriangleCharts; TRefCountPtr<ID3DXMesh> AtlasOnlyMesh; TArray<uint32> AtlasOnlyAdjacency; TArray<int32> AtlasOnlyTriangleCharts; { // Create a D3DXMesh for the triangles that only need to be atlassed. const bool bRemoveDegenerateTriangles = true; if (!ConvertRawMeshToD3DXMesh(Device,MeshToAtlas,bRemoveDegenerateTriangles,AtlasOnlyMesh)) { OutError = LOCTEXT("LayoutUVs_FailedConvert", "LayoutUVs failed, couldn't convert to a D3DXMesh."); return false; } // generate mapping orientations info FLayoutUVWindingInfo WindingInfo(AtlasOnlyMesh, TexCoordIndex); // Generate adjacency for the pre-charted triangles based on their input charts. GenerateAdjacency(AtlasOnlyMesh,AtlasOnlyAdjacency,FUVChartAdjacencyFilter(TexCoordIndex), &WindingInfo); ////clean the mesh TRefCountPtr<ID3DXMesh> TempMesh; TArray<uint32> CleanedAdjacency; CleanedAdjacency.AddUninitialized(AtlasOnlyMesh->GetNumFaces() * 3); if( FAILED(D3DXCleanMesh( D3DXCLEAN_SIMPLIFICATION, AtlasOnlyMesh, (::DWORD *)AtlasOnlyAdjacency.GetTypedData(), TempMesh.GetInitReference(), (::DWORD *)CleanedAdjacency.GetTypedData(), NULL ) ) ) { OutError = LOCTEXT("LayoutUVs_FailedClean", "LayoutUVs failed, couldn't clean mesh."); return false; } // Group the pre-charted triangles into indexed charts based on their adjacency in the chart. AssignMinimalAdjacencyGroups(CleanedAdjacency,AtlasOnlyTriangleCharts); MergedMesh = TempMesh; MergedAdjacency = CleanedAdjacency; MergedTriangleCharts = AtlasOnlyTriangleCharts; } if(MergedMesh) { // Create a buffer to hold the triangle chart data. TRefCountPtr<ID3DXBuffer> MergedTriangleChartsBuffer; VERIFYD3D9RESULT(D3DXCreateBuffer( MergedTriangleCharts.Num() * sizeof(int32), MergedTriangleChartsBuffer.GetInitReference() )); uint32* MergedTriangleChartsBufferPointer = (uint32*)MergedTriangleChartsBuffer->GetBufferPointer(); for(int32 TriangleIndex = 0;TriangleIndex < MergedTriangleCharts.Num();TriangleIndex++) { *MergedTriangleChartsBufferPointer++ = MergedTriangleCharts[TriangleIndex]; } const float GutterSize = 2.0f; // Pack the charts into a unified atlas. HRESULT Result = D3DXUVAtlasPack( MergedMesh, TextureResolution, TextureResolution, GutterSize, TexCoordIndex, (::DWORD *)MergedAdjacency.GetTypedData(), NULL, 0, NULL, 0, MergedTriangleChartsBuffer ); if (FAILED(Result)) { UE_LOG(LogD3D9MeshUtils, Warning, TEXT("D3DXUVAtlasPack() returned %u."), Result ); OutError = LOCTEXT("LayoutUVs_FailedPack", "LayoutUVs failed, D3DXUVAtlasPack failed."); return false; } int32 NewNumTexCoords = FMath::Max<int32>(NumTexCoords, TexCoordIndex + 1); FRawMesh FinalMesh; if (!ConvertD3DXMeshToRawMesh(MergedMesh, FinalMesh, NewNumTexCoords)) { OutError = LOCTEXT("LayoutUVs_FailedSimple", "LayoutUVs failed, couldn't convert the simplified D3DXMesh back to a UStaticMesh."); return false; } RawMesh = FinalMesh; } return true; }
//----------------------------------------------------------------------------- // Name: InitDeviceObjects() // Desc: Initialize scene objects. //----------------------------------------------------------------------------- HRESULT CMeshRender::InitDeviceObjects() { DWORD cVerticesPerMesh; // Load mesh LPD3DXBUFFER pAdjacencyBuffer = NULL; LPDIRECT3DVERTEXBUFFER9 pVertexBuffer = NULL; LPD3DXMESH pMesh = NULL; LPD3DXPMESH pPMesh = NULL; LPD3DXMESH pTempMesh; LPD3DXBUFFER pD3DXMtrlBuffer = NULL; void* pVertices; TCHAR strMediaPath[512]; HRESULT hr; DWORD dw32BitFlag; DWORD cVerticesMin; DWORD cVerticesMax; DWORD iPMesh; D3DXWELDEPSILONS Epsilons; DWORD i; D3DXMATERIAL* d3dxMaterials; // Find the path to the mesh if( FAILED( DXUtil_FindMediaFileCb( strMediaPath, sizeof(strMediaPath), m_strMeshFilename ) ) ) return E_FAIL;//D3DAPPERR_MEDIANOTFOUND; // Load the mesh from the specified file if( FAILED( hr = D3DXLoadMeshFromX( strMediaPath, D3DXMESH_MANAGED, m_pd3dDevice, &pAdjacencyBuffer, &pD3DXMtrlBuffer, NULL, &m_dwNumMaterials, &pMesh ) ) ) { // hide error so that device changes will not cause exit, shows blank screen instead goto End; } dw32BitFlag = (pMesh->GetOptions() & D3DXMESH_32BIT); // perform simple cleansing operations on mesh if( FAILED( hr = D3DXCleanMesh( pMesh, (DWORD*)pAdjacencyBuffer->GetBufferPointer(), &pTempMesh, (DWORD*)pAdjacencyBuffer->GetBufferPointer(), NULL ) ) ) { m_dwNumMaterials = 0; goto End; } SAFE_RELEASE(pMesh); pMesh = pTempMesh; // Perform a weld to try and remove excess vertices like the model bigship1.x in the DX9.0 SDK (current model is fixed) // Weld the mesh using all epsilons of 0.0f. A small epsilon like 1e-6 works well too memset(&Epsilons, 0, sizeof(D3DXWELDEPSILONS)); if( FAILED( hr = D3DXWeldVertices( pMesh, 0, &Epsilons, (DWORD*)pAdjacencyBuffer->GetBufferPointer(), (DWORD*)pAdjacencyBuffer->GetBufferPointer(), NULL, NULL ) ) ) { m_dwNumMaterials = 0; goto End; } // verify validity of mesh for simplification if( FAILED( hr = D3DXValidMesh( pMesh, (DWORD*)pAdjacencyBuffer->GetBufferPointer(), NULL ) ) ) { m_dwNumMaterials = 0; goto End; } // Allocate a material/texture arrays d3dxMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer(); m_mtrlMeshMaterials = new D3DMATERIAL9[m_dwNumMaterials]; m_pMeshTextures = new LPDIRECT3DTEXTURE9[m_dwNumMaterials]; // Copy the materials and load the textures for( i=0; i<m_dwNumMaterials; i++ ) { m_mtrlMeshMaterials[i] = d3dxMaterials[i].MatD3D; m_mtrlMeshMaterials[i].Ambient = m_mtrlMeshMaterials[i].Diffuse; // Find the path to the texture and create that texture DXUtil_FindMediaFileCb( strMediaPath, sizeof(strMediaPath), d3dxMaterials[i].pTextureFilename ); if( FAILED( D3DXCreateTextureFromFile( m_pd3dDevice, strMediaPath, &m_pMeshTextures[i] ) ) ) m_pMeshTextures[i] = NULL; } pD3DXMtrlBuffer->Release(); pD3DXMtrlBuffer = NULL; // Lock the vertex buffer, to generate a simple bounding sphere hr = pMesh->GetVertexBuffer( &pVertexBuffer ); if( FAILED(hr) ) goto End; hr = pVertexBuffer->Lock( 0, 0, &pVertices, D3DLOCK_NOSYSLOCK ); if( FAILED(hr) ) goto End; hr = D3DXComputeBoundingSphere( (D3DXVECTOR3*)pVertices, pMesh->GetNumVertices(), D3DXGetFVFVertexSize(pMesh->GetFVF()), &m_vObjectCenter, &m_fObjectRadius ); pVertexBuffer->Unlock(); pVertexBuffer->Release(); if( FAILED(hr) || m_dwNumMaterials == 0 ) goto End; if ( !(pMesh->GetFVF() & D3DFVF_NORMAL) ) { hr = pMesh->CloneMeshFVF( dw32BitFlag|D3DXMESH_MANAGED, pMesh->GetFVF() | D3DFVF_NORMAL, m_pd3dDevice, &pTempMesh ); if (FAILED(hr)) goto End; D3DXComputeNormals( pTempMesh, NULL ); pMesh->Release(); pMesh = pTempMesh; } hr = D3DXGeneratePMesh( pMesh, (DWORD*)pAdjacencyBuffer->GetBufferPointer(), NULL, NULL, 1, D3DXMESHSIMP_VERTEX, &pPMesh); if( FAILED(hr) ) goto End; cVerticesMin = pPMesh->GetMinVertices(); cVerticesMax = pPMesh->GetMaxVertices(); cVerticesPerMesh = (cVerticesMax - cVerticesMin) / 10; m_cPMeshes = max(1, (DWORD)ceil((cVerticesMax - cVerticesMin) / (float)cVerticesPerMesh)); m_pPMeshes = new LPD3DXPMESH[m_cPMeshes]; if (m_pPMeshes == NULL) { hr = E_OUTOFMEMORY; goto End; } memset(m_pPMeshes, 0, sizeof(LPD3DXPMESH) * m_cPMeshes); // clone full size pmesh hr = pPMesh->ClonePMeshFVF( D3DXMESH_MANAGED | D3DXMESH_VB_SHARE, pPMesh->GetFVF(), m_pd3dDevice, &m_pPMeshFull ); if (FAILED(hr)) goto End; // clone all the separate pmeshes for (iPMesh = 0; iPMesh < m_cPMeshes; iPMesh++) { hr = pPMesh->ClonePMeshFVF( D3DXMESH_MANAGED | D3DXMESH_VB_SHARE, pPMesh->GetFVF(), m_pd3dDevice, &m_pPMeshes[iPMesh] ); if (FAILED(hr)) goto End; // trim to appropriate space hr = m_pPMeshes[iPMesh]->TrimByVertices(cVerticesMin + cVerticesPerMesh * iPMesh, cVerticesMin + cVerticesPerMesh * (iPMesh+1), NULL, NULL); if (FAILED(hr)) goto End; hr = m_pPMeshes[iPMesh]->OptimizeBaseLOD(D3DXMESHOPT_VERTEXCACHE, NULL); if (FAILED(hr)) goto End; } // set current to be maximum number of vertices m_iPMeshCur = m_cPMeshes - 1; hr = m_pPMeshes[m_iPMeshCur]->SetNumVertices(cVerticesMax); if (FAILED(hr)) goto End; hr = m_pPMeshFull->SetNumVertices(cVerticesMax); if (FAILED(hr)) goto End; End: SAFE_RELEASE( pAdjacencyBuffer ); SAFE_RELEASE( pD3DXMtrlBuffer ); SAFE_RELEASE( pMesh ); SAFE_RELEASE( pPMesh ); if (FAILED(hr)) { for (iPMesh = 0; iPMesh < m_cPMeshes; iPMesh++) { SAFE_RELEASE( m_pPMeshes[iPMesh] ); } delete []m_pPMeshes; m_cPMeshes = 0; m_pPMeshes = NULL; SAFE_RELEASE( m_pPMeshFull ) } return hr; }
void optimizePhysXMesh(int flag, IDirect3DDevice9* D3DDevice, float epsilon, std::vector<physx::PxVec3>& pxVertices, oiram::IndexBuffer& indexBuffer) { assert(D3DDevice); D3DVERTEXELEMENT9 szDecl[] = { {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, {0xFF, 0, D3DDECLTYPE_UNUSED, 0, 0, 0} }; // 创建D3D MESH LPD3DXMESH pMesh = 0; DWORD options = D3DXMESH_SYSTEMMEM | D3DXMESH_DYNAMIC; if (indexBuffer.use32BitIndices) options |= D3DXMESH_32BIT; DWORD numVertices = static_cast<DWORD>(pxVertices.size()), numFaces = numVertices / 3; HRESULT hr = D3DXCreateMesh(numFaces, numVertices, options, szDecl, D3DDevice, &pMesh); if (SUCCEEDED(hr)) { LPVOID pData = nullptr; // 填充Index Buffer if (SUCCEEDED(pMesh->LockIndexBuffer(D3DLOCK_DISCARD, &pData))) { if (indexBuffer.use32BitIndices) memcpy(pData, indexBuffer.uiIndexBuffer.data(), indexBuffer.uiIndexBuffer.size() * sizeof(physx::PxU32)); else memcpy(pData, indexBuffer.usIndexBuffer.data(), indexBuffer.usIndexBuffer.size() * sizeof(physx::PxU16)); pMesh->UnlockIndexBuffer(); } // 填充Vertex Buffer if (SUCCEEDED(pMesh->LockVertexBuffer(D3DLOCK_DISCARD, &pData))) { memcpy(pData, pxVertices.data(), pxVertices.size() * sizeof(physx::PxVec3)); pMesh->UnlockVertexBuffer(); } // 进行Mesh优化 DWORD dwFaces = pMesh->GetNumFaces(); std::vector<DWORD> szAdjacencies(dwFaces * 3); DWORD* pAdjacency = &szAdjacencies[0]; pMesh->GenerateAdjacency(epsilon, pAdjacency); // 清理mesh hr = D3DXCleanMesh(D3DXCLEAN_SIMPLIFICATION, pMesh, pAdjacency, &pMesh, pAdjacency, NULL); if (SUCCEEDED(hr)) { // 去除mesh中重复的顶点 hr = D3DXWeldVertices(pMesh, D3DXWELDEPSILONS_WELDALL, NULL, pAdjacency, pAdjacency, NULL, NULL); if (SUCCEEDED(hr)) { // 将优化后的数据写回mesh data DWORD numIndices = pMesh->GetNumFaces() * 3; indexBuffer.use32BitIndices = numIndices > 65535; if (indexBuffer.use32BitIndices) indexBuffer.uiIndexBuffer.resize(numIndices); else indexBuffer.usIndexBuffer.resize(numIndices); // 取出Index Buffer if (SUCCEEDED(pMesh->LockIndexBuffer(D3DLOCK_READONLY | D3DLOCK_DISCARD, &pData))) { if (indexBuffer.use32BitIndices) memcpy(indexBuffer.uiIndexBuffer.data(), pData, indexBuffer.uiIndexBuffer.size() * sizeof(physx::PxU32)); else memcpy(indexBuffer.usIndexBuffer.data(), pData, indexBuffer.usIndexBuffer.size() * sizeof(physx::PxU16)); pMesh->UnlockIndexBuffer(); } // 取出Vertex Buffer DWORD dwVertices = pMesh->GetNumVertices(); pxVertices.resize(dwVertices); if (SUCCEEDED(pMesh->LockVertexBuffer(D3DLOCK_READONLY | D3DLOCK_DISCARD, &pData))) { memcpy(pxVertices.data(), pData, pxVertices.size() * sizeof(physx::PxVec3)); pMesh->UnlockVertexBuffer(); } } } pMesh->Release(); } }
//------------------------------------------------------------------------------------------------ // Name: XMesh // Desc: Constructs the subset geometry for a D3DXMesh //------------------------------------------------------------------------------------------------ bool XMesh::buildGeometryFromD3DXMesh(LPD3DXMESH d3dxMesh, SubsetGeometry* subsetGeometry, DWORD subsets) { // Check parameters if (APP_ERROR(!d3dxMesh || !subsetGeometry)("Invalid parameter to XMesh::buildGeometryFromD3DXMesh")) return false; // Add a reference to the mesh to counteract freeing it at the end d3dxMesh->AddRef(); // Get the device LPDIRECT3DDEVICE9 pd3dDevice = NULL; d3dxMesh->GetDevice(&pd3dDevice); // If this mesh isn't already in the correct format, have D3D do the grunt work of // converting it. bool generate_normals = false; // Whether or not normals need to be generated for this mesh if ((d3dxMesh->GetFVF() != D3DFVF_GEOMETRYVERTEX) || (D3DFMT_GEOMETRYINDEX == D3DFMT_INDEX32) && ((d3dxMesh->GetOptions() & D3DXMESH_32BIT) == 0)) { // Holds the mesh when its converted to the correct format LPD3DXMESH pTemd3dxMesh = NULL; // Duplicate the loaded mesh into the format if (APP_ERROR(d3dxMesh->CloneMeshFVF( D3DXMESH_SYSTEMMEM | ((D3DFMT_GEOMETRYINDEX == D3DFMT_INDEX32) ? D3DXMESH_32BIT : 0), D3DFVF_GEOMETRYVERTEX, pd3dDevice, &pTemd3dxMesh)) ("XMesh couldn't convert the source geometry format")) { d3dxMesh->Release(); pd3dDevice->Release(); return false; } // Generate normals if they didn't exist generate_normals = ((d3dxMesh->GetFVF()&D3DFVF_NORMAL)!=D3DFVF_NORMAL && (D3DFMT_GEOMETRYINDEX&D3DFVF_NORMAL)!=D3DFVF_NORMAL); // Use this mesh instead d3dxMesh->Release(); d3dxMesh = pTemd3dxMesh; } // The mesh must have its attributes sorted before it can be converted to single strips { // Allocate an adjacency buffer DWORD faces = d3dxMesh->GetNumFaces(); DWORD* pAdjacency = new DWORD[faces * 3]; bool failed = false; if (APP_ERROR(FAILED(d3dxMesh->GenerateAdjacency(ADJACENCY_EPSILON, pAdjacency)))("Unable to generate the mesh adjacency")) failed = true; { // Clean up "bowties" in the mesh that prevent lighting from being calculated correctly LPD3DXMESH cleaned_mesh = NULL; DWORD* cleaned_adjacency = new DWORD[faces * 3]; LPD3DXBUFFER errors_and_warnings = NULL; if (!failed && APP_ERROR(FAILED(D3DXCleanMesh(D3DXCLEAN_BOWTIES, d3dxMesh, pAdjacency, &cleaned_mesh, cleaned_adjacency, &errors_and_warnings))) ("Failed to clean mesh")) { failed = true; if (errors_and_warnings) { DEBUG_ERROR("Mesh cleaning error: %s", (const char*)errors_and_warnings->GetBufferPointer()); } } SAFE_RELEASE(errors_and_warnings); // If we successfully cleaned the mesh, use the new mesh and new set of // adjacencies. Otherwise, just delete anything that was allocated and // keep the original. if (failed) { SAFE_DELETE_ARRAY(cleaned_adjacency); SAFE_RELEASE(cleaned_mesh); } else { SAFE_DELETE_ARRAY(pAdjacency); SAFE_RELEASE(d3dxMesh) pAdjacency = cleaned_adjacency; d3dxMesh = cleaned_mesh; } } // Compute mesh normals, if necessary if (!failed && generate_normals && APP_ERROR(FAILED(D3DXComputeNormals(d3dxMesh, pAdjacency)))("Couldn't generate mesh normals")) { failed = true; } // Optimize the mesh if (!failed && APP_ERROR(FAILED(d3dxMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT, pAdjacency, NULL, NULL, NULL))) ("Couldn't optimize mesh attributes")) { failed = true; } // Get rid of the temporary adjacency buffer SAFE_DELETE_ARRAY(pAdjacency); // Return if there was an error if (failed) { SAFE_RELEASE(d3dxMesh); SAFE_RELEASE(pd3dDevice); return false; } } // Lock the vertex buffer GeometryVertex* pXVertices = NULL; if (APP_ERROR(d3dxMesh->LockVertexBuffer(D3DLOCK_READONLY, (VOID**)&pXVertices))("Couldn't lock source vertex buffer")) { // Erase this mesh d3dxMesh->Release(); pd3dDevice->Release(); // Failure return false; } // Iterate through all of the materials and copy vertex/index data, and assign material // information for the mesh. for (DWORD subset = 0; subset < subsets; subset++) { // Use D3DX to convert this subset into a nicely indexed form DWORD numStripIndices; LPDIRECT3DINDEXBUFFER9 pSubsetIB; if (APP_ERROR(D3DXConvertMeshSubsetToSingleStrip(d3dxMesh, subset, D3DXMESH_SYSTEMMEM, &pSubsetIB, &numStripIndices))("Couldn't convert mesh subset into indexable strip")) { // Erase any geometry we made DeallocateGeometry(subsetGeometry); // Get rid of the mesh d3dxMesh->UnlockVertexBuffer(); d3dxMesh->Release(); // Free our device pd3dDevice->Release(); // Return the error return false; } D3DINDEXBUFFER_DESC desc; GeometryIndex* pXIndices = NULL; // Check the format of the indices and lock the strip index buffer if (APP_ERROR(pSubsetIB->GetDesc(&desc))("Couldn't get .X mesh IB desc") || (desc.Format != D3DFMT_GEOMETRYINDEX) || APP_ERROR(pSubsetIB->Lock(0, 0, (VOID**)&pXIndices, D3DLOCK_READONLY))("Unable to lock the .X index buffer")) { // Erase any geometry we made DeallocateGeometry(subsetGeometry); // Get rid of the mesh pSubsetIB->Release(); d3dxMesh->UnlockVertexBuffer(); d3dxMesh->Release(); // Free our device pd3dDevice->Release(); // Error! return false; } // This table pairs an index from the .X file to an index in the buffer that // holds the vertices for this subset XIndicesTable xIndicesTable; // For each of the indices in the strip, puts its vertex ID into the indices // table. Use the counter to determine which vertex this is. { GeometryIndex vertexCounter = 0; for (DWORD e = 0; e < numStripIndices; ++e) { // Insert the entry [x-mesh index, subset index] into the table XIndicesTableInsertResult result = xIndicesTable.insert(XIndicesEntry(pXIndices[e], vertexCounter)); // If the result was successful (this isn't a duplicated X-mesh index) increment the vertex counter if (result.second) vertexCounter++; } } // Grab the number of vertices this geometry uses DWORD numVertices = (DWORD)xIndicesTable.size(); // This buffer holds all of the triangles in this subset TriangleList triangles; // This list keeps track of locations in the strip where the winding order changes. This is necessary // because this next part will remove degenerate triangles from the list. std::set<size_t> windingChanges; // Generate the list of triangles from the strip provided for (DWORD t = 0; t < numStripIndices - 2; ++t) { // Build the triangle that will be added to the buffer // CHANGED July 25, 2008: the winding order is wrong here //Triangle tri = { pXIndices[t + 0], pXIndices[t + 1], pXIndices[t + 2] }; Triangle tri = { pXIndices[t + 0], pXIndices[t + 2], pXIndices[t + 1] }; // Convert the triangle into subset-indices by using the lookup table // we generated before. tri.index[0] = xIndicesTable.find(tri.index[0])->second; tri.index[1] = xIndicesTable.find(tri.index[1])->second; tri.index[2] = xIndicesTable.find(tri.index[2])->second; // Check to make sure this triangle isn't degenerate. If it is, we can just skip // this triangle entirely to simplify the geometry. if (tri.index[0] == tri.index[1] || tri.index[1] == tri.index[2] || tri.index[0] == tri.index[2]) { // Try to find the winding in the list std::set<size_t>::iterator currentWinding = windingChanges.find(triangles.size()); // Add this to the winding change list, or remove the change if it's already there if (currentWinding != windingChanges.end()) windingChanges.erase(currentWinding); else windingChanges.insert(triangles.size()); // Don't insert a triangle here continue; } // Add this triangle to the list triangles.push_back(tri); } // Calculate the number of indices we need for the buffer DWORD numGeometryIndices = (DWORD)(triangles.size() * 3); // Allocate the destination geometry Geometry* pGeometry = NULL; if (APP_ERROR(AllocateGeometry(numVertices, numGeometryIndices, &pGeometry))("Couldn't allocate geometry")) { // Erase any geometry we made DeallocateGeometry(subsetGeometry); // Get rid of the mesh pSubsetIB->Unlock(); pSubsetIB->Release(); d3dxMesh->UnlockVertexBuffer(); d3dxMesh->Release(); // Free our device pd3dDevice->Release(); // Error! return false; } // Copy the vertices needed for this subset into the buffer GeometryVertex* pVertices = pGeometry->pVertices; for (XIndicesIterator i = xIndicesTable.begin(); i != xIndicesTable.end(); ++i) { GeometryVertex* pCurrentVertex = &pVertices[i->second]; *pCurrentVertex = pXVertices[i->first]; // Modify the vertex location to make this a unit mesh sitting on the X-Z plane pCurrentVertex->x = pCurrentVertex->x; pCurrentVertex->y = pCurrentVertex->y; pCurrentVertex->z = pCurrentVertex->z; //pVertices[i->second].color = D3DCOLOR_XRGB(255,255,255); // todo: enable color? } // Copy triangles into the indices buffer DWORD index = 0; GeometryIndex* pIndices = pGeometry->pIndices; DWORD windingOrder = 0; for (TriangleIterator t = triangles.begin(); t != triangles.end(); ++t) { // Find this index in the winding list if (windingChanges.find(index / 3) != windingChanges.end()) windingOrder = 1 - windingOrder; // Alternate the winding order so that everything shows up correctly if ((index / 3) % 2 == windingOrder) { pIndices[index + 0] = t->index[0]; pIndices[index + 1] = t->index[1]; pIndices[index + 2] = t->index[2]; } else { pIndices[index + 0] = t->index[1]; pIndices[index + 1] = t->index[0]; pIndices[index + 2] = t->index[2]; } // Increment the index counter index += 3; } // Unlock and delete strip index buffer pSubsetIB->Unlock(); pSubsetIB->Release(); // Store the buffers in the main array std::pair<SubsetGeometry::iterator,bool> result = subsetGeometry->insert(SubsetGeometry::value_type(subset, pGeometry)); if (APP_ERROR(!result.second)("Couldn't insert subset geometry into main array for .X mesh")) { // Get rid of this geometry DeallocateGeometry(pGeometry); DeallocateGeometry(subsetGeometry); // Erase the mesh d3dxMesh->UnlockVertexBuffer(); d3dxMesh->Release(); // Free our device pd3dDevice->Release(); // Return error return false; } //DEBUG_MSG("Subset %i has %i vertices %i indices (%i polygons)\n", subset, numVertices, numGeometryIndices, numGeometryIndices / 3); } // Done with the DirectX mesh. This will not erase the outside mesh. d3dxMesh->UnlockVertexBuffer(); d3dxMesh->Release(); // Free the device reference pd3dDevice->Release(); // Success return true; }