From a49cb6eb1fb1350bfae5d4da97d2ef20838bcf1f Mon Sep 17 00:00:00 2001 From: cjkelly1 Date: Fri, 25 Oct 2019 02:43:37 -0500 Subject: [PATCH] Backport of H'Uru/Plasma PR533 - Implement a generic hull/trimesh collider format. PhysX requires all of its actors to have cooked mesh data. This cooked data is unfortunately not compatible with other versions of PhysX outside of the minor branch. The format is also largely unknown. Therefore, it is relevant to allow CWE prps to include an engine agnostic physical format to reduce our dependencies on proprietary libraries. All PhysX blobs thankfully begin with the magic string NXS\x01, so we now abuse that by adding our own format that begins with the magic string HSP\x01. These blobs are cooked on the fly while already cooked blobs are passed directly on to PhysX. Original commit: https://github.com/H-uru/Plasma/pull/533/commits/392c5f5cade772354ff21bd8467dfd69268c2f71 author = "Adam Johnson --- .../PubUtilLib/plPhysX/plPXPhysical.cpp | 105 +++++++++++++++++- .../Plasma/PubUtilLib/plPhysX/plPXPhysical.h | 3 + .../PubUtilLib/plPhysX/plSimulationMgr.cpp | 17 ++- 3 files changed, 119 insertions(+), 6 deletions(-) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp index 654e6184..a5793e53 100644 --- a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp @@ -42,6 +42,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plPXPhysical.h" #include "NxPhysics.h" +#include "NxCooking.h" #include "hsResMgr.h" #include "hsStream.h" @@ -1060,12 +1061,10 @@ void plPXPhysical::Read(hsStream* stream, hsResMgr* mgr) } else { - // Read in the cooked mesh - plPXStream pxs(stream); if (recipe.bounds == plSimDefs::kHullBounds) - recipe.convexMesh = plSimulationMgr::GetInstance()->GetSDK()->createConvexMesh(pxs); + recipe.convexMesh = IReadHull(stream); else - recipe.triMesh = plSimulationMgr::GetInstance()->GetSDK()->createTriangleMesh(pxs); + recipe.triMesh = IReadTriMesh(stream); } Init(recipe); @@ -1123,11 +1122,107 @@ void plPXPhysical::Read(hsStream* stream, hsResMgr* mgr) fProxyGen->Init(this); } +static bool IIsNxStream(hsStream* s) +{ + char tag[4]; + s->Read(sizeof(tag), tag); + + // PhysX streams begin with the magic string "NXS\x01" + // Our hacked streams begin with the magic string "HSP\x01" + if (memcmp("HSP\x01", tag, sizeof(tag)) == 0) + return false; + + // We're not going to compare to see if it says NXS. We will let the PhysX SDK + // worry about garbage data. Just rewind the stream back so PhysX can deal with it. + s->SetPosition(s->GetPosition() - sizeof(tag)); + return true; +} + +NxConvexMesh* plPXPhysical::IReadHull(hsStream* s) +{ + if (IIsNxStream(s)) + { + plPXStream pxs(s); + return plSimulationMgr::GetInstance()->GetSDK()->createConvexMesh(pxs); + } + else + { + std::vector verts; + verts.resize(s->ReadSwap32()); + for (size_t i = 0; i < verts.size(); ++i) + verts[i].Read(s); + + // Unfortunately, the only way I know of to accomplish this is to cook to a RAM stream, + // then have PhysX read the cooked data from the RAM stream. Yes, this is very sad. + // I blame PhysX. It needs to die in a fiaaaaaaaaaaah + hsRAMStream ram; + plPXStream pxs(&ram); + + NxConvexMeshDesc desc; + desc.numVertices = verts.size(); + desc.pointStrideBytes = sizeof(hsPoint3); + desc.points = &verts[0]; + desc.flags = NX_CF_COMPUTE_CONVEX | NX_CF_USE_UNCOMPRESSED_NORMALS; + if (!NxCookConvexMesh(desc, pxs)) + { + SimLog("Failed to cook hull for '%s'", GetKeyName()); + return NULL; + } + + ram.Rewind(); + return plSimulationMgr::GetInstance()->GetSDK()->createConvexMesh(pxs); + } +} + +NxTriangleMesh* plPXPhysical::IReadTriMesh(hsStream* s) +{ + if (IIsNxStream(s)) + { + plPXStream pxs(s); + return plSimulationMgr::GetInstance()->GetSDK()->createTriangleMesh(pxs); + } + else + { + std::vector verts; + verts.resize(s->ReadSwap32()); + for (size_t i = 0; i < verts.size(); ++i) + verts[i].Read(s); + std::vector indices; + UInt32 numTriangles = s->ReadSwap32(); + indices.resize(numTriangles * 3); + for (size_t i = 0; i < indices.size(); ++i) + indices[i] = s->ReadSwap32(); + + // Unfortunately, the only way I know of to accomplish this is to cook to a RAM stream, + // then have PhysX read the cooked data from the RAM stream. Yes, this is very sad. + // I blame PhysX. It needs to die in a fiaaaaaaaaaaah + hsRAMStream ram; + plPXStream pxs(&ram); + + NxTriangleMeshDesc desc; + desc.numVertices = verts.size(); + desc.pointStrideBytes = sizeof(hsPoint3); + desc.points = &verts[0]; + desc.numTriangles = numTriangles; + desc.triangleStrideBytes = sizeof(UInt32) * 3; + desc.triangles = &indices[0]; + desc.flags = 0; + if (!NxCookTriangleMesh(desc, pxs)) + { + SimLog("Failed to cook trimesh for '%s'", GetKeyName()); + return NULL; + } + + ram.Rewind(); + return plSimulationMgr::GetInstance()->GetSDK()->createTriangleMesh(pxs); + } +} + void plPXPhysical::Write(hsStream* stream, hsResMgr* mgr) { plPhysical::Write(stream, mgr); - hsAssert(fActor, "nil actor"); + hsAssert(fActor, "nil actor"); hsAssert(fActor->getNbShapes() == 1, "Can only write actors with one shape. Writing first only."); NxShape* shape = fActor->getShapes()[0]; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h index 5dc56c79..7ae2ef2d 100644 --- a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h @@ -193,6 +193,9 @@ public: virtual hsScalar GetMass() {return fMass;} protected: + class NxConvexMesh* IReadHull(hsStream* s); + class NxTriangleMesh* IReadTriMesh(hsStream* s); + void IGetPositionSim(hsPoint3& pos) const; void IGetRotationSim(hsQuat& rot) const; void ISetPositionSim(const hsPoint3& pos); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp index 797f15b0..305f9381 100644 --- a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp @@ -42,6 +42,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plSimulationMgr.h" #include "NxPhysics.h" +#include "NxCooking.h" #include "hsTimer.h" #include "plProfile.h" @@ -367,10 +368,21 @@ bool plSimulationMgr::InitSimulation() { fSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, &gHeapAllocator, &gErrorStream); if (!fSDK) + { + fLog->AddLine("Phailed to init PhysX SDL"); return false; // client will handle this and ask user to install + } fLog = plStatusLogMgr::GetInstance().CreateStatusLog(40, "Simulation.log", plStatusLog::kFilledBackground | plStatusLog::kAlignToTop); - fLog->AddLine("Initialized simulation mgr"); + fLog->AddLine("Initialized PhysX SDK"); + + if (!NxInitCooking(NULL, &gErrorStream)) + { + fLog->AddLine("Phailed to init NxCooking"); + fSDK->release(); + fSDK = NULL; + return false; + } #ifndef PLASMA_EXTERNAL_RELEASE // If this is an internal build, enable the PhysX debugger @@ -399,7 +411,10 @@ plSimulationMgr::~plSimulationMgr() hsAssert(fScenes.empty(), "Unreleased scenes at shutdown"); if (fSDK) + { + NxCloseCooking(); fSDK->release(); + } delete fLog; fLog = nil;