// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "NpShapeManager.h" #include "NpFactory.h" #include "ScbRigidObject.h" #include "NpActor.h" #include "SqPruningStructure.h" #include "NpScene.h" #include "NpPtrTableStorageManager.h" #include "GuBounds.h" #include "CmUtils.h" using namespace physx; using namespace Sq; using namespace Gu; using namespace Cm; namespace physx { extern bool gUnifiedHeightfieldCollision; } static PX_FORCE_INLINE bool isSceneQuery(const NpShape& shape) { return shape.getFlagsFast() & PxShapeFlag::eSCENE_QUERY_SHAPE; } NpShapeManager::NpShapeManager() : mPruningStructure(NULL) { } // PX_SERIALIZATION NpShapeManager::NpShapeManager(const PxEMPTY) : mShapes (PxEmpty), mSceneQueryData (PxEmpty) { } NpShapeManager::~NpShapeManager() { PX_ASSERT(!mPruningStructure); PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); mShapes.clear(sm); mSceneQueryData.clear(sm); } void NpShapeManager::exportExtraData(PxSerializationContext& stream) { mShapes.exportExtraData(stream); mSceneQueryData.exportExtraData(stream); } void NpShapeManager::importExtraData(PxDeserializationContext& context) { mShapes.importExtraData(context); mSceneQueryData.importExtraData(context); } //~PX_SERIALIZATION void NpShapeManager::attachShape(NpShape& shape, PxRigidActor& actor) { PX_ASSERT(!mPruningStructure); PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); const PxU32 index = getNbShapes(); mShapes.add(&shape, sm); mSceneQueryData.add(reinterpret_cast(size_t(SQ_INVALID_PRUNER_DATA)), sm); NpScene* scene = NpActor::getAPIScene(actor); if(scene && isSceneQuery(shape)) setupSceneQuery(scene->getSceneQueryManagerFast(), actor, index); Scb::RigidObject& ro = static_cast(NpActor::getScbFromPxActor(actor)); ro.onShapeAttach(shape.getScbShape()); PX_ASSERT(!shape.isExclusive() || shape.getActor()==NULL); shape.onActorAttach(actor); } bool NpShapeManager::detachShape(NpShape& s, PxRigidActor& actor, bool wakeOnLostTouch) { PX_ASSERT(!mPruningStructure); const PxU32 index = mShapes.find(&s); if(index==0xffffffff) return false; NpScene* scene = NpActor::getAPIScene(actor); if(scene && isSceneQuery(s)) scene->getSceneQueryManagerFast().removePrunerShape(getPrunerData(index)); Scb::RigidObject& ro = static_cast(NpActor::getScbFromPxActor(actor)); ro.onShapeDetach(s.getScbShape(), wakeOnLostTouch, (s.getRefCount() == 1)); PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); mShapes.replaceWithLast(index, sm); mSceneQueryData.replaceWithLast(index, sm); s.onActorDetach(); return true; } void NpShapeManager::detachAll(NpScene* scene) { // assumes all SQ data has been released, which is currently the responsbility of the owning actor const PxU32 nbShapes = getNbShapes(); NpShape*const *shapes = getShapes(); if(scene) teardownAllSceneQuery(scene->getSceneQueryManagerFast()); // actor cleanup in Scb/Sc will remove any outstanding references corresponding to sim objects, so we don't need to do that here. for(PxU32 i=0;ionActorDetach(); PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); mShapes.clear(sm); mSceneQueryData.clear(sm); } PxU32 NpShapeManager::getShapes(PxShape** buffer, PxU32 bufferSize, PxU32 startIndex) const { return getArrayOfPointers(buffer, bufferSize, startIndex, getShapes(), getNbShapes()); } PxBounds3 NpShapeManager::getWorldBounds(const PxRigidActor& actor) const { PxBounds3 bounds(PxBounds3::empty()); const PxU32 nbShapes = getNbShapes(); const PxTransform actorPose = actor.getGlobalPose(); NpShape*const* PX_RESTRICT shapes = getShapes(); for(PxU32 i=0;igetScbShape().getGeometry(), actorPose * shapes[i]->getLocalPoseFast(), !gUnifiedHeightfieldCollision)); return bounds; } void NpShapeManager::clearShapesOnRelease(Scb::Scene& s, PxRigidActor& r) { PX_ASSERT(static_cast(NpActor::getScbFromPxActor(r)).isSimDisabledInternally()); const PxU32 nbShapes = getNbShapes(); NpShape*const* PX_RESTRICT shapes = getShapes(); for(PxU32 i=0;igetScbShape(); scbShape.checkUpdateOnRemove(&s); #if PX_SUPPORT_PVD s.getScenePvdClient().releasePvdInstance(&scbShape, r); #else PX_UNUSED(r); #endif } } void NpShapeManager::releaseExclusiveUserReferences() { // when the factory is torn down, release any shape owner refs that are still outstanding const PxU32 nbShapes = getNbShapes(); NpShape*const* PX_RESTRICT shapes = getShapes(); for(PxU32 i=0;iisExclusiveFast() && shapes[i]->getRefCount()>1) shapes[i]->release(); } } void NpShapeManager::setupSceneQuery(SceneQueryManager& sqManager, const PxRigidActor& actor, const NpShape& shape) { PX_ASSERT(shape.getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE); const PxU32 index = mShapes.find(&shape); PX_ASSERT(index!=0xffffffff); setupSceneQuery(sqManager, actor, index); } void NpShapeManager::teardownSceneQuery(SceneQueryManager& sqManager, const NpShape& shape) { const PxU32 index = mShapes.find(&shape); PX_ASSERT(index!=0xffffffff); teardownSceneQuery(sqManager, index); } void NpShapeManager::setupAllSceneQuery(NpScene* scene, const PxRigidActor& actor, bool hasPrunerStructure, const PxBounds3* bounds) { PX_ASSERT(scene); // shouldn't get here unless we're in a scene SceneQueryManager& sqManager = scene->getSceneQueryManagerFast(); const PxU32 nbShapes = getNbShapes(); NpShape*const *shapes = getShapes(); const PxType actorType = actor.getConcreteType(); const bool isDynamic = actorType == PxConcreteType::eRIGID_DYNAMIC || actorType == PxConcreteType::eARTICULATION_LINK; for(PxU32 i=0;i(geometry.convexMesh); const ConvexHullData& hullData = convexMesh->getHull(); const PxVec3* vertices = hullData.getHullVertices(); const PxU8* indexBuffer = hullData.getVertexData8(); const PxU32 nbPolygons = convexMesh->getNbPolygonsFast(); const PxMat44 m44(PxMat33(absPose.q) * geometry.scale.toMat33(), absPose.p); out << m44 << gCollisionShapeColor; // PT: no need to output this for each segment! for(PxU32 i=0; i(indices); ref0 = dtriangles[i*3+0]; ref1 = dtriangles[i*3+1]; ref2 = dtriangles[i*3+2]; } else { const PxU16* wtriangles = reinterpret_cast(indices); ref0 = wtriangles[i*3+0]; ref1 = wtriangles[i*3+1]; ref2 = wtriangles[i*3+2]; } wp[0] = vertices[ref0]; wp[1] = vertices[ref1]; wp[2] = vertices[ref2]; } static void getTriangle(const Gu::TriangleMesh& mesh, PxU32 i, PxVec3* wp, const PxVec3* vertices, const void* indices, const Matrix34& absPose, bool has16BitIndices) { PxVec3 localVerts[3]; getTriangle(mesh, i, localVerts, vertices, indices, has16BitIndices); wp[0] = absPose.transform(localVerts[0]); wp[1] = absPose.transform(localVerts[1]); wp[2] = absPose.transform(localVerts[2]); } static void visualizeActiveEdges(RenderOutput& out, const Gu::TriangleMesh& mesh, PxU32 nbTriangles, const PxU32* results, const Matrix34& absPose) { const PxU8* extraTrigData = mesh.getExtraTrigData(); PX_ASSERT(extraTrigData); const PxVec3* vertices = mesh.getVerticesFast(); const void* indices = mesh.getTrianglesFast(); out << PxU32(PxDebugColor::eARGB_YELLOW); // PT: no need to output this for each segment! const bool has16Bit = mesh.has16BitIndices(); for(PxU32 i=0; i(geometry.triangleMesh); const PxMat44 midt(PxIdentity); const Matrix34 absPose(PxMat33(pose.q) * geometry.scale.toMat33(), pose.p); PxU32 nbTriangles = triangleMesh->getNbTrianglesFast(); const PxU32 nbVertices = triangleMesh->getNbVerticesFast(); const PxVec3* vertices = triangleMesh->getVerticesFast(); const void* indices = triangleMesh->getTrianglesFast(); const bool has16Bit = triangleMesh->has16BitIndices(); // PT: TODO: don't render the same edge multiple times PxU32* results = NULL; if(useCullBox) { const Gu::Box worldBox( (cullbox.maximum + cullbox.minimum)*0.5f, (cullbox.maximum - cullbox.minimum)*0.5f, PxMat33(PxIdentity)); // PT: TODO: use the callback version here to avoid allocating this huge array results = reinterpret_cast(PX_ALLOC_TEMP(sizeof(PxU32)*nbTriangles, "tmp triangle indices")); LimitedResults limitedResults(results, nbTriangles, 0); Midphase::intersectBoxVsMesh(worldBox, *triangleMesh, pose, geometry.scale, &limitedResults); nbTriangles = limitedResults.mNbResults; if(visualizeShapes) { const PxU32 scolor = gCollisionShapeColor; out << midt << scolor; // PT: no need to output this for each segment! PxDebugLine* segments = out.reserveSegments(nbTriangles*3); for(PxU32 i=0; i(PX_ALLOC(sizeof(PxVec3)*nbVertices, "PxVec3")); for(PxU32 i=0;igetExtraTrigData()) visualizeActiveEdges(out, *triangleMesh, nbTriangles, results, absPose); if(results) PX_FREE(results); } static void visualizeHeightField(const PxHeightFieldGeometry& hfGeometry, RenderOutput& out, const PxTransform& absPose, const PxBounds3& cullbox, bool useCullBox) { const HeightField* heightfield = static_cast(hfGeometry.heightField); // PT: TODO: the debug viz for HFs is minimal at the moment... const PxU32 scolor = gCollisionShapeColor; const PxMat44 midt = PxMat44(PxIdentity); HeightFieldUtil hfUtil(hfGeometry); const PxU32 nbRows = heightfield->getNbRowsFast(); const PxU32 nbColumns = heightfield->getNbColumnsFast(); const PxU32 nbVerts = nbRows * nbColumns; const PxU32 nbTriangles = 2 * nbVerts; out << midt << scolor; // PT: no need to output the same matrix/color for each triangle if(useCullBox) { const PxTransform pose0((cullbox.maximum + cullbox.minimum)*0.5f); const PxBoxGeometry boxGeometry((cullbox.maximum - cullbox.minimum)*0.5f); PxU32* results = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbTriangles, "tmp triangle indices")); bool overflow = false; PxU32 nbTouchedTris = PxMeshQuery::findOverlapHeightField(boxGeometry, pose0, hfGeometry, absPose, results, nbTriangles, 0, overflow); PxDebugLine* segments = out.reserveSegments(nbTouchedTris*3); for(PxU32 i=0; iisValidTriangle(index) && heightfield->getTriangleMaterial(index) != PxHeightFieldMaterial::eHOLE) { outputTriangle(segments, currentTriangle.verts[0], currentTriangle.verts[1], currentTriangle.verts[2], scolor); segments+=3; } } PX_FREE(results); } else { // PT: transform vertices only once PxVec3* tmpVerts = reinterpret_cast(PX_ALLOC(sizeof(PxVec3)*nbVerts, "PxVec3")); // PT: TODO: optimize the following line for(PxU32 i=0;igetVertex(i))); for(PxU32 i=0; iisValidTriangle(i) && heightfield->getTriangleMaterial(i) != PxHeightFieldMaterial::eHOLE) { PxU32 vi0, vi1, vi2; heightfield->getTriangleVertexIndices(i, vi0, vi1, vi2); PxDebugLine* segments = out.reserveSegments(3); outputTriangle(segments, tmpVerts[vi0], tmpVerts[vi1], tmpVerts[vi2], scolor); } } PX_FREE(tmpVerts); } } static void visualize(const PxGeometry& geometry, RenderOutput& out, const PxTransform& absPose, const PxBounds3& cullbox, const PxReal fscale, bool visualizeShapes, bool visualizeEdges, bool useCullBox) { // triangle meshes can render active edges or face normals, but for other types we can just early out if there are no collision shapes if(!visualizeShapes && geometry.getType() != PxGeometryType::eTRIANGLEMESH) return; switch(geometry.getType()) { case PxGeometryType::eSPHERE: visualizeSphere(static_cast(geometry), out, absPose); break; case PxGeometryType::eBOX: visualizeBox(static_cast(geometry), out, absPose); break; case PxGeometryType::ePLANE: visualizePlane(static_cast(geometry), out, absPose); break; case PxGeometryType::eCAPSULE: visualizeCapsule(static_cast(geometry), out, absPose); break; case PxGeometryType::eCONVEXMESH: visualizeConvexMesh(static_cast(geometry), out, absPose); break; case PxGeometryType::eTRIANGLEMESH: visualizeTriangleMesh(static_cast(geometry), out, absPose, cullbox, fscale, visualizeShapes, visualizeEdges, useCullBox); break; case PxGeometryType::eHEIGHTFIELD: visualizeHeightField(static_cast(geometry), out, absPose, cullbox, useCullBox); break; case PxGeometryType::eINVALID: break; case PxGeometryType::eGEOMETRY_COUNT: break; } } void NpShapeManager::visualize(RenderOutput& out, NpScene* scene, const PxRigidActor& actor) { const PxReal scale = scene->getVisualizationParameter(PxVisualizationParameter::eSCALE); if(!scale) return; const PxU32 nbShapes = getNbShapes(); NpShape*const* PX_RESTRICT shapes = getShapes(); const bool visualizeCompounds = (nbShapes>1) && scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_COMPOUNDS)!=0.0f; // PT: moved all these out of the loop, no need to grab them once per shape const PxBounds3& cullbox = scene->getScene().getVisualizationCullingBox(); const bool visualizeAABBs = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_AABBS)!=0.0f; const bool visualizeShapes = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_SHAPES)!=0.0f; const bool visualizeEdges = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_EDGES)!=0.0f; const float fNormals = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_FNORMALS); const bool visualizeFNormals = fNormals!=0.0f; const bool visualizeCollision = visualizeShapes || visualizeFNormals || visualizeEdges; const bool useCullBox = !cullbox.isEmpty(); const bool needsShapeBounds0 = visualizeCompounds || (visualizeCollision && useCullBox); const PxReal collisionAxes = scale * scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_AXES); const PxReal fscale = scale * fNormals; const PxTransform actorPose = actor.getGlobalPose(); PxBounds3 compoundBounds(PxBounds3::empty()); for(PxU32 i=0;igetScbShape(); const PxTransform absPose = actorPose * scbShape.getShape2Actor(); const PxGeometry& geom = scbShape.getGeometry(); const bool shapeDebugVizEnabled = scbShape.getFlags() & PxShapeFlag::eVISUALIZATION; const bool needsShapeBounds = needsShapeBounds0 || (visualizeAABBs && shapeDebugVizEnabled); const PxBounds3 currentShapeBounds = needsShapeBounds ? Gu::computeBounds(geom, absPose, !gUnifiedHeightfieldCollision) : PxBounds3::empty(); if(shapeDebugVizEnabled) { if(visualizeAABBs) out << PxU32(PxDebugColor::eARGB_YELLOW) << PxMat44(PxIdentity) << DebugBox(currentShapeBounds); if(collisionAxes != 0.0f) out << PxMat44(absPose) << DebugBasis(PxVec3(collisionAxes), 0xcf0000, 0x00cf00, 0x0000cf); if(visualizeCollision) { if(!useCullBox || cullbox.intersects(currentShapeBounds)) ::visualize(geom, out, absPose, cullbox, fscale, visualizeShapes, visualizeEdges, useCullBox); } } if(visualizeCompounds) compoundBounds.include(currentShapeBounds); } if(visualizeCompounds && !compoundBounds.isEmpty()) out << gCollisionShapeColor << PxMat44(PxIdentity) << DebugBox(compoundBounds); } #endif // PX_ENABLE_DEBUG_VISUALIZATION