// 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 GU_BOUNDS_H #define GU_BOUNDS_H #include "foundation/PxBounds3.h" #include "foundation/PxFlags.h" #include "foundation/PxVecMath.h" #include "geometry/PxGeometry.h" #include "geometry/PxCapsuleGeometry.h" #include #include "GuBox.h" #include "GuCenterExtents.h" #include "GuSphere.h" #include "GuCapsule.h" // PT: the PX_MAX_BOUNDS_EXTENTS value is too large and produces INF floats when the box values are squared in // some collision routines. Thus, for the SQ subsystem we use this alternative (smaller) value to mark empty bounds. // See PX-954 for details. #define GU_EMPTY_BOUNDS_EXTENTS PxSqrt(0.25f * 1e33f) namespace physx { class PxMeshScale; namespace Gu { PX_FORCE_INLINE void computeCapsuleBounds(PxBounds3& bounds, const PxCapsuleGeometry& capsuleGeom, const PxTransform& pose, float contactOffset=0.0f, float inflation=1.0f) { const PxVec3 d = pose.q.getBasisVector0(); PxVec3 extents; for(PxU32 ax = 0; ax<3; ax++) extents[ax] = (PxAbs(d[ax]) * capsuleGeom.halfHeight + capsuleGeom.radius + contactOffset)*inflation; bounds.minimum = pose.p - extents; bounds.maximum = pose.p + extents; } //'contactOffset' and 'inflation' should not be used at the same time, i.e. either contactOffset==0.0f, or inflation==1.0f PX_PHYSX_COMMON_API void computeBounds(PxBounds3& bounds, const PxGeometry& geometry, const PxTransform& transform, float contactOffset, float inflation); //AABB in world space. PX_FORCE_INLINE PxBounds3 computeBounds(const PxGeometry& geometry, const PxTransform& pose) { PxBounds3 bounds; computeBounds(bounds, geometry, pose, 0.0f, 1.0f); return bounds; } void computeGlobalBox(PxBounds3& bounds, PxU32 nbPrims, const PxBounds3* PX_RESTRICT boxes, const PxU32* PX_RESTRICT primitives); PX_PHYSX_COMMON_API void computeBoundsAroundVertices(PxBounds3& bounds, PxU32 nbVerts, const PxVec3* PX_RESTRICT verts); PX_PHYSX_COMMON_API void computeTightBounds(PxBounds3& bounds, PxU32 nbVerts, const PxVec3* PX_RESTRICT verts, const PxTransform& pose, const PxMeshScale& scale, float contactOffset, float inflation); PX_PHYSX_COMMON_API void computeLocalBoundsAndGeomEpsilon(const PxVec3* vertices, PxU32 nbVerties, PxBounds3& localBounds, PxReal& geomEpsilon); #define StoreBounds(bounds, minV, maxV) \ V4StoreU(minV, &bounds.minimum.x); \ PX_ALIGN(16, PxVec4) max4; \ V4StoreA(maxV, &max4.x); \ bounds.maximum = PxVec3(max4.x, max4.y, max4.z); // PT: TODO: - refactor with "inflateBounds" in GuBounds.cpp if possible template PX_FORCE_INLINE void inflateBounds(PxBounds3& dst, const PxBounds3& src, float enlargement) { const float coeff = 0.5f * enlargement; if(useSIMD) { using namespace physx::aos; Vec4V minV = V4LoadU(&src.minimum.x); Vec4V maxV = V4LoadU(&src.maximum.x); const Vec4V eV = V4Scale(V4Sub(maxV, minV), FLoad(coeff)); minV = V4Sub(minV, eV); maxV = V4Add(maxV, eV); StoreBounds(dst, minV, maxV); } else { // PT: this clumsy but necessary second codepath is used to read the last bound of the array // (making sure we don't V4LoadU invalid memory). Implementation must stay in sync with the // main codepath above. No, this is not very nice. const PxVec3& minV = src.minimum; const PxVec3& maxV = src.maximum; const PxVec3 eV = (maxV - minV) * coeff; dst.minimum = minV - eV; dst.maximum = maxV + eV; } } class ShapeData { public: PX_PHYSX_COMMON_API ShapeData(const PxGeometry& g, const PxTransform& t, PxReal inflation); // PT: used by overlaps (box, capsule, convex) PX_FORCE_INLINE const PxVec3& getPrunerBoxGeomExtentsInflated() const { return mPrunerBoxGeomExtents; } // PT: used by overlaps (box, capsule, convex) PX_FORCE_INLINE const PxVec3& getPrunerWorldPos() const { return mGuBox.center; } PX_FORCE_INLINE const PxBounds3& getPrunerInflatedWorldAABB() const { return mPrunerInflatedAABB; } // PT: used by overlaps (box, capsule, convex) PX_FORCE_INLINE const PxMat33& getPrunerWorldRot33() const { return mGuBox.rot; } // PT: this one only used by overlaps so far (for sphere shape, pruner level) PX_FORCE_INLINE const Gu::Sphere& getGuSphere() const { PX_ASSERT(mType == PxGeometryType::eSPHERE); return reinterpret_cast(mGuSphere); } // PT: this one only used by sweeps so far (for box shape, NP level) PX_FORCE_INLINE const Gu::Box& getGuBox() const { PX_ASSERT(mType == PxGeometryType::eBOX); return mGuBox; } // PT: this one used by sweeps (NP level) and overlaps (pruner level) - for capsule shape PX_FORCE_INLINE const Gu::Capsule& getGuCapsule() const { PX_ASSERT(mType == PxGeometryType::eCAPSULE); return reinterpret_cast(mGuCapsule); } PX_FORCE_INLINE float getCapsuleHalfHeight() const { PX_ASSERT(mType == PxGeometryType::eCAPSULE); return mGuBox.extents.x; } PX_FORCE_INLINE PxU32 isOBB() const { return PxU32(mIsOBB); } PX_FORCE_INLINE PxGeometryType::Enum getType() const { return PxGeometryType::Enum(mType); } PX_NOCOPY(ShapeData) private: // PT: box: pre-inflated box extents // capsule: pre-inflated extents of box-around-capsule // convex: pre-inflated extents of box-around-convex // sphere: not used PxVec3 mPrunerBoxGeomExtents; // used for pruners. This volume encloses but can differ from the original shape // PT: // // box center = unchanged copy of initial shape's position, except for convex (position of box around convex) // SIMD code will load it as a V4 (safe because member is not last of Gu structure) // // box rot = precomputed PxMat33 version of initial shape's rotation, except for convex (rotation of box around convex) // SIMD code will load it as V4s (safe because member is not last of Gu structure) // // box extents = non-inflated initial box extents for box shape, half-height for capsule, otherwise not used Gu::Box mGuBox; PxBounds3 mPrunerInflatedAABB; // precomputed AABB for the pruner shape PxU16 mIsOBB; // true for OBB, false for AABB. Also used as padding for mPrunerInflatedAABB, don't move. PxU16 mType; // shape's type // these union Gu shapes are only precomputed for narrow phase (not pruners), can be different from mPrunerVolume // so need separate storage union { PxU8 mGuCapsule[sizeof(Gu::Capsule)]; // 28 PxU8 mGuSphere[sizeof(Gu::Sphere)]; // 16 }; }; // PT: please make sure it fits in "one" cache line PX_COMPILE_TIME_ASSERT(sizeof(ShapeData)==128); } // namespace Gu } #endif