Files
XCEngine/engine/third_party/physx/source/geomutils/src/GuQuerySystem.cpp

830 lines
24 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 "GuQuerySystem.h"
#include "GuBounds.h"
#include "GuBVH.h"
#include "foundation/PxAlloca.h"
#include "common/PxProfileZone.h"
using namespace physx;
using namespace Gu;
///////////////////////////////////////////////////////////////////////////////
bool contains(PxArray<PxU32>& pruners, PxU32 index)
{
const PxU32 nb = pruners.size();
for(PxU32 i=0;i<nb;i++)
{
if(pruners[i]==index)
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
QuerySystem::PrunerExt::PrunerExt(Pruner* pruner, PxU32 preallocated) : mPruner(pruner), mDirtyList("QuerySystem::PrunerExt::mDirtyList"), mNbStatic(0), mNbDynamic(0), mDirtyStatic(false)
{
if(pruner&& preallocated)
pruner->preallocate(preallocated);
}
QuerySystem::PrunerExt::~PrunerExt()
{
PX_DELETE(mPruner);
}
void QuerySystem::PrunerExt::flushMemory()
{
if(!mDirtyList.size())
mDirtyList.reset();
// PT: TODO: flush bitmap here
// PT: TODO: flush pruner here?
}
// PT: ok things became more complicated than before here. We'd like to delay the update of *both* the transform and the bounds,
// since immediately updating only one of them doesn't make much sense (it invalidates the pruner's data structure anyway). When both
// are delayed it gives users the ability to query the pruners *without* commiting the changes, i.e. they can query the old snapshot
// for as long as they please (i.e. a raycast wouldn't automatically trigger a structure update).
//
// Now the problem is that we need to store (at least) the transform until the update actually happens, and the initial code didn't
// support this. We also want to do this in an efficient way, which of course makes things more difficult.
//
// A naive version would simply use a per-pruner hashmap between the PrunerHandle and its data. Might be slower than before.
//
// Another version could build on the initial bitmap-based solution and use arrays of transforms/bounds as companions to the array
// of PrunerHandle (or we could mix all that data in a single structure). The issue with this is that two consecutive updates on the
// same object wouldn't work anymore: the second call would check the bitmap, see that the bit is set already, and skip the work.
// We'd need to update the cached data instead, i.e. we'd need a mapping between the PrunerHandle and its position in mDirtyList.
// And we don't have that.
//
// A potential way to fix this could be to allow the same PrunerHandle to appear multiple times in mDirtyList, with the assumption
// that users will not update the same object multiple times very often (...). The way it would work:
// - during "add", dirtyMap is set, handle/transform/bounds are pushed to mDirtyList.
// - during "remove", dirtyMap is reset *and that's it*. We don't bother purging mDirtyList (i.e. we kill the current O(n) search there)
// - during "process" we use dirtyMap to validate the update. If bit is cleared, ignore mDirtyList entry. Duplicate entries work as long
// as mDirtyList is processed in linear order. One issue is that the current mDirtyList is also passed to the pruner as-is for the
// update, so we'd need to rebuild a separate array for that and/or make sure all pruners accept duplicate entries in that array.
// Deep down that specific rabbit hole we'll actually find the recently discovered issue regarding the mToRefit array...
//
// Bit tricky. This is only for user-updates anyway (as opposed to sim updates) so this probably doesn't need ultimate perf? Note however
// that we "remove from dirty list" when an object is removed, which happens all the time with or without user updates (e.g. streaming etc).
static const bool gUseOldCode = false;
void QuerySystem::PrunerExt::addToDirtyList(PrunerHandle handle, PxU32 dynamic, const PxTransform& transform, const PxBounds3* userBounds)
{
PxBitMap& dirtyMap = mDirtyMap;
{
if(dirtyMap.size() <= handle)
{
PxU32 size = PxMax<PxU32>(dirtyMap.size()*2, 1024);
const PxU32 minSize = handle+1;
if(minSize>size)
size = minSize*2;
dirtyMap.resize(size);
PX_ASSERT(handle<dirtyMap.size());
PX_ASSERT(!dirtyMap.test(handle));
}
}
if(gUseOldCode)
{
if(!dirtyMap.test(handle))
{
dirtyMap.set(handle);
mDirtyList.pushBack(handle);
}
}
else
{
dirtyMap.set(handle);
mDirtyList.pushBack(handle);
Data& d = mDirtyData.insert();
d.mPose = transform;
if(userBounds)
d.mBounds = *userBounds;
else
d.mBounds.setEmpty();
}
if(!dynamic)
mDirtyStatic = true;
}
void QuerySystem::PrunerExt::removeFromDirtyList(PrunerHandle handle)
{
PxBitMap& dirtyMap = mDirtyMap;
if(gUseOldCode)
{
if(dirtyMap.boundedTest(handle))
{
dirtyMap.reset(handle);
mDirtyList.findAndReplaceWithLast(handle);
}
}
else
{
dirtyMap.boundedReset(handle);
}
// PT: if we remove the object that made us set mDirtyStatic to true, tough luck,
// we don't bother fixing that bool here. It's going to potentially cause an
// unnecessary update of the character controller's caches, which is not a big deal.
}
bool QuerySystem::PrunerExt::processDirtyList(const Adapter& adapter, float inflation)
{
const PxU32 numDirtyList = mDirtyList.size();
if(!numDirtyList)
return false;
if(gUseOldCode)
{
const PrunerHandle* const prunerHandles = mDirtyList.begin();
for(PxU32 i=0; i<numDirtyList; i++)
{
const PrunerHandle handle = prunerHandles[i];
mDirtyMap.reset(handle);
// PT: we compute the new bounds and store them directly in the pruner structure to avoid copies. We delay the updateObjects() call
// to take advantage of batching.
PrunerPayloadData payloadData;
const PrunerPayload& pp = mPruner->getPayloadData(handle, &payloadData);
computeBounds(*payloadData.mBounds, adapter.getGeometry(pp), *payloadData.mTransform, 0.0f, inflation);
}
// PT: batch update happens after the loop instead of once per loop iteration
mPruner->updateObjects(prunerHandles, numDirtyList);
mDirtyList.clear();
}
else
{
// PT: TODO: this stuff is not 100% satisfying, since we do allow the same object to be updated multiple times.
// Would be nice to revisit & improve at some point.
PrunerHandle* prunerHandles = mDirtyList.begin();
PxU32 nbValid = 0;
for(PxU32 i=0; i<numDirtyList; i++)
{
const PrunerHandle handle = prunerHandles[i];
if(mDirtyMap.test(handle))
{
// PT: we compute the new bounds and store them directly in the pruner structure to avoid copies. We delay the updateObjects() call
// to take advantage of batching.
PrunerPayloadData payloadData;
const PrunerPayload& pp = mPruner->getPayloadData(handle, &payloadData);
*payloadData.mTransform = mDirtyData[i].mPose;
if(mDirtyData[i].mBounds.isEmpty())
computeBounds(*payloadData.mBounds, adapter.getGeometry(pp), mDirtyData[i].mPose, 0.0f, inflation);
else
*payloadData.mBounds = mDirtyData[i].mBounds;
prunerHandles[nbValid++] = handle;
}
else
{
// PT: if not set, object has been added to the list then removed
}
}
// PT: batch update happens after the loop instead of once per loop iteration
mPruner->updateObjects(prunerHandles, nbValid);
// PT: have to reset the bits *after* the above loop now. Unclear if clearing the
// whole map would be faster ("it depends" I guess).
while(nbValid--)
{
const PrunerHandle handle = *prunerHandles++;
mDirtyMap.reset(handle);
}
mDirtyList.clear();
mDirtyData.clear();
}
const bool ret = mDirtyStatic;
mDirtyStatic = false;
return ret;
}
///////////////////////////////////////////////////////////////////////////////
static PX_FORCE_INLINE QuerySystem::PrunerExt* checkPrunerIndex(PxU32 prunerIndex, const PxArray<QuerySystem::PrunerExt*>& prunerExt)
{
if(prunerIndex>=prunerExt.size() || !prunerExt[prunerIndex])
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return NULL;
}
return prunerExt[prunerIndex];
}
QuerySystem::QuerySystem(PxU64 contextID, float inflation, const Adapter& adapter, bool usesTreeOfPruners) :
mAdapter (adapter),
mTreeOfPruners (NULL),
mContextID (contextID),
mStaticTimestamp (0),
mInflation (inflation),
mPrunerNeedsUpdating (false),
mTimestampNeedsUpdating (false),
mUsesTreeOfPruners (usesTreeOfPruners)
//mBatchUserUpdates (batchUserUpdates)
{
}
QuerySystem::~QuerySystem()
{
PX_DELETE(mTreeOfPruners);
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i]; // Can be NULL if the pruner has been removed
PX_DELETE(pe);
}
}
PxU32 QuerySystem::addPruner(Pruner* pruner, PxU32 preallocated)
{
PrunerExt* pe = PX_NEW(PrunerExt)(pruner, preallocated);
PxU32 prunerIndex;
if(mFreePruners.size())
{
prunerIndex = mFreePruners.popBack();
mPrunerExt[prunerIndex] = pe;
}
else
{
prunerIndex = mPrunerExt.size();
mPrunerExt.pushBack(pe);
}
return prunerIndex;
}
void QuerySystem::removePruner(PxU32 prunerIndex)
{
PrunerExt* pe = checkPrunerIndex(prunerIndex, mPrunerExt);
if(!pe)
return;
// PT: it is legal to delete a pruner that still contains objects, but we should still properly update the static timestamp.
if(pe->mNbStatic)
invalidateStaticTimestamp();
PX_DELETE(pe);
mPrunerExt[prunerIndex] = NULL;
mFreePruners.pushBack(prunerIndex);
// We don't bother searching mDirtyPruners since it's going to be cleared next frame
}
void QuerySystem::flushMemory()
{
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i]; // Can be NULL if the pruner has been removed
if(pe)
pe->flushMemory();
}
}
ActorShapeData QuerySystem::addPrunerShape(const PrunerPayload& payload, PxU32 prunerIndex, bool dynamic, const PxTransform& transform, const PxBounds3* userBounds)
{
PrunerExt* pe = checkPrunerIndex(prunerIndex, mPrunerExt);
if(!pe)
return INVALID_ACTOR_SHAPE_DATA;
mPrunerNeedsUpdating = true;
if(dynamic)
{
pe->mNbDynamic++;
}
else
{
pe->mNbStatic++;
invalidateStaticTimestamp();
}
PX_ASSERT(pe->mPruner);
const PxBounds3* boundsPtr;
PxBounds3 bounds;
if(userBounds)
{
boundsPtr = userBounds;
}
else
{
computeBounds(bounds, mAdapter.getGeometry(payload), transform, 0.0f, 1.0f + mInflation);
boundsPtr = &bounds;
}
PrunerHandle handle;
pe->mPruner->addObjects(&handle, boundsPtr, &payload, &transform, 1, false);
return createActorShapeData(createPrunerInfo(prunerIndex, dynamic), handle);
}
void QuerySystem::removePrunerShape(ActorShapeData data, PrunerPayloadRemovalCallback* removalCallback)
{
const PrunerInfo info = getPrunerInfo(data);
const PxU32 prunerIndex = getPrunerIndex(info);
PrunerExt* pe = checkPrunerIndex(prunerIndex, mPrunerExt);
if(!pe)
return;
mPrunerNeedsUpdating = true;
const PxU32 dynamic = getDynamic(info);
const PrunerHandle handle = getPrunerHandle(data);
PX_ASSERT(pe->mPruner);
if(dynamic)
{
PX_ASSERT(pe->mNbDynamic);
pe->mNbDynamic--;
}
else
{
PX_ASSERT(pe->mNbStatic);
pe->mNbStatic--;
invalidateStaticTimestamp();
}
//if(mBatchUserUpdates)
pe->removeFromDirtyList(handle);
pe->mPruner->removeObjects(&handle, 1, removalCallback);
}
void QuerySystem::updatePrunerShape(ActorShapeData data, bool immediately, const PxTransform& transform, const PxBounds3* userBounds)
{
const PrunerInfo info = getPrunerInfo(data);
const PxU32 prunerIndex = getPrunerIndex(info);
PrunerExt* pe = checkPrunerIndex(prunerIndex, mPrunerExt);
if(!pe)
return;
mPrunerNeedsUpdating = true;
const PxU32 dynamic = getDynamic(info);
const PrunerHandle handle = getPrunerHandle(data);
PX_ASSERT(pe->mPruner);
Pruner* pruner = pe->mPruner;
if(immediately)
{
if(!dynamic)
invalidateStaticTimestamp();
PrunerPayloadData payloadData;
const PrunerPayload& pp = pruner->getPayloadData(handle, &payloadData);
*payloadData.mTransform = transform;
if(userBounds)
*payloadData.mBounds = *userBounds;
else
computeBounds(*payloadData.mBounds, mAdapter.getGeometry(pp), transform, 0.0f, 1.0f + mInflation);
// PT: TODO: would it be better to pass the bounds & transform directly to this function?
pruner->updateObjects(&handle, 1);
}
else
{
// PT: we don't update the static timestamp immediately, so that users can query the
// old state of the structure without invalidating their caches. This will be resolved
// in processDirtyLists.
if(gUseOldCode)
pruner->setTransform(handle, transform);
// PT: we don't shrink mDirtyList anymore in removePrunerShape so the size of that array can be reused as
// a flag telling us whether we already encountered this pruner or not. If not, we add its index to mDirtyPruners.
// Goal is to avoid processing all pruners in processDirtyLists.
if(!pe->mDirtyList.size())
{
PX_ASSERT(!contains(mDirtyPruners, prunerIndex));
mDirtyPruners.pushBack(prunerIndex);
}
else
{
PX_ASSERT(contains(mDirtyPruners, prunerIndex));
}
pe->addToDirtyList(handle, dynamic, transform, userBounds);
}
}
const PrunerPayload& QuerySystem::getPayloadData(ActorShapeData data, PrunerPayloadData* ppd) const
{
const PrunerInfo info = getPrunerInfo(data);
const PxU32 prunerIndex = getPrunerIndex(info);
PX_ASSERT(checkPrunerIndex(prunerIndex, mPrunerExt));
const PrunerHandle handle = getPrunerHandle(data);
PX_ASSERT(mPrunerExt[prunerIndex]->mPruner);
return mPrunerExt[prunerIndex]->mPruner->getPayloadData(handle, ppd);
}
void QuerySystem::processDirtyLists()
{
PX_PROFILE_ZONE("QuerySystem.processDirtyLists", mContextID);
const PxU32 nbDirtyPruners = mDirtyPruners.size();
if(!nbDirtyPruners)
return;
// must already have acquired writer lock here
const float inflation = 1.0f + mInflation;
bool mustInvalidateStaticTimestamp = false;
for(PxU32 ii=0;ii<nbDirtyPruners;ii++)
{
const PxU32 i = mDirtyPruners[ii];
PrunerExt* pe = mPrunerExt[i]; // Can be NULL if the pruner has been removed
if(pe && pe->processDirtyList(mAdapter, inflation))
mustInvalidateStaticTimestamp = true;
}
if(mustInvalidateStaticTimestamp)
invalidateStaticTimestamp();
mDirtyPruners.clear();
}
void QuerySystem::update(bool buildStep, bool commit)
{
PX_PROFILE_ZONE("QuerySystem::update", mContextID);
if(!buildStep && !commit)
{
//mPrunerNeedsUpdating = true; // PT: removed, why was it here?
return;
}
// flush user modified objects
// if(mBatchUserUpdates)
processDirtyLists();
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i]; // Can be NULL if the pruner has been removed
if(!pe)
continue;
Pruner* pruner = pe->mPruner;
if(pruner)
{
if(buildStep && pruner->isDynamic())
static_cast<DynamicPruner*>(pruner)->buildStep(true);
if(commit)
pruner->commit();
}
}
if(commit)
{
if(mUsesTreeOfPruners)
createTreeOfPruners();
}
mPrunerNeedsUpdating = !commit;
}
void QuerySystem::commitUpdates()
{
PX_PROFILE_ZONE("QuerySystem.commitUpdates", mContextID);
if(mPrunerNeedsUpdating)
{
mSQLock.lock();
if(mPrunerNeedsUpdating)
{
//if(mBatchUserUpdates)
processDirtyLists();
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i]; // Can be NULL if the pruner has been removed
if(!pe)
continue;
Pruner* pruner = pe->mPruner;
if(pruner)
pruner->commit();
}
if(mUsesTreeOfPruners)
createTreeOfPruners();
PxMemoryBarrier();
mPrunerNeedsUpdating = false;
}
mSQLock.unlock();
}
}
PxU32 QuerySystem::startCustomBuildstep()
{
PX_PROFILE_ZONE("QuerySystem.startCustomBuildstep", mContextID);
mTimestampNeedsUpdating = false;
return mPrunerExt.size();
}
void QuerySystem::customBuildstep(PxU32 index)
{
PX_PROFILE_ZONE("QuerySystem.customBuildstep", mContextID);
PX_ASSERT(index<mPrunerExt.size());
// PT: TODO: would be better to not schedule the update of removed pruners at all
PrunerExt* pe = mPrunerExt[index]; // Can be NULL if the pruner has been removed
if(!pe)
return;
Pruner* pruner = pe->mPruner;
//void QuerySystem::processDirtyLists()
{
PX_PROFILE_ZONE("QuerySystem.processDirtyLists", mContextID);
// must already have acquired writer lock here
const float inflation = 1.0f + mInflation;
// PT: note that we don't use the mDirtyPruners array here
if(pe->processDirtyList(mAdapter, inflation))
mTimestampNeedsUpdating = true;
}
if(pruner)
{
if(pruner->isDynamic())
static_cast<DynamicPruner*>(pruner)->buildStep(true); // PT: "true" because that parameter was made for PxSceneQuerySystem::sceneQueryBuildStep(), not us
pruner->commit();
}
}
void QuerySystem::finishCustomBuildstep()
{
PX_PROFILE_ZONE("QuerySystem.finishCustomBuildstep", mContextID);
if(mUsesTreeOfPruners)
createTreeOfPruners();
mPrunerNeedsUpdating = false;
if(mTimestampNeedsUpdating)
invalidateStaticTimestamp();
mDirtyPruners.clear();
}
void QuerySystem::sync(PxU32 prunerIndex, const PrunerHandle* handles, const PxU32* boundsIndices, const PxBounds3* bounds, const PxTransform32* transforms, PxU32 count)
{
if(!count)
return;
PrunerExt* pe = checkPrunerIndex(prunerIndex, mPrunerExt);
if(!pe)
return;
Pruner* pruner = pe->mPruner;
if(pruner)
pruner->updateObjects(handles, count, mInflation, boundsIndices, bounds, transforms);
}
///////////////////////////////////////////////////////////////////////////////
namespace
{
struct LocalRaycastCB : PxBVH::RaycastCallback
{
LocalRaycastCB(const PxArray<QuerySystem::PrunerExt*>& pruners, const PrunerFilter* prunerFilter, const PxVec3& origin, const PxVec3& unitDir, PrunerRaycastCallback& cb) :
mPrunerExt(pruners), mPrunerFilter(prunerFilter), mOrigin(origin), mUnitDir(unitDir), mCB(cb) {}
virtual bool reportHit(PxU32 boundsIndex, PxReal& distance)
{
QuerySystem::PrunerExt* pe = mPrunerExt[boundsIndex]; // Can be NULL if the pruner has been removed
if(pe && (!mPrunerFilter || mPrunerFilter->processPruner(boundsIndex)))
{
Pruner* pruner = pe->mPruner;
if(!pruner->raycast(mOrigin, mUnitDir, distance, mCB))
return false;
}
return true;
}
const PxArray<QuerySystem::PrunerExt*>& mPrunerExt;
const PrunerFilter* mPrunerFilter;
const PxVec3& mOrigin;
const PxVec3& mUnitDir;
PrunerRaycastCallback& mCB;
PX_NOCOPY(LocalRaycastCB)
};
struct LocalOverlapCB : PxBVH::OverlapCallback
{
LocalOverlapCB(const PxArray<QuerySystem::PrunerExt*>& pruners, const PrunerFilter* prunerFilter, const ShapeData& queryVolume, PrunerOverlapCallback& cb) :
mPrunerExt(pruners), mPrunerFilter(prunerFilter), mQueryVolume(queryVolume), mCB(cb) {}
virtual bool reportHit(PxU32 boundsIndex)
{
QuerySystem::PrunerExt* pe = mPrunerExt[boundsIndex]; // Can be NULL if the pruner has been removed
if(pe && (!mPrunerFilter || mPrunerFilter->processPruner(boundsIndex)))
{
Pruner* pruner = pe->mPruner;
if(!pruner->overlap(mQueryVolume, mCB))
return false;
}
return true;
}
const PxArray<QuerySystem::PrunerExt*>& mPrunerExt;
const PrunerFilter* mPrunerFilter;
const ShapeData& mQueryVolume;
PrunerOverlapCallback& mCB;
PX_NOCOPY(LocalOverlapCB)
};
struct LocalSweepCB : PxBVH::RaycastCallback
{
LocalSweepCB(const PxArray<QuerySystem::PrunerExt*>& pruners, const PrunerFilter* prunerFilter, const ShapeData& queryVolume, const PxVec3& unitDir, PrunerRaycastCallback& cb) :
mPrunerExt(pruners), mPrunerFilter(prunerFilter), mQueryVolume(queryVolume), mUnitDir(unitDir), mCB(cb) {}
virtual bool reportHit(PxU32 boundsIndex, PxReal& distance)
{
QuerySystem::PrunerExt* pe = mPrunerExt[boundsIndex]; // Can be NULL if the pruner has been removed
if(pe && (!mPrunerFilter || mPrunerFilter->processPruner(boundsIndex)))
{
Pruner* pruner = pe->mPruner;
if(!pruner->sweep(mQueryVolume, mUnitDir, distance, mCB))
return false;
}
return true;
}
const PxArray<QuerySystem::PrunerExt*>& mPrunerExt;
const PrunerFilter* mPrunerFilter;
const ShapeData& mQueryVolume;
const PxVec3& mUnitDir;
PrunerRaycastCallback& mCB;
PX_NOCOPY(LocalSweepCB)
};
}
void QuerySystem::raycast(const PxVec3& origin, const PxVec3& unitDir, float& inOutDistance, PrunerRaycastCallback& cb, const PrunerFilter* prunerFilter) const
{
if(mTreeOfPruners)
{
LocalRaycastCB localCB(mPrunerExt, prunerFilter, origin, unitDir, cb);
mTreeOfPruners->raycast(origin, unitDir, inOutDistance, localCB, PxGeometryQueryFlag::Enum(0));
}
else
{
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i]; // Can be NULL if the pruner has been removed
if(!pe)
continue;
if(!prunerFilter || prunerFilter->processPruner(i))
{
Pruner* pruner = pe->mPruner;
if(!pruner->raycast(origin, unitDir, inOutDistance, cb))
return;
}
}
}
}
void QuerySystem::overlap(const ShapeData& queryVolume, PrunerOverlapCallback& cb, const PrunerFilter* prunerFilter) const
{
if(mTreeOfPruners)
{
LocalOverlapCB localCB(mPrunerExt, prunerFilter, queryVolume, cb);
mTreeOfPruners->overlap(queryVolume, localCB, PxGeometryQueryFlag::Enum(0));
}
else
{
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i]; // Can be NULL if the pruner has been removed
if(!pe)
continue;
if(!prunerFilter || prunerFilter->processPruner(i))
{
Pruner* pruner = pe->mPruner;
if(!pruner->overlap(queryVolume, cb))
return;
}
}
}
}
void QuerySystem::sweep(const ShapeData& queryVolume, const PxVec3& unitDir, float& inOutDistance, PrunerRaycastCallback& cb, const PrunerFilter* prunerFilter) const
{
if(mTreeOfPruners)
{
LocalSweepCB localCB(mPrunerExt, prunerFilter, queryVolume, unitDir, cb);
mTreeOfPruners->sweep(queryVolume, unitDir, inOutDistance, localCB, PxGeometryQueryFlag::Enum(0));
}
else
{
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i]; // Can be NULL if the pruner has been removed
if(!pe)
continue;
if(!prunerFilter || prunerFilter->processPruner(i))
{
Pruner* pruner = pe->mPruner;
if(!pruner->sweep(queryVolume, unitDir, inOutDistance, cb))
return;
}
}
}
}
void QuerySystem::createTreeOfPruners()
{
PX_PROFILE_ZONE("QuerySystem.createTreeOfPruners", mContextID);
PX_DELETE(mTreeOfPruners);
mTreeOfPruners = PX_NEW(BVH)(NULL);
const PxU32 nb = mPrunerExt.size();
PxBounds3* prunerBounds = reinterpret_cast<PxBounds3*>(PxAlloca(sizeof(PxBounds3)*(nb+1)));
PxU32 nbBounds = 0;
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
Pruner* pruner = pe->mPruner;
if(pruner)
pruner->getGlobalBounds(prunerBounds[nbBounds++]);
}
mTreeOfPruners->init(nbBounds, NULL, prunerBounds, sizeof(PxBounds3), BVH_SPLATTER_POINTS, 1, 0.01f);
}