Files
XCEngine/engine/third_party/physx/source/physxextensions/src/ExtSceneQuerySystem.cpp

470 lines
20 KiB
C++

// 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<PxShape*>(payload.data[0]);
}
static PX_FORCE_INLINE PxRigidActor* getActorFromPayload(const PrunerPayload& payload)
{
return reinterpret_cast<PxRigidActor*>(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; i<numSqShapes; i++)
setPayload(payloads[i], shapes[i], &actor);
const PxU32 actorIndex = actor.getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
PX_ALLOCA(shapeHandles, PrunerData, numSqShapes);
SQ().addCompoundShape(bvh, actorIndex, actor.getGlobalPose(), shapeHandles, payloads, transforms, isDynamicActor(actor));
for(PxU32 i=0; i<numSqShapes; i++)
{
// PT: TODO: actorIndex is now redundant!
mExtAdapter.mDatabase.add(actorIndex, &actor, shapes[i], createActorShapeData(shapeHandles[i], actorIndex));
}
return PxSQCompoundHandle(actorIndex);
}
void ExternalPxSQ::removeSQCompound(PxSQCompoundHandle compoundHandle)
{
DatabaseCleaner cleaner(mExtAdapter);
SQ().removeCompoundActor(PrunerCompoundId(compoundHandle), &cleaner);
}
void ExternalPxSQ::updateSQCompound(PxSQCompoundHandle compoundHandle, const PxTransform& compoundTransform)
{
SQ().updateCompoundActor(PrunerCompoundId(compoundHandle), compoundTransform);
}
PxSQBuildStepHandle ExternalPxSQ::prepareSceneQueryBuildStep(PxU32 prunerIndex)
{
return SQ().prepareSceneQueriesUpdate(PruningIndex::Enum(prunerIndex));
}
void ExternalPxSQ::sceneQueryBuildStep(PxSQBuildStepHandle handle)
{
SQ().sceneQueryBuildStep(handle);
}
void ExternalPxSQ::finalizeUpdates()
{
switch(mUpdateMode)
{
case PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_ENABLED: SQ().afterSync(true, true); break;
case PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_DISABLED: SQ().afterSync(true, false); break;
case PxSceneQueryUpdateMode::eBUILD_DISABLED_COMMIT_DISABLED: SQ().afterSync(false, false); break;
}
}
void ExternalPxSQ::merge(const PxPruningStructure& pxps)
{
Pruner* staticPruner = SQ().getPruner(PruningIndex::eSTATIC);
if(staticPruner)
staticPruner->merge(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;
}