/
redgpu
/
ezEngine
Обзор
Документация
Войти
/
redgpu
/
ezEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
release-21.6
Code/EnginePlugins/PhysXCooking/PhysXCooking.cpp
500 строк
13 KB
Jan Krassnigg
Allow to deactivate convex decomposition support
14 окт 2020, 08:41
14 окт 2020, 08:41
60ae054
Код
Авторство
О чём код?
#include <PhysXCookingPCH.h> #include <Core/Graphics/ConvexHull.h> #include <Foundation/Configuration/Singleton.h> #include <Foundation/Configuration/Startup.h> #include <Foundation/IO/ChunkStream.h> #include <Foundation/Math/BoundingBoxSphere.h> #include <Foundation/Time/Stopwatch.h> #include <Foundation/Utilities/Progress.h> #include <PhysXCooking/PhysXCooking.h> #include <PxPhysicsAPI.h> #include <vhacd/public/VHACD.h> using namespace VHACD; // clang-format off EZ_BEGIN_SUBSYSTEM_DECLARATION(PhysX, PhysXCooking) BEGIN_SUBSYSTEM_DEPENDENCIES "Foundation", "Core", "PhysXPlugin" END_SUBSYSTEM_DEPENDENCIES ON_CORESYSTEMS_STARTUP { ezPhysXCooking::Startup(); } ON_CORESYSTEMS_SHUTDOWN { ezPhysXCooking::Shutdown(); } ON_HIGHLEVELSYSTEMS_STARTUP { } ON_HIGHLEVELSYSTEMS_SHUTDOWN { } EZ_END_SUBSYSTEM_DECLARATION; // clang-format on PxCooking* ezPhysXCooking::s_pCooking = nullptr; ezPhysXInterface* ezPhysXCooking::s_pPhysX = nullptr; void ezPhysXCooking::Startup() { s_pPhysX = ezSingletonRegistry::GetRequiredSingletonInstance<ezPhysXInterface>(); PxCookingParams params = PxCookingParams(s_pPhysX->GetPhysXAPI()->getTolerancesScale()); s_pCooking = PxCreateCooking(PX_PHYSICS_VERSION, s_pPhysX->GetPhysXAPI()->getFoundation(), params); EZ_ASSERT_DEV(s_pCooking != nullptr, "Initializing PhysX cooking API failed"); } void ezPhysXCooking::Shutdown() { if (s_pCooking) { s_pCooking->release(); s_pCooking = nullptr; } } class ezPxOutStream : public PxOutputStream { public: ezPxOutStream(ezStreamWriter* pStream) : m_pStream(pStream) { } virtual PxU32 write(const void* src, PxU32 count) override { if (m_pStream->WriteBytes(src, count).Succeeded()) return count; return 0; } ezStreamWriter* m_pStream; }; class ezPxAllocator : public PxAllocatorCallback { public: virtual void* allocate(size_t size, const char* typeName, const char* filename, int line) override { if (size == 0) return nullptr; return new unsigned char[size]; } virtual void deallocate(void* ptr) override { if (ptr != nullptr) { delete[] static_cast<unsigned char*>(ptr); } } }; ezResult ezPhysXCooking::CookTriangleMesh(const ezPhysXCookingMesh& mesh, ezStreamWriter& OutputStream) { // ezPhysXCookingMesh mesh; // if (ComputeConvexHull(mesh0, mesh).Failed()) //{ // ezLog::Error("Convex Hull computation failed."); // return EZ_FAILURE; //} ezProgressRange range("Cooking Triangle Mesh", 2, false); PxTriangleMeshDesc desc; desc.setToDefault(); desc.materialIndices.data = mesh.m_PolygonSurfaceID.GetData(); desc.materialIndices.stride = sizeof(ezUInt16); range.BeginNextStep("Preprocessing Mesh"); ezDynamicArray<ezUInt32> TriangleIndices; CreateMeshDesc(mesh, desc, TriangleIndices); ezPxOutStream PassThroughStream(&OutputStream); range.BeginNextStep("PhysX Cooking"); if (!s_pCooking->cookTriangleMesh(desc, PassThroughStream)) return EZ_FAILURE; return EZ_SUCCESS; } ezResult ezPhysXCooking::CookConvexMesh(const ezPhysXCookingMesh& mesh0, ezStreamWriter& OutputStream) { ezProgressRange range("Cooking Convex Mesh", 2, false); range.BeginNextStep("Computing Convex Hull"); ezPhysXCookingMesh mesh; EZ_SUCCEED_OR_RETURN(ComputeConvexHull(mesh0, mesh)); range.BeginNextStep("Cooking Convex Hull"); EZ_SUCCEED_OR_RETURN(CookSingleConvexPxMesh(mesh, OutputStream)); return EZ_SUCCESS; } ezResult ezPhysXCooking::CookSingleConvexPxMesh(const ezPhysXCookingMesh& mesh, ezStreamWriter& OutputStream) { ezProgressRange range("Cooking Convex PhysX Mesh", 3, false); if (mesh.m_PolygonIndices.IsEmpty() || mesh.m_Vertices.IsEmpty()) { ezLog::Error("Convex mesh data is empty."); return EZ_FAILURE; } if (mesh.m_VerticesInPolygon.GetCount() > 255) { ezLog::Error("Cannot cook convex meshes with more than 255 polygons. This mesh has {0}.", mesh.m_VerticesInPolygon.GetCount()); return EZ_FAILURE; } PxSimpleTriangleMesh desc; desc.setToDefault(); range.BeginNextStep("Preparing Mesh Data"); ezDynamicArray<ezUInt32> TriangleIndices; CreateMeshDesc(mesh, desc, TriangleIndices); if (desc.triangles.count > 255) { ezLog::Error("Cannot cook convex meshes with more than 255 triangles. This mesh has {0}.", desc.triangles.count); return EZ_FAILURE; } range.BeginNextStep("Computing Hull Polygons"); ezPxAllocator allocator; PxU32 uiNumVertices = desc.points.count, uiNumIndices = desc.triangles.count * 3, uiNumPolygons = 0; PxVec3* pVertices = nullptr; PxU32* pIndices = nullptr; PxHullPolygon* pPolygons = nullptr; if (!s_pCooking->computeHullPolygons(desc, allocator, uiNumVertices, pVertices, uiNumIndices, pIndices, uiNumPolygons, pPolygons)) { ezLog::Error("Convex Hull computation failed"); allocator.deallocate(pVertices); allocator.deallocate(pIndices); allocator.deallocate(pPolygons); return EZ_FAILURE; } PxConvexMeshDesc convex; convex.points.count = uiNumVertices; convex.points.data = pVertices; convex.points.stride = sizeof(PxVec3); convex.indices.count = uiNumIndices; convex.indices.data = pIndices; convex.indices.stride = sizeof(PxU32); convex.polygons.count = uiNumPolygons; convex.polygons.data = pPolygons; convex.polygons.stride = sizeof(PxHullPolygon); convex.vertexLimit = 256; range.BeginNextStep("PhysX Cooking"); ezPxOutStream PassThroughStream(&OutputStream); if (!s_pCooking->cookConvexMesh(convex, PassThroughStream)) { ezLog::Warning("Convex mesh cooking failed. Trying again with inflated mesh."); convex.flags.set(PxConvexFlag::eCOMPUTE_CONVEX); if (!s_pCooking->cookConvexMesh(convex, PassThroughStream)) { allocator.deallocate(pVertices); allocator.deallocate(pIndices); allocator.deallocate(pPolygons); ezLog::Error("Convex mesh cooking failed with inflated mesh as well."); return EZ_FAILURE; } } allocator.deallocate(pVertices); allocator.deallocate(pIndices); allocator.deallocate(pPolygons); return EZ_SUCCESS; } ezResult ezPhysXCooking::ComputeConvexHull(const ezPhysXCookingMesh& mesh, ezPhysXCookingMesh& outMesh) { ezStopwatch timer; outMesh.m_bFlipNormals = mesh.m_bFlipNormals; ezConvexHullGenerator gen; if (gen.Build(mesh.m_Vertices).Failed()) { ezLog::Error("Computing the convex hull failed."); return EZ_FAILURE; } ezDynamicArray<ezConvexHullGenerator::Face> faces; gen.Retrieve(outMesh.m_Vertices, faces); if (faces.GetCount() >= 255) { ezConvexHullGenerator gen2; gen2.SetSimplificationMinTriangleAngle(ezAngle::Degree(30)); gen2.SetSimplificationFlatVertexNormalThreshold(ezAngle::Degree(10)); gen2.SetSimplificationMinTriangleEdgeLength(0.08f); if (gen2.Build(outMesh.m_Vertices).Failed()) { ezLog::Error("Computing the convex hull failed (second try)."); return EZ_FAILURE; } gen2.Retrieve(outMesh.m_Vertices, faces); } for (const auto& face : faces) { outMesh.m_VerticesInPolygon.ExpandAndGetRef() = 3; outMesh.m_PolygonSurfaceID.ExpandAndGetRef() = 0; for (int vert = 0; vert < 3; ++vert) outMesh.m_PolygonIndices.ExpandAndGetRef() = face.m_uiVertexIdx[vert]; } ezLog::Dev("Computed the convex hull in {0} milliseconds", ezArgF(timer.GetRunningTotal().GetMilliseconds(), 1)); return EZ_SUCCESS; } void ezPhysXCooking::CreateMeshDesc(const ezPhysXCookingMesh& mesh, PxSimpleTriangleMesh& desc, ezDynamicArray<ezUInt32>& TriangleIndices) { desc.setToDefault(); desc.points.count = mesh.m_Vertices.GetCount(); desc.points.stride = sizeof(ezVec3); desc.points.data = mesh.m_Vertices.GetData(); ezUInt32 uiTriangles = 0; for (auto numIndices : mesh.m_VerticesInPolygon) uiTriangles += numIndices - 2; TriangleIndices.SetCountUninitialized(uiTriangles * 3); ezUInt32 uiFirstIndex = 0; ezUInt32 uiFirstTriangleIdx = 0; for (auto numIndices : mesh.m_VerticesInPolygon) { for (ezUInt32 t = 2; t < numIndices; ++t) { TriangleIndices[uiFirstTriangleIdx + 0] = mesh.m_PolygonIndices[uiFirstIndex]; TriangleIndices[uiFirstTriangleIdx + 1] = mesh.m_PolygonIndices[uiFirstIndex + t - 1]; TriangleIndices[uiFirstTriangleIdx + 2] = mesh.m_PolygonIndices[uiFirstIndex + t]; uiFirstTriangleIdx += 3; } uiFirstIndex += numIndices; } desc.triangles.count = uiTriangles; desc.triangles.stride = sizeof(ezUInt32) * 3; desc.triangles.data = TriangleIndices.GetData(); desc.flags.set(mesh.m_bFlipNormals ? PxMeshFlag::eFLIPNORMALS : (PxMeshFlag::Enum)0); EZ_ASSERT_DEV(desc.isValid(), "PhysX PxTriangleMeshDesc is invalid"); } ezStatus ezPhysXCooking::WriteResourceToStream(ezChunkStreamWriter& stream, const ezPhysXCookingMesh& mesh, const ezArrayPtr<ezString>& surfaces, MeshType meshType, ezUInt32 uiMaxConvexPieces) { ezResult resCooking = EZ_FAILURE; { stream.BeginChunk("Surfaces", 1); stream << surfaces.GetCount(); for (const auto& slot : surfaces) { stream << slot; } stream.EndChunk(); } { stream.BeginChunk("Details", 1); ezBoundingBoxSphere aabb; aabb.SetFromPoints(mesh.m_Vertices.GetData(), mesh.m_Vertices.GetCount()); stream << aabb; stream.EndChunk(); } if (meshType == MeshType::Triangle) { stream.BeginChunk("TriangleMesh", 1); ezStopwatch timer; resCooking = ezPhysXCooking::CookTriangleMesh(mesh, stream); ezLog::Dev("Triangle Mesh Cooking time: {0}s", ezArgF(timer.GetRunningTotal().GetSeconds(), 2)); stream.EndChunk(); } else { if (meshType == MeshType::ConvexDecomposition) { #ifdef BUILDSYSTEM_ENABLE_VHACD_SUPPORT stream.BeginChunk("ConvexDecompositionMesh", 1); ezStopwatch timer; resCooking = ezPhysXCooking::CookDecomposedConvexMesh(mesh, stream, uiMaxConvexPieces); ezLog::Dev("Decomposed Convex Mesh Cooking time: {0}s", ezArgF(timer.GetRunningTotal().GetSeconds(), 2)); stream.EndChunk(); #else meshType = MeshType::ConvexHull; ezLog::SeriousWarning("Convex decomposition support is not available. Computing convex hull instead."); #endif } if (meshType == MeshType::ConvexHull) { stream.BeginChunk("ConvexMesh", 1); ezStopwatch timer; resCooking = ezPhysXCooking::CookConvexMesh(mesh, stream); ezLog::Dev("Convex Mesh Cooking time: {0}s", ezArgF(timer.GetRunningTotal().GetSeconds(), 2)); stream.EndChunk(); } } if (resCooking.Failed()) return ezStatus("Cooking the collision mesh failed."); return ezStatus(EZ_SUCCESS); } #ifdef BUILDSYSTEM_ENABLE_VHACD_SUPPORT ezResult ezPhysXCooking::CookDecomposedConvexMesh(const ezPhysXCookingMesh& mesh, ezStreamWriter& OutputStream, ezUInt32 uiMaxConvexPieces) { EZ_LOG_BLOCK("Decomposing Mesh"); IVHACD* pConDec = CreateVHACD(); IVHACD::Parameters params; params.Init(); params.m_maxConvexHulls = ezMath::Max(1u, uiMaxConvexPieces); if (uiMaxConvexPieces <= 2) { params.m_resolution = 10 * 10 * 10; } else if (uiMaxConvexPieces <= 5) { params.m_resolution = 20 * 20 * 20; } else if (uiMaxConvexPieces <= 10) { params.m_resolution = 40 * 40 * 40; } else if (uiMaxConvexPieces <= 25) { params.m_resolution = 60 * 60 * 60; } else if (uiMaxConvexPieces <= 50) { params.m_resolution = 80 * 80 * 80; } else { params.m_resolution = 100 * 100 * 100; } if (!pConDec->Compute(mesh.m_Vertices.GetData()->GetData(), mesh.m_Vertices.GetCount(), mesh.m_PolygonIndices.GetData(), mesh.m_VerticesInPolygon.GetCount(), params)) { ezLog::Error("Failed to compute convex decomposition"); return EZ_FAILURE; } ezUInt16 uiNumParts = 0; for (ezUInt32 i = 0; i < pConDec->GetNConvexHulls(); ++i) { IVHACD::ConvexHull ch; pConDec->GetConvexHull(i, ch); if (ch.m_nTriangles == 0) continue; ++uiNumParts; } ezLog::Dev("Convex mesh parts: {}", uiNumParts); OutputStream << uiNumParts; for (ezUInt32 i = 0; i < pConDec->GetNConvexHulls(); ++i) { IVHACD::ConvexHull ch; pConDec->GetConvexHull(i, ch); if (ch.m_nTriangles == 0) continue; ezPhysXCookingMesh chm; chm.m_Vertices.SetCount(ch.m_nPoints); for (ezUInt32 v = 0; v < ch.m_nPoints; ++v) { chm.m_Vertices[v].Set((float)ch.m_points[v * 3 + 0], (float)ch.m_points[v * 3 + 1], (float)ch.m_points[v * 3 + 2]); } chm.m_VerticesInPolygon.SetCount(ch.m_nTriangles); chm.m_PolygonSurfaceID.SetCount(ch.m_nTriangles); chm.m_PolygonIndices.SetCount(ch.m_nTriangles * 3); for (ezUInt32 t = 0; t < ch.m_nTriangles; ++t) { chm.m_VerticesInPolygon[t] = 3; chm.m_PolygonSurfaceID[t] = 0; chm.m_PolygonIndices[t * 3 + 0] = ch.m_triangles[t * 3 + 0]; chm.m_PolygonIndices[t * 3 + 1] = ch.m_triangles[t * 3 + 1]; chm.m_PolygonIndices[t * 3 + 2] = ch.m_triangles[t * 3 + 2]; } EZ_SUCCEED_OR_RETURN(CookSingleConvexPxMesh(chm, OutputStream)); } return EZ_SUCCESS; } #endif EZ_STATICLINK_FILE(PhysXCooking, PhysXCooking_PhysXCooking);