feat(physics): wire physx sdk into build

This commit is contained in:
2026-04-15 12:22:15 +08:00
parent 5bf258df6d
commit 31f40e2cbb
2044 changed files with 752623 additions and 1 deletions

View File

@@ -0,0 +1,46 @@
// 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.
#ifndef SQ_FACTORY_H
#define SQ_FACTORY_H
#include "foundation/PxSimpleTypes.h"
#include "GuFactory.h"
#include "SqTypedef.h"
namespace physx
{
namespace Sq
{
class CompoundPruner;
CompoundPruner* createCompoundPruner(PxU64 contextID);
}
}
#endif

View File

@@ -0,0 +1,197 @@
// 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.
#ifndef SQ_MANAGER_H
#define SQ_MANAGER_H
// PT: SQ-API LEVEL 2 (Level 1 = SqPruner.h)
// PT: this file is part of a "high-level" set of files within Sq. The SqPruner API doesn't rely on them.
// PT: this should really be at Np level but moving it to Sq allows us to share it.
#include "common/PxPhysXCommonConfig.h"
#include "foundation/PxBitMap.h"
#include "foundation/PxArray.h"
#include "SqPruner.h"
#include "geometry/PxGeometryHelpers.h"
namespace physx
{
namespace Sq
{
// PrunerManager-level adapter
class Adapter
{
public:
Adapter() {}
virtual ~Adapter() {}
// Retrieves the PxGeometry associated with a given PrunerPayload. This will be called by
// the PrunerManager class when computing bounds.
virtual const PxGeometry& getGeometry(const Gu::PrunerPayload& payload) const = 0;
};
// PT: extended pruner structure. We might want to move the additional data to the pruner itself later.
struct PrunerExt : public PxUserAllocated
{
// private:
PrunerExt();
~PrunerExt();
void init(Gu::Pruner* pruner);
void flushMemory();
void preallocate(PxU32 nbShapes);
void addToDirtyList(Gu::PrunerHandle handle, bool dynamic, const PxTransform& transform);
void removeFromDirtyList(Gu::PrunerHandle handle);
bool processDirtyList(PxU32 index, const Adapter& adapter, float inflation);
// void growDirtyList(Gu::PrunerHandle handle);
PX_FORCE_INLINE Gu::Pruner* pruner() { return mPruner; }
PX_FORCE_INLINE const Gu::Pruner* pruner() const { return mPruner; }
Gu::Pruner* mPruner;
PxBitMap mDirtyMap;
PxArray<Gu::PrunerHandle> mDirtyList;
bool mDirtyStatic; // true if dirty list contains a static
PX_NOCOPY(PrunerExt)
friend class PrunerManager;
};
}
}
#include "foundation/PxHashSet.h"
namespace physx
{
namespace Sq
{
class CompoundPruner;
typedef PxPair<PrunerCompoundId, Gu::PrunerHandle> CompoundPair;
typedef PxCoalescedHashSet<CompoundPair > CompoundPrunerSet;
// AB: extended compound pruner structure, buffers compound shape changes and flushes them.
struct CompoundPrunerExt : public PxUserAllocated
{
// private:
CompoundPrunerExt();
~CompoundPrunerExt();
void flushMemory();
void preallocate(PxU32 nbShapes);
void flushShapes(const Adapter& adapter, float inflation);
void addToDirtyList(PrunerCompoundId compoundId, Gu::PrunerHandle handle, const PxTransform& transform);
void removeFromDirtyList(PrunerCompoundId compoundId, Gu::PrunerHandle handle);
PX_FORCE_INLINE const CompoundPruner* pruner() const { return mPruner; }
PX_FORCE_INLINE CompoundPruner* pruner() { return mPruner; }
CompoundPruner* mPruner;
CompoundPrunerSet mDirtyList;
PX_NOCOPY(CompoundPrunerExt)
friend class PrunerManager;
};
}
}
#include "foundation/PxMutex.h"
#include "SqPrunerData.h"
namespace physx
{
class PxRenderOutput;
class PxBVH;
class PxSceneLimits; // PT: TODO: decouple from PxSceneLimits
namespace Sq
{
class PrunerManager : public PxUserAllocated
{
public:
PrunerManager(PxU64 contextID, Gu::Pruner* staticPruner, Gu::Pruner* dynamicPruner,
PxU32 dynamicTreeRebuildRateHint, float inflation,
const PxSceneLimits& limits, const Adapter& adapter);
~PrunerManager();
PrunerData addPrunerShape(const Gu::PrunerPayload& payload, bool dynamic, PrunerCompoundId compoundId, const PxBounds3& bounds, const PxTransform& transform, bool hasPruningStructure=false);
void addCompoundShape(const PxBVH& bvh, PrunerCompoundId compoundId, const PxTransform& compoundTransform, PrunerData* prunerData, const Gu::PrunerPayload* payloads, const PxTransform* transforms, bool isDynamic);
void markForUpdate(PrunerCompoundId compoundId, PrunerData s, const PxTransform& transform);
void removePrunerShape(PrunerCompoundId compoundId, PrunerData shapeData, Gu::PrunerPayloadRemovalCallback* removalCallback);
PX_FORCE_INLINE const Gu::Pruner* getPruner(PruningIndex::Enum index) const { return mPrunerExt[index].mPruner; }
PX_FORCE_INLINE Gu::Pruner* getPruner(PruningIndex::Enum index) { return mPrunerExt[index].mPruner; }
PX_FORCE_INLINE const CompoundPruner* getCompoundPruner() const { return mCompoundPrunerExt.mPruner; }
PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; }
void preallocate(PxU32 prunerIndex, PxU32 nbShapes);
void setDynamicTreeRebuildRateHint(PxU32 dynTreeRebuildRateHint);
PX_FORCE_INLINE PxU32 getDynamicTreeRebuildRateHint() const { return mRebuildRateHint; }
void flushUpdates();
void forceRebuildDynamicTree(PxU32 prunerIndex);
void updateCompoundActor(PrunerCompoundId compoundId, const PxTransform& compoundTransform);
void removeCompoundActor(PrunerCompoundId compoundId, Gu::PrunerPayloadRemovalCallback* removalCallback);
void* prepareSceneQueriesUpdate(PruningIndex::Enum index);
void sceneQueryBuildStep(void* handle);
void sync(const Gu::PrunerHandle* handles, const PxU32* boundsIndices, const PxBounds3* bounds, const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices);
void afterSync(bool buildStep, bool commit);
void shiftOrigin(const PxVec3& shift);
void visualize(PxU32 prunerIndex, PxRenderOutput& out) const;
void flushMemory();
PX_FORCE_INLINE PxU32 getStaticTimestamp() const { return mStaticTimestamp; }
PX_FORCE_INLINE const Adapter& getAdapter() const { return mAdapter; }
private:
const Adapter& mAdapter;
PrunerExt mPrunerExt[PruningIndex::eCOUNT];
CompoundPrunerExt mCompoundPrunerExt;
const PxU64 mContextID;
PxU32 mStaticTimestamp;
PxU32 mRebuildRateHint;
const float mInflation; // SQ_PRUNER_EPSILON
PxMutex mSQLock; // to make sure only one query updates the dirty pruner structure if multiple queries run in parallel
volatile bool mPrunerNeedsUpdating;
void flushShapes();
PX_FORCE_INLINE void invalidateStaticTimestamp() { mStaticTimestamp++; }
PX_NOCOPY(PrunerManager)
};
}
}
#endif

View File

@@ -0,0 +1,188 @@
// 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.
#ifndef SQ_PRUNER_H
#define SQ_PRUNER_H
#include "foundation/PxBounds3.h"
#include "foundation/PxUserAllocated.h"
#include "foundation/PxFlags.h"
#include "GuPruner.h"
#include "SqTypedef.h"
namespace physx
{
namespace Gu
{
class BVH;
}
namespace Sq
{
/**
\brief Compound-pruner-specific flags for scene queries.
*/
struct PxCompoundPrunerQueryFlag
{
enum Enum
{
eSTATIC = (1<<0), //!< Traverse static compounds
eDYNAMIC = (1<<1), //!< Traverse dynamic compounds
};
};
/**
\brief Flags typedef for the set of bits defined in PxCompoundPrunerQueryFlag.
*/
typedef PxFlags<PxCompoundPrunerQueryFlag::Enum,PxU32> PxCompoundPrunerQueryFlags;
PX_FLAGS_OPERATORS(PxCompoundPrunerQueryFlag::Enum,PxU32)
struct CompoundPrunerRaycastCallback
{
CompoundPrunerRaycastCallback() {}
virtual ~CompoundPrunerRaycastCallback() {}
virtual bool invoke(PxReal& distance, PxU32 primIndex, const Gu::PrunerPayload* payloads, const PxTransform* transforms, const PxTransform* compoundPose) = 0;
};
struct CompoundPrunerOverlapCallback
{
CompoundPrunerOverlapCallback() {}
virtual ~CompoundPrunerOverlapCallback() {}
virtual bool invoke(PxU32 primIndex, const Gu::PrunerPayload* payloads, const PxTransform* transforms, const PxTransform* compoundPose) = 0;
};
//////////////////////////////////////////////////////////////////////////
/**
* Pruner holding compound objects
*/
//////////////////////////////////////////////////////////////////////////
class CompoundPruner : public Gu::BasePruner
{
public:
virtual ~CompoundPruner() {}
/**
\brief Adds compound to the pruner.
\param results [out] an array for resulting handles
\param bvh [in] BVH
\param compoundId [in] compound id
\param transform [in] compound transform
\param data [in] an array of object data
\return true if success, false if internal allocation failed. The first failing add results in a INVALID_PRUNERHANDLE.
Handles are usable as indices. Each handle is either be a recycled handle returned by the client via removeObjects(),
or a fresh handle that is either zero, or one greater than the last fresh handle returned.
*/
virtual bool addCompound(Gu::PrunerHandle* results, const Gu::BVH& bvh, PrunerCompoundId compoundId, const PxTransform& transform, bool isDynamic, const Gu::PrunerPayload* data, const PxTransform* transforms) = 0;
/**
Removes compound from the pruner.
\param compoundId [in] compound to remove
*/
virtual bool removeCompound(PrunerCompoundId compoundId, Gu::PrunerPayloadRemovalCallback* removalCallback) = 0;
/**
Updates compound object
\param compoundId [in] compound to update
\param transform [in] compound transformation
*/
virtual bool updateCompound(PrunerCompoundId compoundId, const PxTransform& transform) = 0;
/**
Updates object after manually updating their bounds via "getPayload" calls.
\param compoundId [in] compound that the object belongs to
\param handle [in] the object to update
*/
virtual void updateObjectAfterManualBoundsUpdates(PrunerCompoundId compoundId, const Gu::PrunerHandle handle) = 0;
/**
Removes object from compound pruner.
\param compoundId [in] compound that the object belongs to
\param handle [in] the object to remove
*/
virtual void removeObject(PrunerCompoundId compoundId, const Gu::PrunerHandle handle, Gu::PrunerPayloadRemovalCallback* removalCallback) = 0;
/**
\brief Adds object to the pruner.
\param compoundId [in] compound that the object belongs to
\param result [out] an array for resulting handles
\param bounds [in] an array of bounds. These bounds are used as-is so they should be pre-inflated if inflation is needed.
\param userData [in] an array of object data
\return true if success, false if internal allocation failed. The first failing add results in a INVALID_PRUNERHANDLE.
*/
virtual bool addObject(PrunerCompoundId compoundId, Gu::PrunerHandle& result, const PxBounds3& bounds, const Gu::PrunerPayload userData, const PxTransform& transform) = 0;
/**
* Query functions
*
* Note: return value may disappear if PrunerCallback contains the necessary information
* currently it is still used for the dynamic pruner internally (to decide if added objects must be queried)
*/
virtual bool raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, CompoundPrunerRaycastCallback&, PxCompoundPrunerQueryFlags flags) const = 0;
virtual bool overlap(const Gu::ShapeData& queryVolume, CompoundPrunerOverlapCallback&, PxCompoundPrunerQueryFlags flags) const = 0;
virtual bool sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, CompoundPrunerRaycastCallback&, PxCompoundPrunerQueryFlags flags) const = 0;
/**
\brief Retrieves the object's payload and data associated with the handle.
This function returns the payload associated with a given handle. Additionally it can return the
destination addresses for the object's bounds & transform. The user can then write the new bounds
and transform there, before eventually calling updateObjects().
\param[in] handle Object handle (initially returned by addObjects())
\param[in] compoundId The compound id
\param[out] data Optional location where to store the internal data associated with the payload.
\return The payload associated with the given handle.
*/
virtual const Gu::PrunerPayload& getPayloadData(Gu::PrunerHandle handle, PrunerCompoundId compoundId, Gu::PrunerPayloadData* data) const = 0;
/**
\brief Preallocate space
\param[in] nbEntries The number of entries to preallocate space for
*/
virtual void preallocate(PxU32 nbEntries) = 0;
// PT: beware, shape transform
virtual bool setTransform(Gu::PrunerHandle handle, PrunerCompoundId compoundId, const PxTransform& transform) = 0;
// PT: beware, actor transform
virtual const PxTransform& getTransform(PrunerCompoundId compoundId) const = 0;
virtual void visualizeEx(PxRenderOutput& out, PxU32 color, bool drawStatic, bool drawDynamic) const = 0;
};
}
}
#endif

View File

@@ -0,0 +1,60 @@
// 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.
#ifndef SQ_PRUNER_DATA_H
#define SQ_PRUNER_DATA_H
#include "SqTypedef.h"
// PT: SQ-API LEVEL 2 (Level 1 = SqPruner.h)
// PT: this file is part of a "high-level" set of files within Sq. The SqPruner API doesn't rely on them.
// PT: this should really be at Np level but moving it to Sq allows us to share it.
namespace physx
{
namespace Sq
{
struct PruningIndex
{
enum Enum
{
eSTATIC = 0, // PT: must match PX_SCENE_PRUNER_STATIC
eDYNAMIC = 1, // PT: must match PX_SCENE_PRUNER_DYNAMIC
eCOUNT = 2
};
};
PX_FORCE_INLINE PrunerData createPrunerData(PxU32 index, Gu::PrunerHandle h) { return PrunerData((h << 1) | index); }
PX_FORCE_INLINE PxU32 getPrunerIndex(PrunerData data) { return PxU32(data & 1); }
PX_FORCE_INLINE Gu::PrunerHandle getPrunerHandle(PrunerData data) { return Gu::PrunerHandle(data >> 1); }
}
}
#endif

View File

@@ -0,0 +1,143 @@
// 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.
#ifndef SQ_QUERY_H
#define SQ_QUERY_H
// PT: SQ-API LEVEL 3 (Level 1 = SqPruner.h, Level 2 = SqManager/SqPrunerData)
// PT: this file is part of a "high-level" set of files within Sq. The SqPruner API doesn't rely on them.
// PT: this should really be at Np level but moving it to Sq allows us to share it.
#include "foundation/PxSimpleTypes.h"
#include "geometry/PxGeometryQueryFlags.h"
#include "SqManager.h"
#include "PxQueryReport.h"
#include "GuCachedFuncs.h"
namespace physx
{
class PxGeometry;
struct PxQueryFilterData;
struct PxFilterData;
class PxQueryFilterCallback;
namespace Sq
{
struct MultiQueryInput;
class PVDCapture
{
public:
PVDCapture() {}
virtual ~PVDCapture() {}
virtual bool transmitSceneQueries() = 0;
virtual void raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal distance, const PxRaycastHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData, bool multipleHits) = 0;
virtual void sweep(const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, PxReal distance, const PxSweepHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData, bool multipleHits) = 0;
virtual void overlap(const PxGeometry& geometry, const PxTransform& pose, const PxOverlapHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData) = 0;
};
// SceneQueries-level adapter. Augments the PrunerManager-level adapter with functions needed to perform queries.
class QueryAdapter : public Adapter
{
public:
QueryAdapter() {}
virtual ~QueryAdapter() {}
// PT: TODO: decouple from PxQueryCache?
virtual Gu::PrunerHandle findPrunerHandle(const PxQueryCache& cache, PrunerCompoundId& compoundId, PxU32& prunerIndex) const = 0;
// PT: TODO: return reference? but this version is at least consistent with getActorShape
virtual void getFilterData(const Gu::PrunerPayload& payload, PxFilterData& filterData) const = 0;
virtual void getActorShape(const Gu::PrunerPayload& payload, PxActorShape& actorShape) const = 0;
};
}
class SceneQueries
{
PX_NOCOPY(SceneQueries)
public:
SceneQueries(Sq::PVDCapture* pvd, PxU64 contextID, Gu::Pruner* staticPruner, Gu::Pruner* dynamicPruner,
PxU32 dynamicTreeRebuildRateHint, float inflation,
const PxSceneLimits& limits, const Sq::QueryAdapter& adapter);
~SceneQueries();
PX_FORCE_INLINE Sq::PrunerManager& getPrunerManagerFast() { return mSQManager; }
PX_FORCE_INLINE const Sq::PrunerManager& getPrunerManagerFast() const { return mSQManager; }
template<typename QueryHit>
bool multiQuery(
const Sq::MultiQueryInput& in,
PxHitCallback<QueryHit>& hits, PxHitFlags hitFlags, const PxQueryCache* cache,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall) const;
bool _raycast(
const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, // Ray data
PxRaycastCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const;
bool _sweep(
const PxGeometry& geometry, const PxTransform& pose, // GeomObject data
const PxVec3& unitDir, const PxReal distance, // Ray data
PxSweepCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, const PxReal inflation, PxGeometryQueryFlags flags) const;
bool _overlap(
const PxGeometry& geometry, const PxTransform& transform, // GeomObject data
PxOverlapCallback& hitCall,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const;
PX_FORCE_INLINE PxU64 getContextId() const { return mSQManager.getContextId(); }
Sq::PrunerManager mSQManager;
public:
Gu::CachedFuncs mCachedFuncs;
Sq::PVDCapture* mPVD;
};
#if PX_SUPPORT_EXTERN_TEMPLATE
//explicit template instantiation declaration
extern template
bool SceneQueries::multiQuery<PxRaycastHit>(const Sq::MultiQueryInput&, PxHitCallback<PxRaycastHit>&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*) const;
extern template
bool SceneQueries::multiQuery<PxOverlapHit>(const Sq::MultiQueryInput&, PxHitCallback<PxOverlapHit>&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*) const;
extern template
bool SceneQueries::multiQuery<PxSweepHit>(const Sq::MultiQueryInput&, PxHitCallback<PxSweepHit>&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*) const;
#endif
}
#endif

View File

@@ -0,0 +1,47 @@
// 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.
#ifndef SQ_TYPEDEF_H
#define SQ_TYPEDEF_H
#include "foundation/PxSimpleTypes.h"
#include "GuPrunerTypedef.h"
namespace physx
{
namespace Sq
{
typedef PxU32 PrunerCompoundId;
static const PrunerCompoundId INVALID_COMPOUND_ID = 0xffffffff;
typedef PxU32 PrunerData;
#define SQ_INVALID_PRUNER_DATA 0xffffffff
}
}
#endif

View File

@@ -0,0 +1,786 @@
// 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 "SqCompoundPruner.h"
#include "GuSqInternal.h"
#include "GuIncrementalAABBTree.h"
#include "GuPruningPool.h"
#include "GuAABBTreeQuery.h"
#include "GuAABBTreeNode.h"
#include "GuSphere.h"
#include "GuBox.h"
#include "GuCapsule.h"
#include "GuBVH.h"
#include "GuQuery.h"
#include "GuInternal.h"
#include "common/PxRenderBuffer.h"
#include "common/PxRenderOutput.h"
#include "CmVisualization.h"
using namespace physx;
using namespace Gu;
using namespace Sq;
// PT: TODO: this is copied from SqBounds.h, should be either moved to Gu and shared or passed as a user parameter
#define SQ_PRUNER_EPSILON 0.005f
#define SQ_PRUNER_INFLATION (1.0f + SQ_PRUNER_EPSILON) // pruner test shape inflation (not narrow phase shape)
#define PARANOIA_CHECKS 0
///////////////////////////////////////////////////////////////////////////////////////////////
BVHCompoundPruner::BVHCompoundPruner(PxU64 contextID) : mCompoundTreePool(contextID), mDrawStatic(false), mDrawDynamic(false)
{
preallocate(32);
}
///////////////////////////////////////////////////////////////////////////////////////////////
BVHCompoundPruner::~BVHCompoundPruner()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool BVHCompoundPruner::addCompound(PrunerHandle* results, const BVH& bvh, PrunerCompoundId compoundId, const PxTransform& transform, bool isDynamic, const PrunerPayload* data, const PxTransform* transforms)
{
PX_ASSERT(bvh.getNbBounds());
const PxBounds3 compoundBounds = PxBounds3::transformFast(transform, bvh.getNodes()->mBV);
const PoolIndex poolIndex = mCompoundTreePool.addCompound(results, bvh, compoundBounds, transform, isDynamic, data, transforms);
mChangedLeaves.clear();
IncrementalAABBTreeNode* node = mMainTree.insert(poolIndex, mCompoundTreePool.getCurrentCompoundBounds(), mChangedLeaves);
updateMapping(poolIndex, node);
mActorPoolMap[compoundId] = poolIndex;
mPoolActorMap[poolIndex] = compoundId;
#if PARANOIA_CHECKS
test();
#endif
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void BVHCompoundPruner::updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node)
{
// resize mapping if needed
if(mMainTreeUpdateMap.size() <= poolIndex)
{
const PxU32 resizeSize = mMainTreeUpdateMap.size() * 2;
mMainTreeUpdateMap.resize(resizeSize);
mPoolActorMap.resize(resizeSize);
}
// if a node was split we need to update the node indices and also the sibling indices
if(!mChangedLeaves.empty())
{
if(node && node->isLeaf())
{
for(PxU32 j = 0; j < node->getNbPrimitives(); j++)
{
mMainTreeUpdateMap[node->getPrimitives(NULL)[j]] = node;
}
}
for(PxU32 i = 0; i < mChangedLeaves.size(); i++)
{
IncrementalAABBTreeNode* changedNode = mChangedLeaves[i];
PX_ASSERT(changedNode->isLeaf());
for(PxU32 j = 0; j < changedNode->getNbPrimitives(); j++)
{
mMainTreeUpdateMap[changedNode->getPrimitives(NULL)[j]] = changedNode;
}
}
}
else
{
mMainTreeUpdateMap[poolIndex] = node;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool BVHCompoundPruner::removeCompound(PrunerCompoundId compoundId, PrunerPayloadRemovalCallback* removalCallback)
{
const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId);
PX_ASSERT(poolIndexEntry);
bool isDynamic = false;
if(poolIndexEntry)
{
const PoolIndex poolIndex = poolIndexEntry->second;
CompoundTree& compoundTree = mCompoundTreePool.getCompoundTrees()[poolIndex];
isDynamic = compoundTree.mFlags & PxCompoundPrunerQueryFlag::eDYNAMIC;
const PoolIndex poolRelocatedLastIndex = mCompoundTreePool.removeCompound(poolIndex, removalCallback);
IncrementalAABBTreeNode* node = mMainTree.remove(mMainTreeUpdateMap[poolIndex], poolIndex, mCompoundTreePool.getCurrentCompoundBounds());
// if node moved to its parent
if(node && node->isLeaf())
{
for (PxU32 j = 0; j < node->getNbPrimitives(); j++)
{
const PoolIndex index = node->getPrimitives(NULL)[j];
mMainTreeUpdateMap[index] = node;
}
}
// fix indices if we made a swap
if(poolRelocatedLastIndex != poolIndex)
{
mMainTreeUpdateMap[poolIndex] = mMainTreeUpdateMap[poolRelocatedLastIndex];
mMainTree.fixupTreeIndices(mMainTreeUpdateMap[poolIndex], poolRelocatedLastIndex, poolIndex);
mActorPoolMap[mPoolActorMap[poolRelocatedLastIndex]] = poolIndex;
mPoolActorMap[poolIndex] = mPoolActorMap[poolRelocatedLastIndex];
}
mActorPoolMap.erase(compoundId);
}
#if PARANOIA_CHECKS
test();
#endif
return isDynamic;
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool BVHCompoundPruner::updateCompound(PrunerCompoundId compoundId, const PxTransform& transform)
{
const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId);
PX_ASSERT(poolIndexEntry);
bool isDynamic = false;
if(poolIndexEntry)
{
const PxU32 poolIndex = poolIndexEntry->second;
CompoundTree& compoundTree = mCompoundTreePool.getCompoundTrees()[poolIndex];
isDynamic = compoundTree.mFlags & PxCompoundPrunerQueryFlag::eDYNAMIC;
compoundTree.mGlobalPose = transform;
PxBounds3 localBounds;
const IncrementalAABBTreeNode* node = compoundTree.mTree->getNodes();
V4StoreU(node->mBVMin, &localBounds.minimum.x);
PX_ALIGN(16, PxVec4) max4;
V4StoreA(node->mBVMax, &max4.x);
localBounds.maximum = PxVec3(max4.x, max4.y, max4.z);
const PxBounds3 compoundBounds = PxBounds3::transformFast(transform, localBounds);
mCompoundTreePool.getCurrentCompoundBounds()[poolIndex] = compoundBounds;
mChangedLeaves.clear();
IncrementalAABBTreeNode* mainTreeNode = mMainTree.update(mMainTreeUpdateMap[poolIndex], poolIndex, mCompoundTreePool.getCurrentCompoundBounds(), mChangedLeaves);
// we removed node during update, need to update the mapping
updateMapping(poolIndex, mainTreeNode);
}
#if PARANOIA_CHECKS
test();
#endif
return isDynamic;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void BVHCompoundPruner::test()
{
if(mMainTree.getNodes())
{
for(PxU32 i = 0; i < mCompoundTreePool.getNbObjects(); i++)
{
mMainTree.checkTreeLeaf(mMainTreeUpdateMap[i], i);
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
void BVHCompoundPruner::release()
{
}
//////////////////////////////////////////////////////////////////////////
// Queries implementation
//////////////////////////////////////////////////////////////////////////
namespace
{
struct CompoundCallbackRaycastAdapter
{
PX_FORCE_INLINE CompoundCallbackRaycastAdapter(CompoundPrunerRaycastCallback& pcb, const CompoundTree& tree) : mCallback(pcb), mTree(tree) {}
PX_FORCE_INLINE bool invoke(PxReal& distance, PxU32 primIndex)
{
return mCallback.invoke(distance, primIndex, mTree.mPruningPool->getObjects(), mTree.mPruningPool->getTransforms(), &mTree.mGlobalPose);
}
CompoundPrunerRaycastCallback& mCallback;
const CompoundTree& mTree;
PX_NOCOPY(CompoundCallbackRaycastAdapter)
};
struct CompoundCallbackOverlapAdapter
{
PX_FORCE_INLINE CompoundCallbackOverlapAdapter(CompoundPrunerOverlapCallback& pcb, const CompoundTree& tree) : mCallback(pcb), mTree(tree) {}
PX_FORCE_INLINE bool invoke(PxU32 primIndex)
{
return mCallback.invoke(primIndex, mTree.mPruningPool->getObjects(), mTree.mPruningPool->getTransforms(), &mTree.mGlobalPose);
}
CompoundPrunerOverlapCallback& mCallback;
const CompoundTree& mTree;
PX_NOCOPY(CompoundCallbackOverlapAdapter)
};
}
template<class PrunerCallback>
struct MainTreeCompoundPrunerCallback
{
MainTreeCompoundPrunerCallback(PrunerCallback& prunerCallback, PxCompoundPrunerQueryFlags flags, const CompoundTree* compoundTrees)
: mPrunerCallback(prunerCallback), mQueryFlags(flags), mCompoundTrees(compoundTrees)
{
}
virtual ~MainTreeCompoundPrunerCallback() {}
PX_FORCE_INLINE bool filtering(const CompoundTree& compoundTree) const
{
if(!(compoundTree.mFlags & mQueryFlags) || !compoundTree.mTree->getNodes())
return true;
return false;
}
protected:
PrunerCallback& mPrunerCallback;
const PxCompoundPrunerQueryFlags mQueryFlags;
const CompoundTree* mCompoundTrees;
PX_NOCOPY(MainTreeCompoundPrunerCallback)
};
// Raycast/sweeps callback for main AABB tree
template<bool tInflate>
struct MainTreeRaycastCompoundPrunerCallback : MainTreeCompoundPrunerCallback<CompoundPrunerRaycastCallback>
{
MainTreeRaycastCompoundPrunerCallback(const PxVec3& origin, const PxVec3& unitDir, const PxVec3& extent, CompoundPrunerRaycastCallback& prunerCallback, PxCompoundPrunerQueryFlags flags, const CompoundTree* compoundTrees)
: MainTreeCompoundPrunerCallback(prunerCallback, flags, compoundTrees), mOrigin(origin), mUnitDir(unitDir), mExtent(extent)
{
}
virtual ~MainTreeRaycastCompoundPrunerCallback() {}
bool invoke(PxReal& distance, PxU32 primIndex)
{
const CompoundTree& compoundTree = mCompoundTrees[primIndex];
if(filtering(compoundTree))
return true;
// transfer to actor local space
const PxVec3 localOrigin = compoundTree.mGlobalPose.transformInv(mOrigin);
const PxVec3 localDir = compoundTree.mGlobalPose.q.rotateInv(mUnitDir);
PxVec3 localExtent = mExtent;
if(tInflate)
{
const PxBounds3 wBounds = PxBounds3::centerExtents(mOrigin, mExtent);
const PxBounds3 localBounds = PxBounds3::transformSafe(compoundTree.mGlobalPose.getInverse(), wBounds);
localExtent = localBounds.getExtents();
}
// raycast the merged tree
CompoundCallbackRaycastAdapter pcb(mPrunerCallback, compoundTree);
return AABBTreeRaycast<tInflate, true, IncrementalAABBTree, IncrementalAABBTreeNode, CompoundCallbackRaycastAdapter>()
(compoundTree.mPruningPool->getCurrentAABBTreeBounds(), *compoundTree.mTree, localOrigin, localDir, distance, localExtent, pcb);
}
PX_NOCOPY(MainTreeRaycastCompoundPrunerCallback)
private:
const PxVec3& mOrigin;
const PxVec3& mUnitDir;
const PxVec3& mExtent;
};
//////////////////////////////////////////////////////////////////////////
// raycast against the compound pruner
bool BVHCompoundPruner::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, CompoundPrunerRaycastCallback& prunerCallback, PxCompoundPrunerQueryFlags flags) const
{
bool again = true;
// search the main tree if there are nodes
if(mMainTree.getNodes())
{
const PxVec3 extent(0.0f);
// main tree callback
MainTreeRaycastCompoundPrunerCallback<false> pcb(origin, unitDir, extent, prunerCallback, flags, mCompoundTreePool.getCompoundTrees());
// traverse the main tree
again = AABBTreeRaycast<false, true, IncrementalAABBTree, IncrementalAABBTreeNode, MainTreeRaycastCompoundPrunerCallback<false> >()
(mCompoundTreePool.getCurrentAABBTreeBounds(), mMainTree, origin, unitDir, inOutDistance, extent, pcb);
}
return again;
}
//////////////////////////////////////////////////////////////////////////
// overlap main tree callback
// A.B. templated version is complicated due to test transformations, will do a callback per primitive
struct MainTreeOverlapCompoundPrunerCallback : MainTreeCompoundPrunerCallback<CompoundPrunerOverlapCallback>
{
MainTreeOverlapCompoundPrunerCallback(const ShapeData& queryVolume, CompoundPrunerOverlapCallback& prunerCallback, PxCompoundPrunerQueryFlags flags, const CompoundTree* compoundTrees)
: MainTreeCompoundPrunerCallback(prunerCallback, flags, compoundTrees), mQueryVolume(queryVolume)
{
}
virtual ~MainTreeOverlapCompoundPrunerCallback() {}
PX_NOCOPY(MainTreeOverlapCompoundPrunerCallback)
protected:
const ShapeData& mQueryVolume;
};
// OBB
struct MainTreeOBBOverlapCompoundPrunerCallback : public MainTreeOverlapCompoundPrunerCallback
{
MainTreeOBBOverlapCompoundPrunerCallback(const ShapeData& queryVolume, CompoundPrunerOverlapCallback& prunerCallback, PxCompoundPrunerQueryFlags flags, const CompoundTree* compoundTrees)
: MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags, compoundTrees) {}
bool invoke(PxU32 primIndex)
{
const CompoundTree& compoundTree = mCompoundTrees[primIndex];
if(filtering(compoundTree))
return true;
const PxVec3 localPos = compoundTree.mGlobalPose.transformInv(mQueryVolume.getPrunerWorldPos());
const PxMat33 transfMat(compoundTree.mGlobalPose.q);
const PxMat33 localRot = transfMat.getTranspose()*mQueryVolume.getPrunerWorldRot33();
const OBBAABBTest localTest(localPos, localRot, mQueryVolume.getPrunerBoxGeomExtentsInflated());
// overlap the compound local tree
CompoundCallbackOverlapAdapter pcb(mPrunerCallback, compoundTree);
return AABBTreeOverlap<true, OBBAABBTest, IncrementalAABBTree, IncrementalAABBTreeNode, CompoundCallbackOverlapAdapter>()
(compoundTree.mPruningPool->getCurrentAABBTreeBounds(), *compoundTree.mTree, localTest, pcb);
}
PX_NOCOPY(MainTreeOBBOverlapCompoundPrunerCallback)
};
// AABB
struct MainTreeAABBOverlapCompoundPrunerCallback : public MainTreeOverlapCompoundPrunerCallback
{
MainTreeAABBOverlapCompoundPrunerCallback(const ShapeData& queryVolume, CompoundPrunerOverlapCallback& prunerCallback, PxCompoundPrunerQueryFlags flags, const CompoundTree* compoundTrees)
: MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags, compoundTrees) {}
bool invoke(PxU32 primIndex)
{
const CompoundTree& compoundTree = mCompoundTrees[primIndex];
if(filtering(compoundTree))
return true;
const PxVec3 localPos = compoundTree.mGlobalPose.transformInv(mQueryVolume.getPrunerWorldPos());
const PxMat33 transfMat(compoundTree.mGlobalPose.q);
const PxMat33 localRot = transfMat.getTranspose()*mQueryVolume.getPrunerWorldRot33();
// A.B. we dont have the AABB in local space, either we test OBB local space or
// we retest the AABB with the worldSpace AABB of the local tree???
const OBBAABBTest localTest(localPos, localRot, mQueryVolume.getPrunerBoxGeomExtentsInflated());
// overlap the compound local tree
CompoundCallbackOverlapAdapter pcb(mPrunerCallback, compoundTree);
return AABBTreeOverlap<true, OBBAABBTest, IncrementalAABBTree, IncrementalAABBTreeNode, CompoundCallbackOverlapAdapter>()
(compoundTree.mPruningPool->getCurrentAABBTreeBounds(), *compoundTree.mTree, localTest, pcb);
}
PX_NOCOPY(MainTreeAABBOverlapCompoundPrunerCallback)
};
// Capsule
struct MainTreeCapsuleOverlapCompoundPrunerCallback : public MainTreeOverlapCompoundPrunerCallback
{
MainTreeCapsuleOverlapCompoundPrunerCallback(const ShapeData& queryVolume, CompoundPrunerOverlapCallback& prunerCallback, PxCompoundPrunerQueryFlags flags, const CompoundTree* compoundTrees)
: MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags, compoundTrees) {}
bool invoke(PxU32 primIndex)
{
const CompoundTree& compoundTree = mCompoundTrees[primIndex];
if(filtering(compoundTree))
return true;
const PxMat33 transfMat(compoundTree.mGlobalPose.q);
const Capsule& capsule = mQueryVolume.getGuCapsule();
const CapsuleAABBTest localTest(
compoundTree.mGlobalPose.transformInv(capsule.p1),
transfMat.getTranspose()*mQueryVolume.getPrunerWorldRot33().column0,
mQueryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION));
// overlap the compound local tree
CompoundCallbackOverlapAdapter pcb(mPrunerCallback, compoundTree);
return AABBTreeOverlap<true, CapsuleAABBTest, IncrementalAABBTree, IncrementalAABBTreeNode, CompoundCallbackOverlapAdapter>()
(compoundTree.mPruningPool->getCurrentAABBTreeBounds(), *compoundTree.mTree, localTest, pcb);
}
PX_NOCOPY(MainTreeCapsuleOverlapCompoundPrunerCallback)
};
// Sphere
struct MainTreeSphereOverlapCompoundPrunerCallback : public MainTreeOverlapCompoundPrunerCallback
{
MainTreeSphereOverlapCompoundPrunerCallback(const ShapeData& queryVolume, CompoundPrunerOverlapCallback& prunerCallback, PxCompoundPrunerQueryFlags flags, const CompoundTree* compoundTrees)
: MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags, compoundTrees) {}
bool invoke(PxU32 primIndex)
{
const CompoundTree& compoundTree = mCompoundTrees[primIndex];
if(filtering(compoundTree))
return true;
const Sphere& sphere = mQueryVolume.getGuSphere();
const SphereAABBTest localTest(compoundTree.mGlobalPose.transformInv(sphere.center), sphere.radius);
// overlap the compound local tree
CompoundCallbackOverlapAdapter pcb(mPrunerCallback, compoundTree);
return AABBTreeOverlap<true, SphereAABBTest, IncrementalAABBTree, IncrementalAABBTreeNode, CompoundCallbackOverlapAdapter>()
(compoundTree.mPruningPool->getCurrentAABBTreeBounds(), *compoundTree.mTree, localTest, pcb);
}
PX_NOCOPY(MainTreeSphereOverlapCompoundPrunerCallback)
};
//////////////////////////////////////////////////////////////////////////
// overlap implementation
bool BVHCompoundPruner::overlap(const ShapeData& queryVolume, CompoundPrunerOverlapCallback& prunerCallback, PxCompoundPrunerQueryFlags flags) const
{
if(!mMainTree.getNodes())
return true;
bool again = true;
const Gu::AABBTreeBounds& bounds = mCompoundTreePool.getCurrentAABBTreeBounds();
switch (queryVolume.getType())
{
case PxGeometryType::eBOX:
{
if(queryVolume.isOBB())
{
const DefaultOBBAABBTest test(queryVolume);
MainTreeOBBOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags, mCompoundTreePool.getCompoundTrees());
again = AABBTreeOverlap<true, OBBAABBTest, IncrementalAABBTree, IncrementalAABBTreeNode, MainTreeOBBOverlapCompoundPrunerCallback>()(bounds, mMainTree, test, pcb);
}
else
{
const DefaultAABBAABBTest test(queryVolume);
MainTreeAABBOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags, mCompoundTreePool.getCompoundTrees());
again = AABBTreeOverlap<true, AABBAABBTest, IncrementalAABBTree, IncrementalAABBTreeNode, MainTreeAABBOverlapCompoundPrunerCallback>()(bounds, mMainTree, test, pcb);
}
}
break;
case PxGeometryType::eCAPSULE:
{
const DefaultCapsuleAABBTest test(queryVolume, SQ_PRUNER_INFLATION);
MainTreeCapsuleOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags, mCompoundTreePool.getCompoundTrees());
again = AABBTreeOverlap<true, CapsuleAABBTest, IncrementalAABBTree, IncrementalAABBTreeNode, MainTreeCapsuleOverlapCompoundPrunerCallback >()(bounds, mMainTree, test, pcb);
}
break;
case PxGeometryType::eSPHERE:
{
const DefaultSphereAABBTest test(queryVolume);
MainTreeSphereOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags, mCompoundTreePool.getCompoundTrees());
again = AABBTreeOverlap<true, SphereAABBTest, IncrementalAABBTree, IncrementalAABBTreeNode, MainTreeSphereOverlapCompoundPrunerCallback>()(bounds, mMainTree, test, pcb);
}
break;
case PxGeometryType::eCONVEXMESH:
{
const DefaultOBBAABBTest test(queryVolume);
MainTreeOBBOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags, mCompoundTreePool.getCompoundTrees());
again = AABBTreeOverlap<true, OBBAABBTest, IncrementalAABBTree, IncrementalAABBTreeNode, MainTreeOBBOverlapCompoundPrunerCallback>()(bounds, mMainTree, test, pcb);
}
break;
default:
PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type");
}
return again;
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool BVHCompoundPruner::sweep(const ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, CompoundPrunerRaycastCallback& prunerCallback, PxCompoundPrunerQueryFlags flags) const
{
bool again = true;
if(mMainTree.getNodes())
{
const PxBounds3& aabb = queryVolume.getPrunerInflatedWorldAABB();
const PxVec3 extents = aabb.getExtents();
const PxVec3 center = aabb.getCenter();
MainTreeRaycastCompoundPrunerCallback<true> pcb(center, unitDir, extents, prunerCallback, flags, mCompoundTreePool.getCompoundTrees());
again = AABBTreeRaycast<true, true, IncrementalAABBTree, IncrementalAABBTreeNode, MainTreeRaycastCompoundPrunerCallback<true> >()
(mCompoundTreePool.getCurrentAABBTreeBounds(), mMainTree, center, unitDir, inOutDistance, extents, pcb);
}
return again;
}
///////////////////////////////////////////////////////////////////////////////////////////////
const PrunerPayload& BVHCompoundPruner::getPayloadData(PrunerHandle handle, PrunerCompoundId compoundId, PrunerPayloadData* data) const
{
const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId);
PX_ASSERT(poolIndexEntry);
return mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].mPruningPool->getPayloadData(handle, data);
}
///////////////////////////////////////////////////////////////////////////////////////////////
void BVHCompoundPruner::preallocate(PxU32 nbEntries)
{
mCompoundTreePool.preallocate(nbEntries);
mMainTreeUpdateMap.resizeUninitialized(nbEntries);
mPoolActorMap.resizeUninitialized(nbEntries);
mChangedLeaves.reserve(nbEntries);
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool BVHCompoundPruner::setTransform(PrunerHandle handle, PrunerCompoundId compoundId, const PxTransform& transform)
{
const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId);
PX_ASSERT(poolIndexEntry);
return mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].mPruningPool->setTransform(handle, transform);
}
const PxTransform& BVHCompoundPruner::getTransform(PrunerCompoundId compoundId) const
{
const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId);
PX_ASSERT(poolIndexEntry);
return mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].mGlobalPose;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void BVHCompoundPruner::updateObjectAfterManualBoundsUpdates(PrunerCompoundId compoundId, const PrunerHandle handle)
{
const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId);
PX_ASSERT(poolIndexEntry);
if(!poolIndexEntry)
return;
mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].updateObjectAfterManualBoundsUpdates(handle);
const PxU32 poolIndex = poolIndexEntry->second;
updateMainTreeNode(poolIndex);
}
///////////////////////////////////////////////////////////////////////////////////////////////
void BVHCompoundPruner::removeObject(PrunerCompoundId compoundId, const PrunerHandle handle, PrunerPayloadRemovalCallback* removalCallback)
{
const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId);
PX_ASSERT(poolIndexEntry);
if(!poolIndexEntry)
return;
const PxU32 poolIndex = poolIndexEntry->second;
mCompoundTreePool.getCompoundTrees()[poolIndex].removeObject(handle, removalCallback);
// edge case, we removed all objects for the compound tree, we need to remove it now completely
if(!mCompoundTreePool.getCompoundTrees()[poolIndex].mTree->getNodes())
removeCompound(compoundId, removalCallback);
else
updateMainTreeNode(poolIndex);
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool BVHCompoundPruner::addObject(PrunerCompoundId compoundId, PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload userData, const PxTransform& transform)
{
const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId);
PX_ASSERT(poolIndexEntry);
if(!poolIndexEntry)
return false;
mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].addObject(result, bounds, userData, transform);
const PxU32 poolIndex = poolIndexEntry->second;
updateMainTreeNode(poolIndex);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void BVHCompoundPruner::updateMainTreeNode(PoolIndex poolIndex)
{
PxBounds3 localBounds;
const IncrementalAABBTreeNode* node = mCompoundTreePool.getCompoundTrees()[poolIndex].mTree->getNodes();
V4StoreU(node->mBVMin, &localBounds.minimum.x);
PX_ALIGN(16, PxVec4) max4;
V4StoreA(node->mBVMax, &max4.x);
localBounds.maximum = PxVec3(max4.x, max4.y, max4.z);
const PxBounds3 compoundBounds = PxBounds3::transformFast(mCompoundTreePool.getCompoundTrees()[poolIndex].mGlobalPose, localBounds);
mCompoundTreePool.getCurrentCompoundBounds()[poolIndex] = compoundBounds;
mChangedLeaves.clear();
IncrementalAABBTreeNode* mainTreeNode = mMainTree.update(mMainTreeUpdateMap[poolIndex], poolIndex, mCompoundTreePool.getCurrentCompoundBounds(), mChangedLeaves);
// we removed node during update, need to update the mapping
updateMapping(poolIndex, mainTreeNode);
}
///////////////////////////////////////////////////////////////////////////////////////////////
void BVHCompoundPruner::shiftOrigin(const PxVec3& shift)
{
mCompoundTreePool.shiftOrigin(shift);
mMainTree.shiftOrigin(shift);
}
///////////////////////////////////////////////////////////////////////////////////////////////
namespace
{
class CompoundTreeVizCb : public DebugVizCallback
{
PX_NOCOPY(CompoundTreeVizCb)
public:
CompoundTreeVizCb(PxRenderOutput& out, const CompoundTree& tree) :
mOut (out),
mPose (tree.mGlobalPose)
{
}
virtual bool visualizeNode(const IncrementalAABBTreeNode& /*node*/, const PxBounds3& bounds)
{
if(0)
{
Cm::renderOutputDebugBox(mOut, PxBounds3::transformSafe(mPose, bounds));
}
else
{
PxVec3 pts[8];
computeBoxPoints(bounds, pts);
for(PxU32 i=0;i<8;i++)
pts[i] = mPose.transform(pts[i]);
const PxU8* edges = getBoxEdges();
for(PxU32 i=0;i<12;i++)
{
const PxVec3& p0 = pts[*edges++];
const PxVec3& p1 = pts[*edges++];
mOut.outputSegment(p0, p1);
}
}
return true;
}
PxRenderOutput& mOut;
const PxTransform& mPose;
};
class CompoundPrunerDebugVizCb : public DebugVizCallback
{
PX_NOCOPY(CompoundPrunerDebugVizCb)
public:
CompoundPrunerDebugVizCb(PxRenderOutput& out, const CompoundTree* trees, bool debugStatic, bool debugDynamic) :
mOut (out),
mTrees (trees),
mDebugVizStatic (debugStatic),
mDebugVizDynamic(debugDynamic)
{}
virtual bool visualizeNode(const IncrementalAABBTreeNode& node, const PxBounds3& /*bounds*/)
{
if(node.isLeaf())
{
PxU32 nbPrims = node.getNbPrimitives();
const PxU32* prims = node.getPrimitives(NULL);
while(nbPrims--)
{
const CompoundTree& compoundTree = mTrees[*prims++];
const bool isDynamic = compoundTree.mFlags & PxCompoundPrunerQueryFlag::eDYNAMIC;
if((mDebugVizDynamic && isDynamic) || (mDebugVizStatic && !isDynamic))
{
const PxU32 color = isDynamic ? SQ_DEBUG_VIZ_DYNAMIC_COLOR : SQ_DEBUG_VIZ_STATIC_COLOR;
CompoundTreeVizCb leafCB(mOut, compoundTree);
visualizeTree(mOut, color, compoundTree.mTree, &leafCB);
mOut << SQ_DEBUG_VIZ_COMPOUND_COLOR;
}
}
}
return false;
}
PxRenderOutput& mOut;
const CompoundTree* mTrees;
const bool mDebugVizStatic;
const bool mDebugVizDynamic;
};
}
void BVHCompoundPruner::visualize(PxRenderOutput& out, PxU32 primaryColor, PxU32 /*secondaryColor*/) const
{
if(mDrawStatic || mDrawDynamic)
{
CompoundPrunerDebugVizCb cb(out, mCompoundTreePool.getCompoundTrees(), mDrawStatic, mDrawDynamic);
visualizeTree(out, primaryColor, &mMainTree, &cb);
}
}
void BVHCompoundPruner::visualizeEx(PxRenderOutput& out, PxU32 color, bool drawStatic, bool drawDynamic) const
{
mDrawStatic = drawStatic;
mDrawDynamic = drawDynamic;
visualize(out, color, color);
}
///////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,102 @@
// 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.
#ifndef SQ_COMPOUND_PRUNER_H
#define SQ_COMPOUND_PRUNER_H
#include "SqCompoundPruningPool.h"
#include "GuSqInternal.h"
#include "GuPrunerMergeData.h"
#include "GuIncrementalAABBTree.h"
#include "GuPruningPool.h"
#include "foundation/PxHashMap.h"
#include "foundation/PxArray.h"
namespace physx
{
namespace Sq
{
///////////////////////////////////////////////////////////////////////////////////////////////
typedef PxHashMap<PrunerCompoundId, Gu::PoolIndex> ActorIdPoolIndexMap;
typedef PxArray<PrunerCompoundId> PoolIndexActorIdMap;
///////////////////////////////////////////////////////////////////////////////////////////////
class BVHCompoundPruner : public CompoundPruner
{
public:
BVHCompoundPruner(PxU64 contextID);
virtual ~BVHCompoundPruner();
void release();
// BasePruner
DECLARE_BASE_PRUNER_API
//~BasePruner
// CompoundPruner
// compound level
virtual bool addCompound(Gu::PrunerHandle* results, const Gu::BVH& bvh, PrunerCompoundId compoundId, const PxTransform& transform, bool isDynamic, const Gu::PrunerPayload* data, const PxTransform* transforms);
virtual bool removeCompound(PrunerCompoundId compoundId, Gu::PrunerPayloadRemovalCallback* removalCallback);
virtual bool updateCompound(PrunerCompoundId compoundId, const PxTransform& transform);
// object level
virtual void updateObjectAfterManualBoundsUpdates(PrunerCompoundId compoundId, const Gu::PrunerHandle handle);
virtual void removeObject(PrunerCompoundId compoundId, const Gu::PrunerHandle handle, Gu::PrunerPayloadRemovalCallback* removalCallback);
virtual bool addObject(PrunerCompoundId compoundId, Gu::PrunerHandle& result, const PxBounds3& bounds, const Gu::PrunerPayload userData, const PxTransform& transform);
//queries
virtual bool raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, CompoundPrunerRaycastCallback&, PxCompoundPrunerQueryFlags flags) const;
virtual bool overlap(const Gu::ShapeData& queryVolume, CompoundPrunerOverlapCallback&, PxCompoundPrunerQueryFlags flags) const;
virtual bool sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, CompoundPrunerRaycastCallback&, PxCompoundPrunerQueryFlags flags) const;
virtual const Gu::PrunerPayload& getPayloadData(Gu::PrunerHandle handle, PrunerCompoundId compoundId, Gu::PrunerPayloadData* data) const;
virtual void preallocate(PxU32 nbEntries);
virtual bool setTransform(Gu::PrunerHandle handle, PrunerCompoundId compoundId, const PxTransform& transform);
virtual const PxTransform& getTransform(PrunerCompoundId compoundId) const;
virtual void visualizeEx(PxRenderOutput& out, PxU32 color, bool drawStatic, bool drawDynamic) const;
// ~CompoundPruner
private:
void updateMapping(const Gu::PoolIndex poolIndex, Gu::IncrementalAABBTreeNode* node);
void updateMainTreeNode(Gu::PoolIndex index);
void test();
Gu::IncrementalAABBTree mMainTree;
UpdateMap mMainTreeUpdateMap;
CompoundTreePool mCompoundTreePool;
ActorIdPoolIndexMap mActorPoolMap;
PoolIndexActorIdMap mPoolActorMap;
Gu::NodeList mChangedLeaves;
mutable bool mDrawStatic;
mutable bool mDrawDynamic;
};
}
}
#endif

View File

@@ -0,0 +1,271 @@
// 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 "foundation/PxAllocator.h"
#include "SqCompoundPruningPool.h"
#include "GuPruningPool.h"
#include "GuAABBTree.h"
#include "GuBVH.h"
using namespace physx;
using namespace Cm;
using namespace Gu;
using namespace Sq;
///////////////////////////////////////////////////////////////////////////////////////////////
void CompoundTree::updateObjectAfterManualBoundsUpdates(PrunerHandle handle)
{
const PxBounds3* newBounds = mPruningPool->getCurrentWorldBoxes();
const PoolIndex poolIndex = mPruningPool->getIndex(handle);
NodeList changedLeaves;
changedLeaves.reserve(8);
IncrementalAABBTreeNode* node = mTree->update((*mUpdateMap)[poolIndex], poolIndex, newBounds, changedLeaves);
// we removed node during update, need to update the mapping
updateMapping(poolIndex, node, changedLeaves);
}
///////////////////////////////////////////////////////////////////////////////////////////////
void CompoundTree::removeObject(PrunerHandle handle, PrunerPayloadRemovalCallback* removalCallback)
{
const PoolIndex poolIndex = mPruningPool->getIndex(handle); // save the pool index for removed object
const PoolIndex poolRelocatedLastIndex = mPruningPool->removeObject(handle, removalCallback); // save the lastIndex returned by removeObject
IncrementalAABBTreeNode* node = mTree->remove((*mUpdateMap)[poolIndex], poolIndex, mPruningPool->getCurrentWorldBoxes());
// if node moved to its parent
if (node && node->isLeaf())
{
for (PxU32 j = 0; j < node->getNbPrimitives(); j++)
{
const PoolIndex index = node->getPrimitives(NULL)[j];
(*mUpdateMap)[index] = node;
}
}
(*mUpdateMap)[poolIndex] = (*mUpdateMap)[poolRelocatedLastIndex];
// fix indices if we made a swap
if(poolRelocatedLastIndex != poolIndex)
mTree->fixupTreeIndices((*mUpdateMap)[poolIndex], poolRelocatedLastIndex, poolIndex);
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool CompoundTree::addObject(PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload& data, const PxTransform& transform)
{
mPruningPool->addObjects(&result, &bounds, &data, &transform, 1);
if (mPruningPool->mMaxNbObjects > mUpdateMap->size())
mUpdateMap->resize(mPruningPool->mMaxNbObjects);
const PoolIndex poolIndex = mPruningPool->getIndex(result);
NodeList changedLeaves;
changedLeaves.reserve(8);
IncrementalAABBTreeNode* node = mTree->insert(poolIndex, mPruningPool->getCurrentWorldBoxes(), changedLeaves);
updateMapping(poolIndex, node, changedLeaves);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void CompoundTree::updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node, const NodeList& changedLeaves)
{
// if a node was split we need to update the node indices and also the sibling indices
if(!changedLeaves.empty())
{
if(node && node->isLeaf())
{
for(PxU32 j = 0; j < node->getNbPrimitives(); j++)
{
const PoolIndex index = node->getPrimitives(NULL)[j];
(*mUpdateMap)[index] = node;
}
}
for(PxU32 i = 0; i < changedLeaves.size(); i++)
{
IncrementalAABBTreeNode* changedNode = changedLeaves[i];
PX_ASSERT(changedNode->isLeaf());
for(PxU32 j = 0; j < changedNode->getNbPrimitives(); j++)
{
const PoolIndex index = changedNode->getPrimitives(NULL)[j];
(*mUpdateMap)[index] = changedNode;
}
}
}
else
{
(*mUpdateMap)[poolIndex] = node;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
CompoundTreePool::CompoundTreePool(PxU64 contextID) :
mNbObjects (0),
mMaxNbObjects (0),
mCompoundTrees (NULL),
mContextID (contextID)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
CompoundTreePool::~CompoundTreePool()
{
PX_FREE(mCompoundTrees);
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool CompoundTreePool::resize(PxU32 newCapacity)
{
mCompoundBounds.resize(newCapacity, mNbObjects);
CompoundTree* newTrees = PX_ALLOCATE(CompoundTree, newCapacity, "IncrementalTrees*");
if(!newTrees)
return false;
// memzero, we need to set the pointers in the compound tree to NULL
PxMemZero(newTrees, sizeof(CompoundTree)*newCapacity);
if(mCompoundTrees)
PxMemCopy(newTrees, mCompoundTrees, mNbObjects*sizeof(CompoundTree));
mMaxNbObjects = newCapacity;
PX_FREE(mCompoundTrees);
mCompoundTrees = newTrees;
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void CompoundTreePool::preallocate(PxU32 newCapacity)
{
if(newCapacity>mMaxNbObjects)
resize(newCapacity);
}
///////////////////////////////////////////////////////////////////////////////////////////////
void CompoundTreePool::shiftOrigin(const PxVec3& shift)
{
PxBounds3* bounds = mCompoundBounds.getBounds();
for(PxU32 i=0; i < mNbObjects; i++)
{
bounds[i].minimum -= shift;
bounds[i].maximum -= shift;
mCompoundTrees[i].mGlobalPose.p -= shift;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
PoolIndex CompoundTreePool::addCompound(PrunerHandle* results, const BVH& bvh, const PxBounds3& compoundBounds, const PxTransform& transform, bool isDynamic, const PrunerPayload* data, const PxTransform* transforms)
{
if(mNbObjects==mMaxNbObjects) // increase the capacity on overflow
{
if(!resize(PxMax<PxU32>(mMaxNbObjects*2, 32)))
{
// pool can return an invalid handle if memory alloc fails
PxGetFoundation().error(PxErrorCode::eOUT_OF_MEMORY, PX_FL, "CompoundTreePool::addCompound memory allocation in resize failed.");
return INVALID_PRUNERHANDLE;
}
}
PX_ASSERT(mNbObjects!=mMaxNbObjects);
const PoolIndex index = mNbObjects++;
mCompoundBounds.getBounds()[index] = compoundBounds;
const PxU32 nbObjects = bvh.getNbBounds();
CompoundTree& tree = mCompoundTrees[index];
PX_ASSERT(tree.mPruningPool == NULL);
PX_ASSERT(tree.mTree == NULL);
PX_ASSERT(tree.mUpdateMap == NULL);
tree.mGlobalPose = transform;
tree.mFlags = isDynamic ? PxCompoundPrunerQueryFlag::eDYNAMIC : PxCompoundPrunerQueryFlag::eSTATIC;
// prepare the pruning pool
PruningPool* pool = PX_NEW(PruningPool)(mContextID, TRANSFORM_CACHE_LOCAL);
pool->preallocate(nbObjects);
pool->addObjects(results, bvh.getBounds(), data, transforms, nbObjects);
tree.mPruningPool = pool;
// prepare update map
UpdateMap* map = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(UpdateMap), "Update map"), UpdateMap);
map->resizeUninitialized(nbObjects);
tree.mUpdateMap = map;
IncrementalAABBTree* iTree = PX_NEW(IncrementalAABBTree);
iTree->copy(bvh, *map);
tree.mTree = iTree;
return index;
}
///////////////////////////////////////////////////////////////////////////////////////////////
PoolIndex CompoundTreePool::removeCompound(PoolIndex indexOfRemovedObject, PrunerPayloadRemovalCallback* removalCallback)
{
PX_ASSERT(mNbObjects);
// release the tree
PX_DELETE(mCompoundTrees[indexOfRemovedObject].mTree);
mCompoundTrees[indexOfRemovedObject].mUpdateMap->clear();
mCompoundTrees[indexOfRemovedObject].mUpdateMap->~PxArray();
PX_FREE(mCompoundTrees[indexOfRemovedObject].mUpdateMap);
if(removalCallback)
{
const PruningPool* pool = mCompoundTrees[indexOfRemovedObject].mPruningPool;
removalCallback->invoke(pool->getNbActiveObjects(), pool->getObjects());
}
PX_DELETE(mCompoundTrees[indexOfRemovedObject].mPruningPool);
const PoolIndex indexOfLastObject = --mNbObjects; // swap the object at last index with index
if(indexOfLastObject!=indexOfRemovedObject)
{
// PT: move last object's data to recycled spot (from removed object)
// PT: the last object has moved so we need to handle the mappings for this object
mCompoundBounds.getBounds() [indexOfRemovedObject] = mCompoundBounds.getBounds() [indexOfLastObject];
mCompoundTrees [indexOfRemovedObject] = mCompoundTrees [indexOfLastObject];
mCompoundTrees [indexOfLastObject].mPruningPool = NULL;
mCompoundTrees [indexOfLastObject].mUpdateMap = NULL;
mCompoundTrees [indexOfLastObject].mTree = NULL;
}
return indexOfLastObject;
}

View File

@@ -0,0 +1,112 @@
// 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.
#ifndef SQ_COMPOUND_PRUNING_POOL_H
#define SQ_COMPOUND_PRUNING_POOL_H
#include "SqPruner.h"
#include "foundation/PxArray.h"
#include "GuPrunerMergeData.h"
#include "GuIncrementalAABBTree.h"
#include "GuAABBTreeBounds.h"
namespace physx
{
namespace Gu
{
class PruningPool;
}
namespace Sq
{
///////////////////////////////////////////////////////////////////////////////////////////////
typedef PxArray<Gu::IncrementalAABBTreeNode*> UpdateMap;
///////////////////////////////////////////////////////////////////////////////////////////////
class CompoundTree
{
public:
void updateObjectAfterManualBoundsUpdates(Gu::PrunerHandle handle);
void removeObject(Gu::PrunerHandle handle, Gu::PrunerPayloadRemovalCallback* removalCallback);
bool addObject(Gu::PrunerHandle& result, const PxBounds3& bounds, const Gu::PrunerPayload& data, const PxTransform& transform);
private:
void updateMapping(const Gu::PoolIndex poolIndex, Gu::IncrementalAABBTreeNode* node, const Gu::NodeList& changedLeaves);
public:
Gu::IncrementalAABBTree* mTree;
Gu::PruningPool* mPruningPool;
UpdateMap* mUpdateMap;
PxTransform mGlobalPose;
PxCompoundPrunerQueryFlags mFlags;
};
///////////////////////////////////////////////////////////////////////////////////////////////
class CompoundTreePool
{
public:
CompoundTreePool(PxU64 contextID);
~CompoundTreePool();
void preallocate(PxU32 newCapacity);
Gu::PoolIndex addCompound(Gu::PrunerHandle* results, const Gu::BVH& bvh, const PxBounds3& compoundBounds, const PxTransform& transform, bool isDynamic, const Gu::PrunerPayload* data, const PxTransform* transforms);
Gu::PoolIndex removeCompound(Gu::PoolIndex index, Gu::PrunerPayloadRemovalCallback* removalCallback);
void shiftOrigin(const PxVec3& shift);
PX_FORCE_INLINE const Gu::AABBTreeBounds& getCurrentAABBTreeBounds() const { return mCompoundBounds; }
PX_FORCE_INLINE const PxBounds3* getCurrentCompoundBounds() const { return mCompoundBounds.getBounds(); }
PX_FORCE_INLINE PxBounds3* getCurrentCompoundBounds() { return mCompoundBounds.getBounds(); }
PX_FORCE_INLINE const CompoundTree* getCompoundTrees() const { return mCompoundTrees; }
PX_FORCE_INLINE CompoundTree* getCompoundTrees() { return mCompoundTrees; }
PX_FORCE_INLINE PxU32 getNbObjects() const { return mNbObjects; }
private:
bool resize(PxU32 newCapacity);
PxU32 mNbObjects; //!< Current number of objects
PxU32 mMaxNbObjects; //!< Max. number of objects (capacity for mWorldBoxes, mObjects)
//!< these arrays are parallel
Gu::AABBTreeBounds mCompoundBounds; //!< List of compound world boxes, stores mNbObjects, capacity=mMaxNbObjects
CompoundTree* mCompoundTrees;
PxU64 mContextID;
};
}
}
#endif

View File

@@ -0,0 +1,39 @@
// 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 "SqFactory.h"
#include "SqCompoundPruner.h"
using namespace physx;
using namespace Sq;
CompoundPruner* physx::Sq::createCompoundPruner(PxU64 contextID)
{
return PX_NEW(BVHCompoundPruner)(contextID);
}

View File

@@ -0,0 +1,577 @@
// 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.
// PT: SQ-API LEVEL 2 (Level 1 = SqPruner.h)
// PT: this file is part of a "high-level" set of files within Sq. The SqPruner API doesn't rely on them.
// PT: this should really be at Np level but moving it to Sq allows us to share it.
#include "SqManager.h"
#include "GuSqInternal.h"
#include "GuBounds.h"
using namespace physx;
using namespace Sq;
using namespace Gu;
PrunerExt::PrunerExt() : mPruner(NULL), mDirtyList("SQmDirtyList"), mDirtyStatic(false)
{
}
PrunerExt::~PrunerExt()
{
PX_DELETE(mPruner);
}
void PrunerExt::init(Pruner* pruner)
{
mPruner = pruner;
}
void PrunerExt::preallocate(PxU32 nbShapes)
{
// if(nbShapes > mDirtyMap.size())
// mDirtyMap.resize(nbShapes);
if(mPruner)
mPruner->preallocate(nbShapes);
}
void PrunerExt::flushMemory()
{
if(!mDirtyList.size())
mDirtyList.reset();
// PT: TODO: flush bitmap here
// PT: TODO: flush pruner here?
}
void PrunerExt::addToDirtyList(PrunerHandle handle, bool dynamic, const PxTransform& transform)
{
if(mPruner)
mPruner->setTransform(handle, transform);
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(!dirtyMap.test(handle))
{
dirtyMap.set(handle);
mDirtyList.pushBack(handle);
}
if(!dynamic)
mDirtyStatic = true;
}
void PrunerExt::removeFromDirtyList(PrunerHandle handle)
{
PxBitMap& dirtyMap = mDirtyMap;
// if(dirtyMap.test(handle))
if(dirtyMap.boundedTest(handle))
{
dirtyMap.reset(handle);
mDirtyList.findAndReplaceWithLast(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 PrunerExt::processDirtyList(PxU32 index, const Adapter& adapter, float inflation)
{
const PxU32 numDirtyList = mDirtyList.size();
if(!numDirtyList)
return false;
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.
PX_UNUSED(index);
PrunerPayloadData ppd;
const PrunerPayload& pp = mPruner->getPayloadData(handle, &ppd);
computeBounds(*ppd.mBounds, adapter.getGeometry(pp), *ppd.mTransform, 0.0f, inflation);
}
// PT: batch update happens after the loop instead of once per loop iteration
mPruner->updateObjects(prunerHandles, numDirtyList);
mDirtyList.clear();
const bool ret = mDirtyStatic;
mDirtyStatic = false;
return ret;
}
// PT: TODO: re-inline this
/*void PrunerExt::growDirtyList(PrunerHandle handle)
{
// pruners must either provide indices in order or reuse existing indices, so this 'if' is enough to ensure we have space for the new handle
// PT: TODO: fix this. There is just no need for any of it. The pruning pool itself could support the feature for free, similar to what we do
// in MBP. There would be no need for the bitmap or the dirty list array. However doing this through the virtual interface would be clumsy,
// adding the cost of virtual calls for very cheap & simple operations. It would be a lot easier to drop it and go back to what we had before.
PxBitMap& dirtyMap = mDirtyMap;
if(dirtyMap.size() <= handle)
dirtyMap.resize(PxMax<PxU32>(dirtyMap.size() * 2, 1024));
PX_ASSERT(handle<dirtyMap.size());
dirtyMap.reset(handle);
}*/
///////////////////////////////////////////////////////////////////////////////
CompoundPrunerExt::CompoundPrunerExt() :
mPruner (NULL)
{
}
CompoundPrunerExt::~CompoundPrunerExt()
{
PX_DELETE(mPruner);
}
void CompoundPrunerExt::preallocate(PxU32 nbShapes)
{
// if(nbShapes > mDirtyList.size())
// mDirtyList.reserve(nbShapes);
if(mPruner)
mPruner->preallocate(nbShapes);
}
void CompoundPrunerExt::flushMemory()
{
if(!mDirtyList.size())
mDirtyList.clear();
}
void CompoundPrunerExt::flushShapes(const Adapter& adapter, float inflation)
{
const PxU32 numDirtyList = mDirtyList.size();
if(!numDirtyList)
return;
const CompoundPair* const compoundPairs = mDirtyList.getEntries();
for(PxU32 i=0; i<numDirtyList; i++)
{
const PrunerHandle handle = compoundPairs[i].second;
const PrunerCompoundId compoundId = compoundPairs[i].first;
// 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 ppd;
const PrunerPayload& pp = mPruner->getPayloadData(handle, compoundId, &ppd);
computeBounds(*ppd.mBounds, adapter.getGeometry(pp), *ppd.mTransform, 0.0f, inflation);
// A.B. not very effective, we might do better here
mPruner->updateObjectAfterManualBoundsUpdates(compoundId, handle);
}
mDirtyList.clear();
}
void CompoundPrunerExt::addToDirtyList(PrunerCompoundId compoundId, PrunerHandle handle, const PxTransform& transform)
{
if(mPruner)
mPruner->setTransform(handle, compoundId, transform);
mDirtyList.insert(CompoundPair(compoundId, handle));
}
void CompoundPrunerExt::removeFromDirtyList(PrunerCompoundId compoundId, PrunerHandle handle)
{
mDirtyList.erase(CompoundPair(compoundId, handle));
}
///////////////////////////////////////////////////////////////////////////////
#include "SqFactory.h"
#include "common/PxProfileZone.h"
#include "common/PxRenderBuffer.h"
#include "GuBVH.h"
#include "foundation/PxAlloca.h"
#include "PxSceneDesc.h" // PT: for PxSceneLimits TODO: remove
namespace
{
enum PxScenePrunerIndex
{
PX_SCENE_PRUNER_STATIC = 0,
PX_SCENE_PRUNER_DYNAMIC = 1,
PX_SCENE_COMPOUND_PRUNER = 0xffffffff,
};
}
PrunerManager::PrunerManager( PxU64 contextID, Pruner* staticPruner, Pruner* dynamicPruner,
PxU32 dynamicTreeRebuildRateHint, float inflation,
const PxSceneLimits& limits, const Adapter& adapter) :
mAdapter (adapter),
mContextID (contextID),
mStaticTimestamp (0),
mInflation (inflation)
{
mPrunerExt[PruningIndex::eSTATIC].init(staticPruner);
mPrunerExt[PruningIndex::eDYNAMIC].init(dynamicPruner);
setDynamicTreeRebuildRateHint(dynamicTreeRebuildRateHint);
mCompoundPrunerExt.mPruner = createCompoundPruner(contextID);
preallocate(PruningIndex::eSTATIC, limits.maxNbStaticShapes);
preallocate(PruningIndex::eDYNAMIC, limits.maxNbDynamicShapes);
preallocate(PxU32(PX_SCENE_COMPOUND_PRUNER), 32);
mPrunerNeedsUpdating = false;
}
PrunerManager::~PrunerManager()
{
}
void PrunerManager::preallocate(PxU32 prunerIndex, PxU32 nbShapes)
{
if(prunerIndex==PruningIndex::eSTATIC)
mPrunerExt[PruningIndex::eSTATIC].preallocate(nbShapes);
else if(prunerIndex==PruningIndex::eDYNAMIC)
mPrunerExt[PruningIndex::eDYNAMIC].preallocate(nbShapes);
else if(prunerIndex==PX_SCENE_COMPOUND_PRUNER)
mCompoundPrunerExt.preallocate(nbShapes);
}
void PrunerManager::flushMemory()
{
for(PxU32 i=0;i<PruningIndex::eCOUNT;i++)
mPrunerExt[i].flushMemory();
mCompoundPrunerExt.flushMemory();
}
PrunerData PrunerManager::addPrunerShape(const PrunerPayload& payload, bool dynamic, PrunerCompoundId compoundId, const PxBounds3& bounds, const PxTransform& transform, bool hasPruningStructure)
{
mPrunerNeedsUpdating = true;
const PxU32 index = PxU32(dynamic);
if(!index)
invalidateStaticTimestamp();
PrunerHandle handle;
if(compoundId == INVALID_COMPOUND_ID)
{
PX_ASSERT(mPrunerExt[index].pruner());
mPrunerExt[index].pruner()->addObjects(&handle, &bounds, &payload, &transform, 1, hasPruningStructure);
//mPrunerExt[index].growDirtyList(handle);
}
else
{
PX_ASSERT(mCompoundPrunerExt.pruner());
mCompoundPrunerExt.pruner()->addObject(compoundId, handle, bounds, payload, transform);
}
return createPrunerData(index, handle);
}
void PrunerManager::removePrunerShape(PrunerCompoundId compoundId, PrunerData data, PrunerPayloadRemovalCallback* removalCallback)
{
mPrunerNeedsUpdating = true;
const PxU32 index = getPrunerIndex(data);
const PrunerHandle handle = getPrunerHandle(data);
if(!index)
invalidateStaticTimestamp();
if(compoundId == INVALID_COMPOUND_ID)
{
PX_ASSERT(mPrunerExt[index].pruner());
mPrunerExt[index].removeFromDirtyList(handle);
mPrunerExt[index].pruner()->removeObjects(&handle, 1, removalCallback);
}
else
{
mCompoundPrunerExt.removeFromDirtyList(compoundId, handle);
mCompoundPrunerExt.pruner()->removeObject(compoundId, handle, removalCallback);
}
}
void PrunerManager::markForUpdate(PrunerCompoundId compoundId, PrunerData data, const PxTransform& transform)
{
mPrunerNeedsUpdating = true;
const PxU32 index = getPrunerIndex(data);
const PrunerHandle handle = getPrunerHandle(data);
if(!index)
invalidateStaticTimestamp();
if(compoundId == INVALID_COMPOUND_ID)
// PT: TODO: at this point do we still need a dirty list? we could just update the bounds directly?
mPrunerExt[index].addToDirtyList(handle, index!=0, transform);
else
mCompoundPrunerExt.addToDirtyList(compoundId, handle, transform);
}
void PrunerManager::setDynamicTreeRebuildRateHint(PxU32 rebuildRateHint)
{
mRebuildRateHint = rebuildRateHint;
for(PxU32 i=0;i<PruningIndex::eCOUNT;i++)
{
Pruner* pruner = mPrunerExt[i].pruner();
if(pruner && pruner->isDynamic())
static_cast<DynamicPruner*>(pruner)->setRebuildRateHint(rebuildRateHint);
}
}
void PrunerManager::afterSync(bool buildStep, bool commit)
{
PX_PROFILE_ZONE("Sim.sceneQueryBuildStep", mContextID);
if(!buildStep && !commit)
{
mPrunerNeedsUpdating = true;
return;
}
// flush user modified objects
flushShapes();
for(PxU32 i=0; i<PruningIndex::eCOUNT; i++)
{
Pruner* pruner = mPrunerExt[i].pruner();
if(pruner)
{
if(pruner->isDynamic())
static_cast<DynamicPruner*>(pruner)->buildStep(true);
if(commit)
pruner->commit();
}
}
mPrunerNeedsUpdating = !commit;
}
void PrunerManager::flushShapes()
{
PX_PROFILE_ZONE("SceneQuery.flushShapes", mContextID);
// must already have acquired writer lock here
const float inflation = 1.0f + mInflation;
bool mustInvalidateStaticTimestamp = false;
for(PxU32 i=0; i<PruningIndex::eCOUNT; i++)
{
if(mPrunerExt[i].processDirtyList(i, mAdapter, inflation))
mustInvalidateStaticTimestamp = true;
}
if(mustInvalidateStaticTimestamp)
invalidateStaticTimestamp();
mCompoundPrunerExt.flushShapes(mAdapter, inflation);
}
void PrunerManager::flushUpdates()
{
PX_PROFILE_ZONE("SceneQuery.flushUpdates", mContextID);
if(mPrunerNeedsUpdating)
{
// no need to take lock if manual sq update is enabled
// as flushUpdates will only be called from NpScene::flushQueryUpdates()
mSQLock.lock();
if(mPrunerNeedsUpdating)
{
flushShapes();
for(PxU32 i=0; i<PruningIndex::eCOUNT; i++)
if(mPrunerExt[i].pruner())
mPrunerExt[i].pruner()->commit();
PxMemoryBarrier();
mPrunerNeedsUpdating = false;
}
mSQLock.unlock();
}
}
void PrunerManager::forceRebuildDynamicTree(PxU32 prunerIndex)
{
PX_PROFILE_ZONE("SceneQuery.forceDynamicTreeRebuild", mContextID);
PxMutex::ScopedLock lock(mSQLock);
Pruner* pruner = mPrunerExt[prunerIndex].pruner();
if(pruner && pruner->isDynamic())
{
static_cast<DynamicPruner*>(pruner)->purge();
static_cast<DynamicPruner*>(pruner)->commit();
}
}
void* PrunerManager::prepareSceneQueriesUpdate(PruningIndex::Enum index)
{
bool retVal = false;
Pruner* pruner = mPrunerExt[index].pruner();
if(pruner && pruner->isDynamic())
retVal = static_cast<DynamicPruner*>(pruner)->prepareBuild();
return retVal ? pruner : NULL;
}
void PrunerManager::sceneQueryBuildStep(void* handle)
{
PX_PROFILE_ZONE("SceneQuery.sceneQueryBuildStep", mContextID);
Pruner* pruner = reinterpret_cast<Pruner*>(handle);
if(pruner && pruner->isDynamic())
{
const bool buildFinished = static_cast<DynamicPruner*>(pruner)->buildStep(false);
if(buildFinished)
mPrunerNeedsUpdating = true;
}
}
// PT: TODO: revisit this. Perhaps it should be the user's responsibility to call the pruner's
// visualize functions directly, when & how he wants.
void PrunerManager::visualize(PxU32 prunerIndex, PxRenderOutput& out) const
{
if(prunerIndex==PX_SCENE_PRUNER_STATIC)
{
if(getPruner(PruningIndex::eSTATIC))
getPruner(PruningIndex::eSTATIC)->visualize(out, SQ_DEBUG_VIZ_STATIC_COLOR, SQ_DEBUG_VIZ_STATIC_COLOR2);
}
else if(prunerIndex==PX_SCENE_PRUNER_DYNAMIC)
{
if(getPruner(PruningIndex::eDYNAMIC))
getPruner(PruningIndex::eDYNAMIC)->visualize(out, SQ_DEBUG_VIZ_DYNAMIC_COLOR, SQ_DEBUG_VIZ_DYNAMIC_COLOR2);
}
else if(prunerIndex==PX_SCENE_COMPOUND_PRUNER)
{
const CompoundPruner* cp = mCompoundPrunerExt.pruner();
if(cp)
cp->visualizeEx(out, SQ_DEBUG_VIZ_COMPOUND_COLOR, true, true);
}
}
void PrunerManager::shiftOrigin(const PxVec3& shift)
{
for(PxU32 i=0; i<PruningIndex::eCOUNT; i++)
mPrunerExt[i].pruner()->shiftOrigin(shift);
mCompoundPrunerExt.pruner()->shiftOrigin(shift);
}
void PrunerManager::addCompoundShape(const PxBVH& pxbvh, PrunerCompoundId compoundId, const PxTransform& compoundTransform, PrunerData* prunerData, const PrunerPayload* payloads, const PxTransform* transforms, bool isDynamic)
{
const BVH& bvh = static_cast<const BVH&>(pxbvh);
const PxU32 nbShapes = bvh.Gu::BVH::getNbBounds();
PX_ALLOCA(res, PrunerHandle, nbShapes);
PX_ASSERT(mCompoundPrunerExt.mPruner);
mCompoundPrunerExt.mPruner->addCompound(res, bvh, compoundId, compoundTransform, isDynamic, payloads, transforms);
const PxU32 index = PxU32(isDynamic);
if(!index)
invalidateStaticTimestamp();
for(PxU32 i = 0; i < nbShapes; i++)
prunerData[i] = createPrunerData(index, res[i]);
}
void PrunerManager::updateCompoundActor(PrunerCompoundId compoundId, const PxTransform& compoundTransform)
{
PX_ASSERT(mCompoundPrunerExt.mPruner);
const bool isDynamic = mCompoundPrunerExt.mPruner->updateCompound(compoundId, compoundTransform);
if(!isDynamic)
invalidateStaticTimestamp();
}
void PrunerManager::removeCompoundActor(PrunerCompoundId compoundId, PrunerPayloadRemovalCallback* removalCallback)
{
PX_ASSERT(mCompoundPrunerExt.mPruner);
const bool isDynamic = mCompoundPrunerExt.mPruner->removeCompound(compoundId, removalCallback);
if(!isDynamic)
invalidateStaticTimestamp();
}
void PrunerManager::sync(const PrunerHandle* handles, const PxU32* boundsIndices, const PxBounds3* bounds, const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices)
{
if(!count)
return;
Pruner* dynamicPruner = getPruner(PruningIndex::eDYNAMIC);
if(!dynamicPruner)
return;
PxU32 startIndex = 0;
PxU32 numIndices = count;
// if shape sim map is not empty, parse the indices and skip update for the dirty one
if(ignoredIndices.count())
{
// PT: I think this codepath was used with SCB / buffered changes, but it's not needed anymore
numIndices = 0;
for(PxU32 i=0; i<count; i++)
{
// if(ignoredIndices.test(boundsIndices[i]))
if(ignoredIndices.boundedTest(boundsIndices[i]))
{
dynamicPruner->updateObjects(handles + startIndex, numIndices, mInflation, boundsIndices + startIndex, bounds, transforms);
numIndices = 0;
startIndex = i + 1;
}
else
numIndices++;
}
// PT: we fallback to the next line on purpose - no "else"
}
dynamicPruner->updateObjects(handles + startIndex, numIndices, mInflation, boundsIndices + startIndex, bounds, transforms);
}

File diff suppressed because it is too large Load Diff