feat(physics): wire physx sdk into build
This commit is contained in:
829
engine/third_party/physx/source/geomutils/src/GuQuerySystem.cpp
vendored
Normal file
829
engine/third_party/physx/source/geomutils/src/GuQuerySystem.cpp
vendored
Normal file
@@ -0,0 +1,829 @@
|
||||
// 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);
|
||||
}
|
||||
Reference in New Issue
Block a user