aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/GeomUtils/src/GuBounds.cpp
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/GeomUtils/src/GuBounds.cpp
downloadphysx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz
physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip
Initial commit:
PhysX 3.4.0 Update @ 21294896 APEX 1.4.0 Update @ 21275617 [CL 21300167]
Diffstat (limited to 'PhysX_3.4/Source/GeomUtils/src/GuBounds.cpp')
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/GuBounds.cpp641
1 files changed, 641 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/GeomUtils/src/GuBounds.cpp b/PhysX_3.4/Source/GeomUtils/src/GuBounds.cpp
new file mode 100644
index 00000000..13560311
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/GuBounds.cpp
@@ -0,0 +1,641 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 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 "GuBounds.h"
+#include "PxBoxGeometry.h"
+#include "PxSphereGeometry.h"
+#include "PxCapsuleGeometry.h"
+#include "PxPlaneGeometry.h"
+#include "PxConvexMeshGeometry.h"
+#include "PxTriangleMeshGeometry.h"
+#include "PxHeightFieldGeometry.h"
+#include "GuInternal.h"
+#include "CmUtils.h"
+#include "GuConvexMesh.h"
+#include "GuConvexMeshData.h"
+#include "GuTriangleMesh.h"
+#include "GuHeightFieldData.h"
+#include "GuHeightField.h"
+#include "PsFoundation.h"
+#include "GuConvexUtilsInternal.h"
+#include "GuBoxConversion.h"
+
+using namespace physx;
+using namespace Gu;
+using namespace Ps::aos;
+
+static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxMat33& rot, const PxVec3& pos, const CenterExtentsPadded& bounds)
+{
+ c = rot.transform(bounds.mCenter) + pos;
+ ext = Cm::basisExtent(rot.column0, rot.column1, rot.column2, bounds.mExtents);
+}
+
+// PT: this one may have duplicates in GuBV4_BoxSweep_Internal.h & GuBV4_Raycast.cpp
+static PX_FORCE_INLINE Vec4V multiply3x3V(const Vec4V p, const PxMat33Padded& mat_Padded)
+{
+ Vec4V ResV = V4Scale(V4LoadU(&mat_Padded.column0.x), V4GetX(p));
+ ResV = V4Add(ResV, V4Scale(V4LoadU(&mat_Padded.column1.x), V4GetY(p)));
+ ResV = V4Add(ResV, V4Scale(V4LoadU(&mat_Padded.column2.x), V4GetZ(p)));
+ return ResV;
+}
+
+static PX_FORCE_INLINE void transformNoEmptyTestV(Vec3p& c, Vec3p& ext, const PxMat33Padded& rot, const PxVec3& pos, const CenterExtentsPadded& bounds)
+{
+ const Vec4V boundsCenterV = V4LoadU(&bounds.mCenter.x); // PT: this load is safe since extents follow center in the class
+
+ // PT: unfortunately we can't V4LoadU 'pos' directly (it can come directly from users!). So we have to live with this for now:
+ const Vec4V posV = Vec4V_From_Vec3V(V3LoadU(&pos.x));
+ // PT: but eventually we'd like to use the "unsafe" version (e.g. by switching p&q in PxTransform), which would save 6 instructions on Win32
+ const Vec4V cV = V4Add(multiply3x3V(boundsCenterV, rot), posV);
+// const Vec4V cV = V4Add(multiply3x3V(boundsCenterV, rot), V4LoadU(&pos.x)); // ### unsafe
+ V4StoreU(cV, &c.x);
+
+ // extended basis vectors
+ const Vec4V boundsExtentsV = V4LoadU(&bounds.mExtents.x); // PT: this load is safe since bounds are padded
+ const Vec4V c0V = V4Scale(V4LoadU(&rot.column0.x), V4GetX(boundsExtentsV));
+ const Vec4V c1V = V4Scale(V4LoadU(&rot.column1.x), V4GetY(boundsExtentsV));
+ const Vec4V c2V = V4Scale(V4LoadU(&rot.column2.x), V4GetZ(boundsExtentsV));
+
+ // find combination of base vectors that produces max. distance for each component = sum of abs()
+ Vec4V extentsV = V4Add(V4Abs(c0V), V4Abs(c1V));
+ extentsV = V4Add(extentsV, V4Abs(c2V));
+ V4StoreU(extentsV, &ext.x);
+}
+
+static PX_FORCE_INLINE PxU32 isNonIdentity(const PxVec3& scale)
+{
+ #define IEEE_1_0 0x3f800000 //!< integer representation of 1.0
+ const PxU32* binary = reinterpret_cast<const PxU32*>(&scale.x);
+ return (binary[0] - IEEE_1_0)|(binary[1] - IEEE_1_0)|(binary[2] - IEEE_1_0);
+}
+
+// PT: please don't inline this one - 300+ lines of rarely used code
+static void computeScaledMatrix(PxMat33Padded& rot, const PxMeshScale& scale)
+{
+ rot = rot * scale.toMat33();
+}
+
+static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxTransform& transform, const PxMeshScale& scale, const CenterExtentsPadded& bounds)
+{
+ PxMat33Padded rot(transform.q);
+
+ if(isNonIdentity(scale.scale))
+ computeScaledMatrix(rot, scale);
+
+ transformNoEmptyTestV(c, ext, rot, transform.p, bounds);
+}
+
+static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxVec3& pos, const PxMat33Padded& rot, const PxMeshScale& scale, const CenterExtentsPadded& bounds)
+{
+ if(scale.isIdentity())
+ transformNoEmptyTest(c, ext, rot, pos, bounds);
+ else
+ transformNoEmptyTest(c, ext, rot * scale.toMat33(), pos, bounds);
+}
+
+static void computeMeshBounds(const PxTransform& pose, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, const PxMeshScale& meshScale, Vec3p& origin, Vec3p& extent)
+{
+ transformNoEmptyTest(origin, extent, pose, meshScale, *localSpaceBounds);
+}
+
+static void computePlaneBounds(PxBounds3& bounds, const PxTransform& pose, float contactOffset, float inflation)
+{
+ // PT: A plane is infinite, so usually the bounding box covers the whole world.
+ // Now, in particular cases when the plane is axis-aligned, we can take
+ // advantage of this to compute a smaller bounding box anyway.
+
+ // PT: we use PX_MAX_BOUNDS_EXTENTS to be compatible with PxBounds3::setMaximal,
+ // and to make sure that the value doesn't collide with the BP's sentinels.
+ const PxF32 bigValue = PX_MAX_BOUNDS_EXTENTS;
+// const PxF32 bigValue = 1000000.0f;
+ PxVec3 minPt = PxVec3(-bigValue, -bigValue, -bigValue);
+ PxVec3 maxPt = PxVec3(bigValue, bigValue, bigValue);
+
+ const PxVec3 planeNormal = pose.q.getBasisVector0();
+ const PxPlane plane(pose.p, planeNormal);
+
+ const float nx = PxAbs(planeNormal.x);
+ const float ny = PxAbs(planeNormal.y);
+ const float nz = PxAbs(planeNormal.z);
+ const float epsilon = 1e-6f;
+ const float oneMinusEpsilon = 1.0f - epsilon;
+ if(nx>oneMinusEpsilon && ny<epsilon && nz<epsilon)
+ {
+ if(planeNormal.x>0.0f) maxPt.x = -plane.d + contactOffset;
+ else minPt.x = plane.d - contactOffset;
+ }
+ else if(nx<epsilon && ny>oneMinusEpsilon && nz<epsilon)
+ {
+ if(planeNormal.y>0.0f) maxPt.y = -plane.d + contactOffset;
+ else minPt.y = plane.d - contactOffset;
+ }
+ else if(nx<epsilon && ny<epsilon && nz>oneMinusEpsilon)
+ {
+ if(planeNormal.z>0.0f) maxPt.z = -plane.d + contactOffset;
+ else minPt.z = plane.d - contactOffset;
+ }
+
+ // PT: it is important to compute the min/max form directly without going through the
+ // center/extents intermediate form. With PX_MAX_BOUNDS_EXTENTS, those back-and-forth
+ // computations destroy accuracy.
+
+ // PT: inflation actually destroys the bounds really. We keep it to please UTs but this is broken (DE10595).
+ // (e.g. for SQ 1% of PX_MAX_BOUNDS_EXTENTS is still a huge number, effectively making the AABB infinite and defeating the point of the above computation)
+ if(inflation!=1.0f)
+ {
+ const PxVec3 c = (maxPt + minPt)*0.5f;
+ const PxVec3 e = (maxPt - minPt)*0.5f*inflation;
+ minPt = c - e;
+ maxPt = c + e;
+ }
+
+ bounds.minimum = minPt;
+ bounds.maximum = maxPt;
+}
+
+static PX_FORCE_INLINE void inflateBounds(PxBounds3& bounds, const Vec3p& origin, const Vec3p& extents, float contactOffset, float inflation)
+{
+ Vec4V extentsV = V4LoadU(&extents.x);
+ extentsV = V4Add(extentsV, V4Load(contactOffset));
+ extentsV = V4Scale(extentsV, FLoad(inflation));
+
+ const Vec4V originV = V4LoadU(&origin.x);
+ const Vec4V minV = V4Sub(originV, extentsV);
+ const Vec4V maxV = V4Add(originV, extentsV);
+
+ StoreBounds(bounds, minV, maxV);
+}
+
+static PX_FORCE_INLINE Vec4V basisExtentV(const PxMat33Padded& basis, const PxVec3& extent, float offset, float inflation)
+{
+ // extended basis vectors
+ const Vec4V c0V = V4Scale(V4LoadU(&basis.column0.x), FLoad(extent.x));
+ const Vec4V c1V = V4Scale(V4LoadU(&basis.column1.x), FLoad(extent.y));
+ const Vec4V c2V = V4Scale(V4LoadU(&basis.column2.x), FLoad(extent.z));
+
+ // find combination of base vectors that produces max. distance for each component = sum of abs()
+ Vec4V extentsV = V4Add(V4Abs(c0V), V4Abs(c1V));
+ extentsV = V4Add(extentsV, V4Abs(c2V));
+ extentsV = V4Add(extentsV, V4Load(offset));
+ extentsV = V4Scale(extentsV, FLoad(inflation));
+ return extentsV;
+}
+
+void Gu::computeBounds(PxBounds3& bounds, const PxGeometry& geometry, const PxTransform& pose, float contactOffset, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, float inflation, bool extrudeHeightfields)
+{
+ PX_ASSERT(contactOffset==0.0f || inflation==1.0f);
+
+ // Box, Convex, Mesh and HeightField will compute local bounds and pose to world space.
+ // Sphere, Capsule & Plane will compute world space bounds directly.
+
+ switch(geometry.getType())
+ {
+ case PxGeometryType::eSPHERE:
+ {
+ PX_ASSERT(!localSpaceBounds);
+
+ const PxSphereGeometry& shape = static_cast<const PxSphereGeometry&>(geometry);
+ const PxVec3 extents((shape.radius+contactOffset)*inflation);
+ bounds.minimum = pose.p - extents;
+ bounds.maximum = pose.p + extents;
+ }
+ break;
+
+ case PxGeometryType::ePLANE:
+ {
+ PX_ASSERT(!localSpaceBounds);
+
+ computePlaneBounds(bounds, pose, contactOffset, inflation);
+ }
+ break;
+
+ case PxGeometryType::eCAPSULE:
+ {
+ PX_ASSERT(!localSpaceBounds);
+
+ const PxCapsuleGeometry& shape = static_cast<const PxCapsuleGeometry& >(geometry);
+ const PxVec3 d = pose.q.getBasisVector0();
+ PxVec3 extents;
+ for(PxU32 ax = 0; ax<3; ax++)
+ extents[ax] = (PxAbs(d[ax]) * shape.halfHeight + shape.radius + contactOffset)*inflation;
+ bounds.minimum = pose.p - extents;
+ bounds.maximum = pose.p + extents;
+ }
+ break;
+
+ case PxGeometryType::eBOX:
+ {
+ PX_ASSERT(!localSpaceBounds);
+
+ const PxBoxGeometry& shape = static_cast<const PxBoxGeometry& >(geometry);
+
+ const Vec3p origin(pose.p);
+
+ const PxMat33Padded basis(pose.q);
+
+ const Vec4V extentsV = basisExtentV(basis, shape.halfExtents, contactOffset, inflation);
+
+ const Vec4V originV = V4LoadU(&origin.x);
+ const Vec4V minV = V4Sub(originV, extentsV);
+ const Vec4V maxV = V4Add(originV, extentsV);
+
+ StoreBounds(bounds, minV, maxV);
+ }
+ break;
+
+ case PxGeometryType::eCONVEXMESH:
+ {
+ const PxConvexMeshGeometry& shape = static_cast<const PxConvexMeshGeometry& >(geometry);
+ const Gu::ConvexHullData& hullData = static_cast<const Gu::ConvexMesh*>(shape.convexMesh)->getHull();
+
+ const bool useTightBounds = shape.meshFlags & PxConvexMeshGeometryFlag::eTIGHT_BOUNDS;
+ if(useTightBounds)
+ {
+ PxMat33Padded rot(pose.q);
+
+ if(isNonIdentity(shape.scale.scale))
+ computeScaledMatrix(rot, shape.scale);
+
+ PxU32 nb = hullData.mNbHullVertices;
+ const PxVec3* v = hullData.getHullVertices();
+ Vec4V minV;
+ Vec4V maxV;
+
+ {
+ const Vec4V vertexV = multiply3x3V(V4LoadU(&v->x), rot);
+ v++;
+
+ minV = vertexV;
+ maxV = vertexV;
+ nb--;
+ }
+
+ while(nb--)
+ {
+ const Vec4V vertexV = multiply3x3V(V4LoadU(&v->x), rot);
+ v++;
+
+ minV = V4Min(minV, vertexV);
+ maxV = V4Max(maxV, vertexV);
+ }
+
+ const Vec4V offsetV = V4Load(contactOffset);
+ minV = V4Sub(minV, offsetV);
+ maxV = V4Add(maxV, offsetV);
+
+ const Vec4V posV = Vec4V_From_Vec3V(V3LoadU(&pose.p.x));
+ maxV = V4Add(maxV, posV);
+ minV = V4Add(minV, posV);
+
+ // Inflation
+ {
+ const Vec4V centerV = V4Scale(V4Add(maxV, minV), FLoad(0.5f));
+ const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), FLoad(0.5f*inflation));
+ maxV = V4Add(centerV, extentsV);
+ minV = V4Sub(centerV, extentsV);
+ }
+
+ StoreBounds(bounds, minV, maxV);
+ }
+ else
+ {
+ Vec3p origin, extents;
+ computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &hullData.getPaddedBounds(), shape.scale, origin, extents);
+
+ inflateBounds(bounds, origin, extents, contactOffset, inflation);
+ }
+ }
+ break;
+
+ case PxGeometryType::eTRIANGLEMESH:
+ {
+ Vec3p origin, extents;
+ const PxTriangleMeshGeometry& shape = static_cast<const PxTriangleMeshGeometry& >(geometry);
+ computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &static_cast<const Gu::TriangleMesh*>(shape.triangleMesh)->getPaddedBounds(), shape.scale, origin, extents);
+
+ inflateBounds(bounds, origin, extents, contactOffset, inflation);
+ }
+ break;
+
+ case PxGeometryType::eHEIGHTFIELD:
+ {
+ const PxHeightFieldGeometry& shape = static_cast<const PxHeightFieldGeometry& >(geometry);
+ const PxMeshScale scale(PxVec3(shape.rowScale, shape.heightScale, shape.columnScale), PxQuat(PxIdentity));
+
+ const Gu::HeightFieldData& data = static_cast<const Gu::HeightField*>(shape.heightField)->getData();
+ //Get the center and extents of the hf.
+ CenterExtentsPadded cep;
+ if(localSpaceBounds)
+ {
+ cep = *localSpaceBounds;
+ }
+ else
+ {
+ cep = static_cast<Gu::HeightField*>(shape.heightField)->getData().getPaddedBounds();
+ }
+
+ //Add on the thickness.
+ //The thickness is an absolute quantity and is not to be multiplied by the height-scale of the hf.
+ //To enforce this we need to divide the thickness by the scale because computeMeshBounds multiplies by the scale.
+ //Another way of expressing this is that computeMeshBounds uses unscaled coordinates as input so we need to express the thickness
+ //in unscaled coordinates too so that we may legally add it to the extents.
+ bounds.minimum = cep.getMin();
+ bounds.maximum = cep.getMax();
+
+ if (extrudeHeightfields)
+ {
+ const PxF32 thickness = data.thickness;
+ const PxReal thicknessScaled = thickness / shape.heightScale;
+ if (thicknessScaled < 0.f)
+ bounds.minimum.y += thicknessScaled;
+ else
+ bounds.maximum.y += thicknessScaled;
+ }
+
+ //Recompute the center and extent after adding on the thickness.
+ cep.setMinMax(bounds.minimum, bounds.maximum);
+
+ //Compute and inflate the bounds from the pose, scale and center/extents.
+ Vec3p origin, extents;
+ computeMeshBounds(pose, &cep, scale, origin, extents);
+ inflateBounds(bounds, origin, extents, contactOffset, inflation);
+ }
+ break;
+ case PxGeometryType::eGEOMETRY_COUNT:
+ case PxGeometryType::eINVALID:
+ {
+ PX_ASSERT(0);
+ Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::GeometryUnion::computeBounds: Unknown shape type.");
+ }
+ }
+}
+
+// PT: TODO: refactor this with regular function
+PxF32 Gu::computeBoundsWithCCDThreshold(Vec3p& origin, Vec3p& extent, const PxGeometry& geometry, const PxTransform& pose, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds)
+{
+ // Box, Convex, Mesh and HeightField will compute local bounds and pose to world space.
+ // Sphere, Capsule & Plane will compute world space bounds directly.
+
+ const PxReal inSphereRatio = 0.75f;
+
+ //The CCD thresholds are as follows:
+ //(1) sphere = inSphereRatio * radius
+ //(2) plane = inf (we never need CCD against this shape)
+ //(3) capsule = inSphereRatio * radius
+ //(4) box = inSphereRatio * (box minimum extent axis)
+ //(5) convex = inSphereRatio * convex in-sphere * min scale
+ //(6) triangle mesh = 0.f (polygons have 0 thickness)
+ //(7) heightfields = 0.f (polygons have 0 thickness)
+
+ //The decision to enter CCD depends on the sum of the shapes' CCD thresholds. One of the 2 shapes must be a
+ //sphere/capsule/box/convex so the sum of the CCD thresholds will be non-zero.
+
+ switch (geometry.getType())
+ {
+ case PxGeometryType::eSPHERE:
+ {
+ PX_ASSERT(!localSpaceBounds);
+
+ const PxSphereGeometry& shape = static_cast<const PxSphereGeometry&>(geometry);
+ origin = pose.p;
+ extent = PxVec3(shape.radius, shape.radius, shape.radius);
+ return shape.radius*inSphereRatio;
+ }
+ case PxGeometryType::ePLANE:
+ {
+ PX_ASSERT(!localSpaceBounds);
+
+ PxBounds3 bounds;
+ computePlaneBounds(bounds, pose, 0.0f, 1.0f);
+ origin = bounds.getCenter();
+ extent = bounds.getExtents();
+ return PX_MAX_REAL;
+ }
+ case PxGeometryType::eCAPSULE:
+ {
+ PX_ASSERT(!localSpaceBounds);
+
+ const PxCapsuleGeometry& shape = static_cast<const PxCapsuleGeometry&>(geometry);
+ origin = pose.p;
+ const PxVec3 d = pose.q.getBasisVector0();
+ for(PxU32 ax = 0; ax<3; ax++)
+ extent[ax] = PxAbs(d[ax]) * shape.halfHeight + shape.radius;
+ return shape.radius * inSphereRatio;
+ }
+
+ case PxGeometryType::eBOX:
+ {
+ PX_ASSERT(!localSpaceBounds);
+
+ const PxBoxGeometry& shape = static_cast<const PxBoxGeometry&>(geometry);
+
+ const PxMat33 rot(pose.q);
+ extent = Cm::basisExtent(rot.column0, rot.column1, rot.column2, shape.halfExtents);
+
+ origin = pose.p;
+
+ return PxMin(PxMin(shape.halfExtents.x, shape.halfExtents.y), shape.halfExtents.z)*inSphereRatio;
+ }
+
+ case PxGeometryType::eCONVEXMESH:
+ {
+ const PxConvexMeshGeometry& shape = static_cast<const PxConvexMeshGeometry&>(geometry);
+ const Gu::ConvexHullData& hullData = static_cast<const Gu::ConvexMesh*>(shape.convexMesh)->getHull();
+ computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &hullData.getPaddedBounds(), shape.scale, origin, extent);
+ return PxMin(shape.scale.scale.z, PxMin(shape.scale.scale.x, shape.scale.scale.y)) * hullData.mInternal.mRadius * inSphereRatio;
+ }
+
+ case PxGeometryType::eTRIANGLEMESH:
+ {
+ const PxTriangleMeshGeometry& shape = static_cast<const PxTriangleMeshGeometry&>(geometry);
+ computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &static_cast<const Gu::TriangleMesh*>(shape.triangleMesh)->getPaddedBounds(), shape.scale, origin, extent);
+ return 0.0f;
+ }
+
+ case PxGeometryType::eHEIGHTFIELD:
+ {
+ const PxHeightFieldGeometry& shape = static_cast<const PxHeightFieldGeometry&>(geometry);
+ const PxMeshScale scale(PxVec3(shape.rowScale, shape.heightScale, shape.columnScale), PxQuat(PxIdentity));
+ const Gu::HeightFieldData& data = static_cast<const Gu::HeightField*>(shape.heightField)->getData();
+ computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &data.getPaddedBounds(), scale, origin, extent);
+ return 0.f;
+ }
+
+ case PxGeometryType::eGEOMETRY_COUNT:
+ case PxGeometryType::eINVALID:
+ {
+ PX_ASSERT(0);
+ Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::GeometryUnion::computeBounds: Unknown shape type.");
+ }
+ }
+ return PX_MAX_REAL;
+}
+
+
+static PX_FORCE_INLINE void computeBoxExtentsAroundCapsule(PxVec3& extents, const PxCapsuleGeometry& capsuleGeom, float inflation)
+{
+ extents.x = (capsuleGeom.radius + capsuleGeom.halfHeight) * inflation;
+ extents.y = capsuleGeom.radius * inflation;
+ extents.z = capsuleGeom.radius * inflation;
+}
+
+static const PxReal SQ_PRUNER_INFLATION = 1.01f; // pruner test shape inflation (not narrow phase shape)
+
+static void computeMeshBounds(const PxVec3& pos, const PxMat33Padded& rot, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, const PxMeshScale& meshScale, Vec3p& origin, Vec3p& extent)
+{
+ Ps::prefetchLine(localSpaceBounds); // PT: this one helps reducing L2 misses in transformNoEmptyTest
+ transformNoEmptyTest(origin, extent, pos, rot, meshScale, *localSpaceBounds);
+}
+
+// PT: warning: this writes 4 bytes after the end of 'bounds'. Calling code must ensure it is safe to do so.
+static PX_FORCE_INLINE void computeMinMaxBounds(PxBounds3* PX_RESTRICT bounds, const Vec3p& c, const Vec3p& e, float prunerInflation, float offset)
+{
+ const Vec4V extentsV = V4Scale(V4Add(V4LoadU(&e.x), V4Load(offset)), FLoad(prunerInflation));
+ const Vec4V centerV = V4LoadU(&c.x);
+ const Vec4V minV = V4Sub(centerV, extentsV);
+ const Vec4V maxV = V4Add(centerV, extentsV);
+ V4StoreU(minV, &bounds->minimum.x);
+ V4StoreU(maxV, &bounds->maximum.x);
+}
+
+ShapeData::ShapeData(const PxGeometry& g, const PxTransform& t, PxReal inflation)
+{
+ using namespace physx::shdfnd::aos;
+
+ // PT: this cast to matrix is already done in GeometryUnion::computeBounds (e.g. for boxes). So we do it first,
+ // then we'll pass the matrix directly to computeBoundsShapeData, to avoid the double conversion.
+ const bool isOBB = PxAbs(t.q.w) < 0.999999f;
+ if(isOBB)
+ {
+ // PT: writes 4 bytes after 'rot' but it's safe since we then write 'center' just afterwards
+ buildFrom(mGuBox, t.q);
+ }
+ else
+ {
+ mGuBox.rot = PxMat33(PxIdentity);
+ }
+
+ // PT: can't use V4Load here since there's no guarantee on 't.p'
+ // PT: must store 'center' after 'rot' now
+ mGuBox.center = t.p;
+
+ // Compute AABB, used by the BucketPruner as cullBox
+ switch(g.getType())
+ {
+ case PxGeometryType::eSPHERE:
+ {
+ const PxSphereGeometry& shape = static_cast<const PxSphereGeometry&>(g);
+ computeMinMaxBounds(&mPrunerInflatedAABB, mGuBox.center, PxVec3(0.0f), SQ_PRUNER_INFLATION, shape.radius+inflation);
+
+ //
+
+ reinterpret_cast<Sphere&>(mGuSphere) = Sphere(t.p, shape.radius);
+ }
+ break;
+
+ case PxGeometryType::eCAPSULE:
+ {
+ const PxCapsuleGeometry& shape = static_cast<const PxCapsuleGeometry&>(g);
+ const Vec3p extents = mGuBox.rot.column0.abs() * shape.halfHeight;
+ computeMinMaxBounds(&mPrunerInflatedAABB, mGuBox.center, extents, SQ_PRUNER_INFLATION, shape.radius+inflation);
+
+ //
+
+ Capsule& dstWorldCapsule = reinterpret_cast<Capsule&>(mGuCapsule); // store a narrow phase version copy
+ getCapsule(dstWorldCapsule, shape, t);
+
+ mGuBox.extents.x = shape.halfHeight;
+
+ // compute PxBoxGeometry pruner geom around input capsule geom; transform remains unchanged
+
+ computeBoxExtentsAroundCapsule(mPrunerBoxGeomExtents, shape, SQ_PRUNER_INFLATION);
+ }
+ break;
+
+ case PxGeometryType::eBOX:
+ {
+ const PxBoxGeometry& shape = static_cast<const PxBoxGeometry&>(g);
+ // PT: cast is safe because 'rot' followed by other members
+ Vec4V extentsV = basisExtentV(static_cast<const PxMat33Padded&>(mGuBox.rot), shape.halfExtents, inflation, SQ_PRUNER_INFLATION);
+
+ // PT: c/e-to-m/M conversion
+ const Vec4V centerV = V4LoadU(&mGuBox.center.x);
+ const Vec4V minV = V4Sub(centerV, extentsV);
+ const Vec4V maxV = V4Add(centerV, extentsV);
+ V4StoreU(minV, &mPrunerInflatedAABB.minimum.x);
+ V4StoreU(maxV, &mPrunerInflatedAABB.maximum.x); // PT: WARNING: writes past end of class
+
+ //
+
+ mGuBox.extents = shape.halfExtents; // PT: TODO: use SIMD
+ mPrunerBoxGeomExtents = shape.halfExtents*SQ_PRUNER_INFLATION;
+ }
+ break;
+
+ case PxGeometryType::eCONVEXMESH:
+ {
+ const PxConvexMeshGeometry& shape = static_cast<const PxConvexMeshGeometry&>(g);
+
+ const ConvexMesh* cm = static_cast<const ConvexMesh*>(shape.convexMesh);
+ const ConvexHullData* hullData = &cm->getHull();
+
+ // PT: cast is safe since 'rot' is followed by other members of the box
+ Vec3p center, extents;
+ computeMeshBounds(mGuBox.center, static_cast<const PxMat33Padded&>(mGuBox.rot), &hullData->getPaddedBounds(), shape.scale, center, extents);
+
+ computeMinMaxBounds(&mPrunerInflatedAABB, center, extents, SQ_PRUNER_INFLATION, inflation);
+
+ //
+
+ Box prunerBox;
+ computeOBBAroundConvex(prunerBox, shape, cm, t);
+ mGuBox.rot = prunerBox.rot; // PT: TODO: optimize this copy
+
+ // AP: pruners are now responsible for growing the OBB by 1% for overlap/sweep/GJK accuracy
+ mPrunerBoxGeomExtents = prunerBox.extents*SQ_PRUNER_INFLATION;
+ mGuBox.center = prunerBox.center;
+ }
+ break;
+
+ case PxGeometryType::ePLANE:
+ case PxGeometryType::eTRIANGLEMESH:
+ case PxGeometryType::eHEIGHTFIELD:
+ case PxGeometryType::eGEOMETRY_COUNT:
+ case PxGeometryType::eINVALID:
+ PX_ALWAYS_ASSERT_MESSAGE("PhysX internal error: Invalid shape in ShapeData contructor.");
+ }
+
+ // PT: WARNING: these writes must stay after the above code
+ mIsOBB = PxU32(isOBB);
+ mType = PxU16(g.getType());
+}
+
+
+
+