bool GameObject::CreateTriangleMesh( CPhysX const * pPhysX ) { bool bResult = false; if( pPhysX && pPhysX->GetPhysics() && !m_BufVertices.empty() && !m_BufIndices.empty() ) { uint NumVerticies = m_BufVertices.size() / 3; uint NumTriangles = m_BufIndices.size() / 3; //Create pointer for vertices physx::PxVec3* verts = new physx::PxVec3[ NumVerticies ]; int ii = -1; for( uint i = 0; i < NumVerticies; ++i ) { ++ii; verts[ i ].x = m_BufVertices[ ii ]; verts[ i ].y = m_BufVertices[ ++ii ]; verts[ i ].z = m_BufVertices[ ++ii ]; } //Create pointer for indices physx::PxU16 *tris = new physx::PxU16[ m_BufIndices.size() ]; for( uint i = 0; i < m_BufIndices.size(); ++i ) tris[ i ] = m_BufIndices[ i ]; // Build physical model physx::PxTriangleMeshDesc TriMeshDesc; TriMeshDesc.points.count = NumVerticies; TriMeshDesc.points.stride = sizeof(physx::PxVec3); TriMeshDesc.points.data = verts; TriMeshDesc.triangles.count = NumTriangles; TriMeshDesc.triangles.stride = 3 * sizeof(physx::PxU16); TriMeshDesc.triangles.data = tris; TriMeshDesc.flags = physx::PxMeshFlag::e16_BIT_INDICES;// | physx::PxMeshFlag::eFLIPNORMALS; PxToolkit::MemoryOutputStream writeBuffer; PxCooking* pCooking = pPhysX->GetCooking(); if( pCooking && TriMeshDesc.isValid() ) if( pCooking->cookTriangleMesh( TriMeshDesc, writeBuffer ) ) { PxToolkit::MemoryInputData readBuffer( writeBuffer.getData(), writeBuffer.getSize() ); m_pTriangleMesh = pPhysX->GetPhysics()->createTriangleMesh( readBuffer ); bResult = true; } delete[] verts; delete[] tris; } return bResult; }
virtual bool CookTriMesh(FName Format, int32 RuntimeCookFlags, const TArray<FVector>& SrcVertices, const TArray<FTriIndices>& SrcIndices, const TArray<uint16>& SrcMaterialIndices, const bool FlipNormals, TArray<uint8>& OutBuffer, bool bDeformableMesh = false) const override { #if WITH_PHYSX PxPlatform::Enum PhysXFormat = PxPlatform::ePC; bool bIsPhysXFormatValid = GetPhysXFormat(Format, PhysXFormat); check(bIsPhysXFormatValid); PxTriangleMeshDesc PTriMeshDesc; PTriMeshDesc.points.data = SrcVertices.GetData(); PTriMeshDesc.points.count = SrcVertices.Num(); PTriMeshDesc.points.stride = sizeof(FVector); PTriMeshDesc.triangles.data = SrcIndices.GetData(); PTriMeshDesc.triangles.count = SrcIndices.Num(); PTriMeshDesc.triangles.stride = sizeof(FTriIndices); PTriMeshDesc.materialIndices.data = SrcMaterialIndices.GetData(); PTriMeshDesc.materialIndices.stride = sizeof(PxMaterialTableIndex); PTriMeshDesc.flags = FlipNormals ? PxMeshFlag::eFLIPNORMALS : (PxMeshFlags)0; // Set up cooking const PxCookingParams& Params = PhysXCooking->getParams(); PxCookingParams NewParams = Params; NewParams.targetPlatform = PhysXFormat; PxMeshPreprocessingFlags OldCookingFlags = NewParams.meshPreprocessParams; if (RuntimeCookFlags & ERuntimePhysxCookOptimizationFlags::SuppressFaceRemapTable) { NewParams.suppressTriangleMeshRemapTable = true; } if (bDeformableMesh) { // In the case of a deformable mesh, we have to change the cook params NewParams.meshPreprocessParams = PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH; } PhysXCooking->setParams(NewParams); // Cook TriMesh Data FPhysXOutputStream Buffer(&OutBuffer); bool Result = PhysXCooking->cookTriangleMesh(PTriMeshDesc, Buffer); if (bDeformableMesh) //restore old params { NewParams.meshPreprocessParams = OldCookingFlags; PhysXCooking->setParams(NewParams); } return Result; #else return false; #endif // WITH_PHYSX }
void ConvertFbxFile(string inFilename, string outFilename, vector<AnimClip>& animClips, CollisionGeneration generateCollision) { Mesh mesh; { //Get mesh from FileReader FbxFileReader fbxFile(inFilename); mesh = fbxFile.GetMesh(); std::cout << "\nProcessing FBX file " << inFilename << "...\n\n"; //Extract vertex attributes and skeleton mesh.ExtractData(); std::cout << "Done.\nOptimizing vertex attributes... "; //Remove duplicates and use indirect arrays mesh.Optimize(); std::cout << "Done.\nExtracting bone transforms... "; //Get bone transforms for(auto& animClip : animClips) for(auto& transformAtTime : animClip.TransformsAtTimeStamps) transformAtTime.second = mesh.GetBoneTransforms(transformAtTime.first); } std::cout << "Done.\nChecking if vertices are linked to more than 4 bones... "; //Make sure none of the vertices is skinned to more than 4 bones for(auto& elem : mesh.BlendInformation.data) while(elem.BlendIndices.size() > 4) { elem.BlendIndices.pop_back(); elem.BlendWeights.pop_back(); } std::cout << "Done.\nBuilding vertex- and indexbuffers... "; // Construct vertexbuffer/indexBuffer vector<Vertex> vertexBuffer; vector<unsigned int> indexBuffer; for(unsigned int i=0; i < mesh.Positions.indices.size(); ++i){ Vertex newVert; newVert.iPosition = mesh.Positions.indices[i]; newVert.iTexCoord = mesh.TexCoords.indices.empty() ? 0 : mesh.TexCoords.indices[i]; newVert.iNormal = mesh.Normals.indices.empty() ? 0 : mesh.Normals.indices[i]; newVert.iTangent = mesh.Tangents.indices.empty() ? 0 : mesh.Tangents.indices[i]; newVert.iBinormal = mesh.Binormals.indices.empty() ? 0 : mesh.Binormals.indices[i]; newVert.iVertexColor = mesh.Colors.indices.empty() ? 0 : mesh.Colors.indices[i]; newVert.iAnimData = mesh.BlendInformation.indices.empty() ? 0 : mesh.BlendInformation.indices[i]; auto it = find(vertexBuffer.begin(), vertexBuffer.end(), newVert); if(it==vertexBuffer.end()){ vertexBuffer.push_back(newVert); it = vertexBuffer.end() - 1; } indexBuffer.push_back(it-vertexBuffer.begin()); } std::cout << "Done.\nWriting mesh data... "; //Write a binary file containing all of the mesh & skeleton data unsigned int version=1, nrOfUVChannels=mesh.Normals.data.empty() ?0:1, vertexFormat=0; //Build vertex format vertexFormat |= mesh.Normals.data.empty() ? 0 : 1 << 0; vertexFormat |= mesh.Tangents.data.empty() ? 0 : 1 << 1; vertexFormat |= mesh.Binormals.data.empty() ? 0 : 1 << 2; vertexFormat |= mesh.Colors.data.empty() ? 0 : 1 << 3; vertexFormat |= mesh.BlendInformation.data.empty() ? 0 : 1 << 4; //Create an output file BinaryWriter oFile(outFilename + ".ttmesh"); oFile.Write<unsigned short>(version); //version number oFile.Write<unsigned char>(vertexFormat); // vertex format oFile.Write<unsigned char>(nrOfUVChannels); // nr of texcoord channels oFile.Write<unsigned int>(mesh.Positions.data.size()); // nr of positions // nr of texcoords if(nrOfUVChannels > 0) oFile.Write<unsigned int>(mesh.TexCoords.data.size()); // nr of normals if(vertexFormat & 1 << 0) oFile.Write<unsigned int>(mesh.Normals.data.size()); // nr of tangents if(vertexFormat & 1 << 1) oFile.Write<unsigned int>(mesh.Tangents.data.size()); // nr of binormals if(vertexFormat & 1 << 2) oFile.Write<unsigned int>(mesh.Binormals.data.size()); // nr of vertex colors if(vertexFormat & 1 << 3) oFile.Write<unsigned int>(mesh.Colors.data.size()); //nr of blendweights/-indices if(vertexFormat & 1 << 4) oFile.Write<unsigned int>(mesh.BlendInformation.data.size()); oFile.Write<unsigned int>(vertexBuffer.size()); // nr of vertices oFile.Write<unsigned int>(indexBuffer.size()); // nr of indices for(auto& elem : mesh.Positions.data) //positions oFile.Write<FbxVector4>(elem); for(auto& elem : mesh.TexCoords.data) //texCoords oFile.Write<FbxVector2>(FbxVector2(elem.mData[0],1-elem.mData[1])); for(auto& elem : mesh.Normals.data) //normals oFile.Write<FbxVector4>(elem); for(auto& elem : mesh.Tangents.data) //tangents oFile.Write<FbxVector4>(elem); for(auto& elem : mesh.Binormals.data) //binormals oFile.Write<FbxVector4>(elem); for(auto& elem : mesh.Colors.data) //vertex colors oFile.Write<FbxColor>(elem); for(auto& elem : mesh.BlendInformation.data){ //blend indices oFile.Write<unsigned int>(elem.BlendIndices.size() ); for(auto index : elem.BlendIndices) oFile.Write<unsigned int>(index); //blend weights for(auto weight : elem.BlendWeights) oFile.Write<float>(static_cast<float>(weight) ); } //Vertex buffer for(auto& vertex : vertexBuffer){ oFile.Write<unsigned int>(vertex.iPosition); if(nrOfUVChannels > 0) oFile.Write<unsigned int>(vertex.iTexCoord); if(vertexFormat & 1 << 0) oFile.Write<unsigned int>(vertex.iNormal); if(vertexFormat & 1 << 1) oFile.Write<unsigned int>(vertex.iTangent); if(vertexFormat & 1 << 2) oFile.Write<unsigned int>(vertex.iBinormal); if(vertexFormat & 1 << 3) oFile.Write<unsigned int>(vertex.iVertexColor); if(vertexFormat & 1 << 4) oFile.Write<unsigned int>(vertex.iAnimData); } //Index buffer for(auto index : indexBuffer) oFile.Write<unsigned int>(index); std::cout << "Done.\nWriting skeleton data... "; //Bones (#, names, bindposes) oFile.Write<unsigned int>( mesh.Skeleton.size() ); for(auto& bone : mesh.Skeleton){ oFile.Write<std::string>(bone.Name); oFile.Write<FbxAMatrix>(bone.BindPose); } std::cout << "Done.\nWriting bone animations... "; //animClips (#, name, fps, nrOfKeys, keyTime0, boneTransforms0, keyTime1, boneTransforms1...) oFile.Write<unsigned int>( animClips.size() ); for(auto& animClip : animClips){ oFile.Write<std::string>(animClip.Name); oFile.Write<float>(animClip.FramesPerSecond); oFile.Write<unsigned int>( animClip.TransformsAtTimeStamps.size() ); for(auto& animKey : animClip.TransformsAtTimeStamps){ oFile.Write<float>(static_cast<float>(animKey.first) ); for(auto& boneTransform : animKey.second) oFile.Write<FbxAMatrix>(boneTransform); } } //Check if we need to generate a collision mesh if(generateCollision == CollisionGeneration::None) { std::cout << "Done.\n\nOperation succeeded!\n\n"; return; } std::cout << "Done.\nWriting PhysX data... "; //Initialize cooker PxCookingParams params{ PxTolerancesScale() }; PxCooking* pCooker = PxCreateCooking(PX_PHYSICS_VERSION, PxGetFoundation(), params); //Build a vertex buffer for PhysX (containing only vertex positions), also copy index buffer, casting to PxU32 unsigned int nrOfVerts = mesh.Positions.data.size(); unsigned int nrOfIndices = mesh.Positions.indices.size(); PxVec3* pVertices = new PxVec3[nrOfVerts]; PxU32* pIndices = new PxU32[nrOfIndices]; for(unsigned int i=0; i<nrOfVerts; ++i){ auto pos = mesh.Positions.data[i]; pVertices[i] = PxVec3( static_cast<PxReal>(pos.mData[0]), static_cast<PxReal>(pos.mData[1]), static_cast<PxReal>(pos.mData[2]) ); } for(unsigned int i=0; i<nrOfIndices; ++i) pIndices[i] = static_cast<PxU32>(mesh.Positions.indices[i]); PxTriangleMeshDesc triMeshDesc; PxConvexMeshDesc convexMeshDesc; //Build physical model switch(generateCollision){ case CollisionGeneration::Concave: //Fill desc triMeshDesc.points.count = nrOfVerts; triMeshDesc.triangles.count = nrOfIndices / 3; triMeshDesc.points.stride = sizeof(PxVec3); triMeshDesc.triangles.stride = 3 * sizeof(PxU32); triMeshDesc.points.data = pVertices; triMeshDesc.triangles.data = pIndices; //Cook pCooker->cookTriangleMesh(triMeshDesc, UserStream((outFilename + ".ttcol").c_str(), false)); break; case CollisionGeneration::Convex: //Fill desc convexMeshDesc.points.count = nrOfVerts; convexMeshDesc.triangles.count = nrOfIndices / 3; convexMeshDesc.points.stride = sizeof(PxVec3); convexMeshDesc.triangles.stride = 3*sizeof(PxU32); convexMeshDesc.points.data = pVertices; convexMeshDesc.triangles.data = pIndices; convexMeshDesc.flags.set(PxConvexFlag::Enum::eCOMPUTE_CONVEX); //Cook pCooker->cookConvexMesh(convexMeshDesc, UserStream( (outFilename + ".ttcol").c_str(), false)); break; }; //Stop cooking pCooker->release(); std::cout << "Done.\n\nOperation succeeded!\n\n"; }
/** * Attempts to cook a triangle or convex mesh from the provided mesh data. Will log a warning and return false if it is * unable to cook the mesh. If the method returns true the resulting convex mesh will be output in the @p data buffer, * and its size in @p size. The data buffer will be allocated used the generic allocator and is up to the caller to * free it. */ bool cookMesh(const SPtr<MeshData>& meshData, PhysicsMeshType type, UINT8** data, UINT32& size) { if (meshData == nullptr) return false; PxCooking* cooking = gPhysX().getCooking(); if (cooking == nullptr) { LOGWRN("Attempting to cook a physics mesh but cooking is not enabled globally."); return false; } SPtr<VertexDataDesc> vertexDesc = meshData->getVertexDesc(); if (!vertexDesc->hasElement(VES_POSITION)) { LOGWRN("Provided PhysicsMesh mesh data has no vertex positions."); return false; } if (type == PhysicsMeshType::Convex) { if(!cookConvex(cooking, meshData, data, size)) { LOGWRN("Failed cooking a convex mesh. Perpahs it is too complex? Maximum number of convex vertices is 256."); return false; } } else { PxTriangleMeshDesc meshDesc; meshDesc.points.count = meshData->getNumVertices(); meshDesc.points.stride = vertexDesc->getVertexStride(); meshDesc.points.data = meshData->getElementData(VES_POSITION); meshDesc.triangles.count = meshData->getNumIndices() / 3; meshDesc.flags |= PxMeshFlag::eFLIPNORMALS; IndexType indexType = meshData->getIndexType(); if (indexType == IT_32BIT) { meshDesc.triangles.stride = 3 * sizeof(PxU32); meshDesc.triangles.data = meshData->getIndices32(); } else { meshDesc.triangles.stride = 3 * sizeof(PxU16); meshDesc.triangles.data = meshData->getIndices16(); meshDesc.flags |= PxMeshFlag::e16_BIT_INDICES; } PxDefaultMemoryOutputStream output; if (!cooking->cookTriangleMesh(meshDesc, output)) return false; size = output.getSize(); *data = (UINT8*)bs_alloc(size); memcpy(*data, output.getData(), size); } return true; }