// 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-2025 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 "extensions/PxSceneQuerySystemExt.h" #include "extensions/PxShapeExt.h" #include "foundation/PxAlloca.h" #include "foundation/PxHashMap.h" #include "foundation/PxUserAllocated.h" #include "geometry/PxBVH.h" #include "GuActorShapeMap.h" #include "SqQuery.h" #include "SqFactory.h" #include "PxRigidActor.h" #include "PxPruningStructure.h" #include "PxSceneDesc.h" // PT: for PxSceneLimits TODO: remove // PT: this file re-implements a scene-queries system for PxScene inside PxExtensions. All SQ-related calls from the PxScene API // will be re-routed to this class. This version re-uses the same internal code as PhysX (most notably from SqQuery.h) so there is // little differences between the internal PhysX version and this one except minor implementation details like what we store in the // PrunerPayload. The whole API is available, it uses the same number of "pruners" for the queries, etc. This is a good working // starting point for users who want to start tinkering with the system without starting from scratch. using namespace physx; using namespace Sq; using namespace Gu; #define EXT_PRUNER_EPSILON 0.005f // PT: in this external implementation we'll use Px pointers instead of Np pointers in the payload. static PX_FORCE_INLINE void setPayload(PrunerPayload& pp, const PxShape* shape, const PxRigidActor* actor) { pp.data[0] = size_t(shape); pp.data[1] = size_t(actor); } static PX_FORCE_INLINE PxShape* getShapeFromPayload(const PrunerPayload& payload) { return reinterpret_cast(payload.data[0]); } static PX_FORCE_INLINE PxRigidActor* getActorFromPayload(const PrunerPayload& payload) { return reinterpret_cast(payload.data[1]); } static PX_FORCE_INLINE bool isDynamicActor(const PxRigidActor& actor) { const PxType actorType = actor.getConcreteType(); return actorType != PxConcreteType::eRIGID_STATIC; } /////////////////////////////////////////////////////////////////////////////// static PX_FORCE_INLINE ActorShapeData createActorShapeData(PrunerData data, PrunerCompoundId id) { return (ActorShapeData(id) << 32) | ActorShapeData(data); } static PX_FORCE_INLINE PrunerData getPrunerData(ActorShapeData data) { return PrunerData(data); } static PX_FORCE_INLINE PrunerCompoundId getCompoundID(ActorShapeData data) { return PrunerCompoundId(data >> 32); } /////////////////////////////////////////////////////////////////////////////// namespace { class ExtSqAdapter : public QueryAdapter { public: ExtSqAdapter() {} virtual ~ExtSqAdapter() {} // Adapter virtual const PxGeometry& getGeometry(const PrunerPayload& payload) const; //~Adapter // QueryAdapter virtual PrunerHandle findPrunerHandle(const PxQueryCache& cache, PrunerCompoundId& compoundId, PxU32& prunerIndex) const; virtual void getFilterData(const PrunerPayload& payload, PxFilterData& filterData) const; virtual void getActorShape(const PrunerPayload& payload, PxActorShape& actorShape) const; //~QueryAdapter ActorShapeMap mDatabase; }; } const PxGeometry& ExtSqAdapter::getGeometry(const PrunerPayload& payload) const { PxShape* shape = getShapeFromPayload(payload); return shape->getGeometry(); } PrunerHandle ExtSqAdapter::findPrunerHandle(const PxQueryCache& cache, PrunerCompoundId& compoundId, PxU32& prunerIndex) const { const PxU32 actorIndex = cache.actor->getInternalActorIndex(); PX_ASSERT(actorIndex!=0xffffffff); const ActorShapeData actorShapeData = mDatabase.find(actorIndex, cache.actor, cache.shape); const PrunerData prunerData = getPrunerData(actorShapeData); compoundId = getCompoundID(actorShapeData); prunerIndex = getPrunerIndex(prunerData); return getPrunerHandle(prunerData); } void ExtSqAdapter::getFilterData(const PrunerPayload& payload, PxFilterData& filterData) const { PxShape* shape = getShapeFromPayload(payload); filterData = shape->getQueryFilterData(); } void ExtSqAdapter::getActorShape(const PrunerPayload& payload, PxActorShape& actorShape) const { actorShape.actor = getActorFromPayload(payload); actorShape.shape = getShapeFromPayload(payload); } /////////////////////////////////////////////////////////////////////////////// namespace { class ExternalPxSQ : public PxSceneQuerySystem, public PxUserAllocated { public: ExternalPxSQ(PVDCapture* pvd, PxU64 contextID, Pruner* staticPruner, Pruner* dynamicPruner, PxU32 dynamicTreeRebuildRateHint, PxSceneQueryUpdateMode::Enum mode, const PxSceneLimits& limits) : mQueries (pvd, contextID, staticPruner, dynamicPruner, dynamicTreeRebuildRateHint, EXT_PRUNER_EPSILON, limits, mExtAdapter), mUpdateMode (mode), mRefCount (1) {} virtual ~ExternalPxSQ() {} virtual void release(); virtual void acquireReference(); virtual void preallocate(PxU32 prunerIndex, PxU32 nbShapes) { SQ().preallocate(prunerIndex, nbShapes); } virtual void addSQShape( const PxRigidActor& actor, const PxShape& shape, const PxBounds3& bounds, const PxTransform& transform, const PxSQCompoundHandle* compoundHandle, bool hasPruningStructure); virtual void removeSQShape(const PxRigidActor& actor, const PxShape& shape); virtual void updateSQShape(const PxRigidActor& actor, const PxShape& shape, const PxTransform& transform); virtual PxSQCompoundHandle addSQCompound(const PxRigidActor& actor, const PxShape** shapes, const PxBVH& pxbvh, const PxTransform* transforms); virtual void removeSQCompound(PxSQCompoundHandle compoundHandle); virtual void updateSQCompound(PxSQCompoundHandle compoundHandle, const PxTransform& compoundTransform); virtual void flushUpdates() { SQ().flushUpdates(); } virtual void flushMemory() { SQ().flushMemory(); } virtual void visualize(PxU32 prunerIndex, PxRenderOutput& out) const { SQ().visualize(prunerIndex, out); } virtual void shiftOrigin(const PxVec3& shift) { SQ().shiftOrigin(shift); } virtual PxSQBuildStepHandle prepareSceneQueryBuildStep(PxU32 prunerIndex); virtual void sceneQueryBuildStep(PxSQBuildStepHandle handle); virtual void finalizeUpdates(); virtual void setDynamicTreeRebuildRateHint(PxU32 dynTreeRebuildRateHint) { SQ().setDynamicTreeRebuildRateHint(dynTreeRebuildRateHint); } virtual PxU32 getDynamicTreeRebuildRateHint() const { return SQ().getDynamicTreeRebuildRateHint(); } virtual void forceRebuildDynamicTree(PxU32 prunerIndex) { SQ().forceRebuildDynamicTree(prunerIndex); } virtual PxSceneQueryUpdateMode::Enum getUpdateMode() const { return mUpdateMode; } virtual void setUpdateMode(PxSceneQueryUpdateMode::Enum mode) { mUpdateMode = mode; } virtual PxU32 getStaticTimestamp() const { return SQ().getStaticTimestamp(); } virtual void merge(const PxPruningStructure& pxps); virtual bool raycast(const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, PxRaycastCallback& hitCall, PxHitFlags hitFlags, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, const PxQueryCache* cache, PxGeometryQueryFlags flags) const; virtual bool sweep( const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, PxSweepCallback& hitCall, PxHitFlags hitFlags, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, const PxQueryCache* cache, const PxReal inflation, PxGeometryQueryFlags flags) const; virtual bool overlap(const PxGeometry& geometry, const PxTransform& transform, PxOverlapCallback& hitCall, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, const PxQueryCache* cache, PxGeometryQueryFlags flags) const; virtual PxSQPrunerHandle getHandle(const PxRigidActor& actor, const PxShape& shape, PxU32& prunerIndex) const; virtual void sync(PxU32 prunerIndex, const PxSQPrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds, const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices); PX_FORCE_INLINE PrunerManager& SQ() { return mQueries.mSQManager; } PX_FORCE_INLINE const PrunerManager& SQ() const { return mQueries.mSQManager; } ExtSqAdapter mExtAdapter; SceneQueries mQueries; PxSceneQueryUpdateMode::Enum mUpdateMode; PxU32 mRefCount; }; } /////////////////////////////////////////////////////////////////////////////// void addExternalSQ(PxSceneQuerySystem* added); void removeExternalSQ(PxSceneQuerySystem* removed); void ExternalPxSQ::release() { mRefCount--; if(!mRefCount) { removeExternalSQ(this); PX_DELETE_THIS; } } void ExternalPxSQ::acquireReference() { mRefCount++; } void ExternalPxSQ::addSQShape(const PxRigidActor& actor, const PxShape& shape, const PxBounds3& bounds, const PxTransform& transform, const PxSQCompoundHandle* compoundHandle, bool hasPruningStructure) { PrunerPayload payload; setPayload(payload, &shape, &actor); const PrunerCompoundId cid = compoundHandle ? PrunerCompoundId(*compoundHandle) : INVALID_COMPOUND_ID; const PrunerData prunerData = SQ().addPrunerShape(payload, isDynamicActor(actor), cid, bounds, transform, hasPruningStructure); const PxU32 actorIndex = actor.getInternalActorIndex(); PX_ASSERT(actorIndex!=0xffffffff); mExtAdapter.mDatabase.add(actorIndex, &actor, &shape, createActorShapeData(prunerData, cid)); } namespace { struct DatabaseCleaner : PrunerPayloadRemovalCallback { DatabaseCleaner(ExtSqAdapter& adapter) : mAdapter(adapter){} virtual void invoke(PxU32 nbRemoved, const PrunerPayload* removed) PX_OVERRIDE PX_FINAL { PxU32 actorIndex = 0xffffffff; const PxRigidActor* cachedActor = NULL; while(nbRemoved--) { const PrunerPayload& payload = *removed++; const PxRigidActor* actor = getActorFromPayload(payload); if(actor!=cachedActor) { actorIndex = actor->getInternalActorIndex(); cachedActor = actor; } PX_ASSERT(actorIndex!=0xffffffff); bool status = mAdapter.mDatabase.remove(actorIndex, actor, getShapeFromPayload(payload), NULL); PX_ASSERT(status); PX_UNUSED(status); } } ExtSqAdapter& mAdapter; PX_NOCOPY(DatabaseCleaner) }; } void ExternalPxSQ::removeSQShape(const PxRigidActor& actor, const PxShape& shape) { const PxU32 actorIndex = actor.getInternalActorIndex(); PX_ASSERT(actorIndex!=0xffffffff); ActorShapeData actorShapeData; mExtAdapter.mDatabase.remove(actorIndex, &actor, &shape, &actorShapeData); const PrunerData data = getPrunerData(actorShapeData); const PrunerCompoundId compoundId = getCompoundID(actorShapeData); SQ().removePrunerShape(compoundId, data, NULL); } void ExternalPxSQ::updateSQShape(const PxRigidActor& actor, const PxShape& shape, const PxTransform& transform) { const PxU32 actorIndex = actor.getInternalActorIndex(); PX_ASSERT(actorIndex!=0xffffffff); const ActorShapeData actorShapeData = mExtAdapter.mDatabase.find(actorIndex, &actor, &shape); const PrunerData shapeHandle = getPrunerData(actorShapeData); const PrunerCompoundId cid = getCompoundID(actorShapeData); SQ().markForUpdate(cid, shapeHandle, transform); } PxSQCompoundHandle ExternalPxSQ::addSQCompound(const PxRigidActor& actor, const PxShape** shapes, const PxBVH& bvh, const PxTransform* transforms) { const PxU32 numSqShapes = bvh.getNbBounds(); PX_ALLOCA(payloads, PrunerPayload, numSqShapes); for(PxU32 i=0; imerge(pxps.getStaticMergeData()); Pruner* dynamicPruner = SQ().getPruner(PruningIndex::eDYNAMIC); if(dynamicPruner) dynamicPruner->merge(pxps.getDynamicMergeData()); } bool ExternalPxSQ::raycast( const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, PxRaycastCallback& hitCall, PxHitFlags hitFlags, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, const PxQueryCache* cache, PxGeometryQueryFlags flags) const { return mQueries._raycast(origin, unitDir, distance, hitCall, hitFlags, filterData, filterCall, cache, flags); } bool ExternalPxSQ::sweep( const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, PxSweepCallback& hitCall, PxHitFlags hitFlags, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, const PxQueryCache* cache, const PxReal inflation, PxGeometryQueryFlags flags) const { return mQueries._sweep(geometry, pose, unitDir, distance, hitCall, hitFlags, filterData, filterCall, cache, inflation, flags); } bool ExternalPxSQ::overlap( const PxGeometry& geometry, const PxTransform& transform, PxOverlapCallback& hitCall, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, const PxQueryCache* cache, PxGeometryQueryFlags flags) const { return mQueries._overlap( geometry, transform, hitCall, filterData, filterCall, cache, flags); } PxSQPrunerHandle ExternalPxSQ::getHandle(const PxRigidActor& actor, const PxShape& shape, PxU32& prunerIndex) const { const PxU32 actorIndex = actor.getInternalActorIndex(); PX_ASSERT(actorIndex!=0xffffffff); const ActorShapeData actorShapeData = mExtAdapter.mDatabase.find(actorIndex, &actor, &shape); const PrunerData prunerData = getPrunerData(actorShapeData); prunerIndex = getPrunerIndex(prunerData); return PxSQPrunerHandle(getPrunerHandle(prunerData)); } void ExternalPxSQ::sync(PxU32 prunerIndex, const PxSQPrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds, const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices) { PX_ASSERT(prunerIndex==PruningIndex::eDYNAMIC); if(prunerIndex==PruningIndex::eDYNAMIC) SQ().sync(handles, indices, bounds, transforms, count, ignoredIndices); } /////////////////////////////////////////////////////////////////////////////// static CompanionPrunerType getCompanionType(PxDynamicTreeSecondaryPruner::Enum type) { switch(type) { case PxDynamicTreeSecondaryPruner::eNONE: return COMPANION_PRUNER_NONE; case PxDynamicTreeSecondaryPruner::eBUCKET: return COMPANION_PRUNER_BUCKET; case PxDynamicTreeSecondaryPruner::eINCREMENTAL: return COMPANION_PRUNER_INCREMENTAL; case PxDynamicTreeSecondaryPruner::eBVH: return COMPANION_PRUNER_AABB_TREE; case PxDynamicTreeSecondaryPruner::eLAST: return COMPANION_PRUNER_NONE; } return COMPANION_PRUNER_NONE; } static BVHBuildStrategy getBuildStrategy(PxBVHBuildStrategy::Enum bs) { switch(bs) { case PxBVHBuildStrategy::eFAST: return BVH_SPLATTER_POINTS; case PxBVHBuildStrategy::eDEFAULT: return BVH_SPLATTER_POINTS_SPLIT_GEOM_CENTER; case PxBVHBuildStrategy::eSAH: return BVH_SAH; case PxBVHBuildStrategy::eLAST: return BVH_SPLATTER_POINTS; } return BVH_SPLATTER_POINTS; } static Pruner* create(PxPruningStructureType::Enum type, PxU64 contextID, PxDynamicTreeSecondaryPruner::Enum secondaryType, PxBVHBuildStrategy::Enum buildStrategy, PxU32 nbObjectsPerNode) { // if(0) // return createIncrementalPruner(contextID); const CompanionPrunerType cpType = getCompanionType(secondaryType); const BVHBuildStrategy bs = getBuildStrategy(buildStrategy); Pruner* pruner = NULL; switch(type) { case PxPruningStructureType::eNONE: { pruner = createBucketPruner(contextID); break; } case PxPruningStructureType::eDYNAMIC_AABB_TREE: { pruner = createAABBPruner(contextID, true, cpType, bs, nbObjectsPerNode); break; } case PxPruningStructureType::eSTATIC_AABB_TREE: { pruner = createAABBPruner(contextID, false, cpType, bs, nbObjectsPerNode); break; } case PxPruningStructureType::eLAST: break; } return pruner; } PxSceneQuerySystem* physx::PxCreateExternalSceneQuerySystem(const PxSceneQueryDesc& desc, PxU64 contextID) { PVDCapture* pvd = NULL; Pruner* staticPruner = create(desc.staticStructure, contextID, desc.dynamicTreeSecondaryPruner, desc.staticBVHBuildStrategy, desc.staticNbObjectsPerNode); Pruner* dynamicPruner = create(desc.dynamicStructure, contextID, desc.dynamicTreeSecondaryPruner, desc.dynamicBVHBuildStrategy, desc.dynamicNbObjectsPerNode); ExternalPxSQ* pxsq = PX_NEW(ExternalPxSQ)(pvd, contextID, staticPruner, dynamicPruner, desc.dynamicTreeRebuildRateHint, desc.sceneQueryUpdateMode, PxSceneLimits()); addExternalSQ(pxsq); return pxsq; }