aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseRTree.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/mesh/GuMidphaseRTree.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/mesh/GuMidphaseRTree.cpp')
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseRTree.cpp886
1 files changed, 886 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseRTree.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseRTree.cpp
new file mode 100644
index 00000000..6133f0b8
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseRTree.cpp
@@ -0,0 +1,886 @@
+// 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 "GuSweepMesh.h"
+#include "GuIntersectionRayTriangle.h"
+#include "GuIntersectionCapsuleTriangle.h"
+#include "GuIntersectionRayBox.h"
+#include "GuIntersectionRayBoxSIMD.h"
+#include "GuSphere.h"
+#include "GuBoxConversion.h"
+#include "GuConvexUtilsInternal.h"
+#include "GuVecTriangle.h"
+#include "GuIntersectionTriangleBox.h"
+#include "GuSIMDHelpers.h"
+#include "GuTriangleVertexPointers.h"
+#include "GuTriangleMeshRTree.h"
+#include "GuInternal.h"
+
+// This file contains code specific to the RTree midphase.
+
+using namespace physx;
+using namespace Cm;
+using namespace Gu;
+using namespace physx::shdfnd::aos;
+
+struct MeshRayCollider
+{
+ template <int tInflate, int tRayTest>
+ PX_PHYSX_COMMON_API static void collide(
+ const PxVec3& orig, const PxVec3& dir, // dir is not normalized (full length), both in mesh space (unless meshWorld is non-zero)
+ PxReal maxT, // maxT is from [0,1], if maxT is 0.0f, AABB traversal will be used
+ bool bothTriangleSidesCollide, const RTreeTriangleMesh* mesh, MeshHitCallback<PxRaycastHit>& callback,
+ const PxVec3* inflate = NULL);
+
+ PX_PHYSX_COMMON_API static void collideOBB(
+ const Box& obb, bool bothTriangleSidesCollide, const RTreeTriangleMesh* mesh, MeshHitCallback<PxRaycastHit>& callback,
+ bool checkObbIsAligned = true); // perf hint, pass false if obb is rarely axis aligned
+};
+
+class SimpleRayTriOverlap
+{
+public:
+ PX_FORCE_INLINE SimpleRayTriOverlap(const PxVec3& origin, const PxVec3& dir, bool bothSides, PxReal geomEpsilon)
+ : mOrigin(origin), mDir(dir), mBothSides(bothSides), mGeomEpsilon(geomEpsilon)
+ {
+ }
+
+ PX_FORCE_INLINE Ps::IntBool overlap(const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, PxRaycastHit& hit) const
+ {
+ if(!intersectRayTriangle(mOrigin, mDir, vert0, vert1, vert2, hit.distance, hit.u, hit.v, !mBothSides, mGeomEpsilon))
+ return false;
+
+ if(hit.distance<-mGeomEpsilon) // test if the ray intersection t is really negative
+ return false;
+
+ return true;
+ }
+
+ PxVec3 mOrigin;
+ PxVec3 mDir;
+ bool mBothSides;
+ PxReal mGeomEpsilon;
+};
+
+using Gu::RTree;
+
+// This callback comes from RTree and decodes LeafTriangle indices stored in rtree into actual triangles
+// This callback is needed because RTree doesn't know that it stores triangles since it's a general purpose spatial index
+
+#if PX_VC
+ #pragma warning(push)
+ #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value.
+#endif
+
+template <int tInflate, bool tRayTest>
+struct RayRTreeCallback : RTree::CallbackRaycast, RTree::Callback
+{
+ MeshHitCallback<PxRaycastHit>& outerCallback;
+ PxI32 has16BitIndices;
+ const void* mTris;
+ const PxVec3* mVerts;
+ const PxVec3* mInflate;
+ const SimpleRayTriOverlap rayCollider;
+ PxReal maxT;
+ PxRaycastHit closestHit; // recorded closest hit over the whole traversal (only for callback mode eCLOSEST)
+ PxVec3 cv0, cv1, cv2; // PT: make sure these aren't last in the class, to safely V4Load them
+ PxU32 cis[3];
+ bool hadClosestHit;
+ const bool closestMode;
+ Vec3V inflateV, rayOriginV, rayDirV;
+
+ RayRTreeCallback(
+ PxReal geomEpsilon, MeshHitCallback<PxRaycastHit>& callback,
+ PxI32 has16BitIndices_, const void* tris, const PxVec3* verts,
+ const PxVec3& origin, const PxVec3& dir, PxReal maxT_, bool bothSides, const PxVec3* inflate)
+ : outerCallback(callback), has16BitIndices(has16BitIndices_),
+ mTris(tris), mVerts(verts), mInflate(inflate), rayCollider(origin, dir, bothSides, geomEpsilon),
+ maxT(maxT_), closestMode(callback.inClosestMode())
+ {
+ PX_ASSERT(closestHit.distance == PX_MAX_REAL);
+ hadClosestHit = false;
+ if (tInflate)
+ inflateV = V3LoadU(*mInflate);
+ rayOriginV = V3LoadU(rayCollider.mOrigin);
+ rayDirV = V3LoadU(rayCollider.mDir);
+ }
+
+ PX_FORCE_INLINE void getVertIndices(PxU32 triIndex, PxU32& i0, PxU32 &i1, PxU32 &i2)
+ {
+ if(has16BitIndices)
+ {
+ const PxU16* p = reinterpret_cast<const PxU16*>(mTris) + triIndex*3;
+ i0 = p[0]; i1 = p[1]; i2 = p[2];
+ }
+ else
+ {
+ const PxU32* p = reinterpret_cast<const PxU32*>(mTris) + triIndex*3;
+ i0 = p[0]; i1 = p[1]; i2 = p[2];
+ }
+ }
+
+ virtual PX_FORCE_INLINE bool processResults(PxU32 NumTouched, PxU32* Touched, PxF32& newMaxT)
+ {
+ PX_ASSERT(NumTouched > 0);
+ // Loop through touched leaves
+ PxRaycastHit tempHit;
+ for(PxU32 leaf = 0; leaf<NumTouched; leaf++)
+ {
+ // Each leaf box has a set of triangles
+ LeafTriangles currentLeaf;
+ currentLeaf.Data = Touched[leaf];
+ PxU32 nbLeafTris = currentLeaf.GetNbTriangles();
+ PxU32 baseLeafTriIndex = currentLeaf.GetTriangleIndex();
+
+ for(PxU32 i = 0; i < nbLeafTris; i++)
+ {
+ PxU32 i0, i1, i2;
+ const PxU32 triangleIndex = baseLeafTriIndex+i;
+ getVertIndices(triangleIndex, i0, i1, i2);
+
+ const PxVec3& v0 = mVerts[i0], &v1 = mVerts[i1], &v2 = mVerts[i2];
+ const PxU32 vinds[3] = { i0, i1, i2 };
+
+ if (tRayTest)
+ {
+ Ps::IntBool overlap;
+ if (tInflate)
+ {
+ // AP: mesh skew is already included here (ray is pre-transformed)
+ Vec3V v0v = V3LoadU(v0), v1v = V3LoadU(v1), v2v = V3LoadU(v2);
+ Vec3V minB = V3Min(V3Min(v0v, v1v), v2v), maxB = V3Max(V3Max(v0v, v1v), v2v);
+
+ // PT: we add an epsilon to max distance, to make sure we don't reject triangles that are just at the same
+ // distance as best triangle so far. We need to keep all of these to make sure we return the one with the
+ // best normal.
+ const float relativeEpsilon = GU_EPSILON_SAME_DISTANCE * PxMax(1.0f, maxT);
+ FloatV tNear, tFar;
+ overlap = intersectRayAABB2(
+ V3Sub(minB, inflateV), V3Add(maxB, inflateV), rayOriginV, rayDirV, FLoad(maxT+relativeEpsilon), tNear, tFar);
+ if (overlap)
+ {
+ // can't clip to tFar here because hitting the AABB doesn't guarantee that we can clip
+ // (since we can still miss the actual tri)
+ tempHit.distance = maxT;
+ tempHit.faceIndex = triangleIndex;
+ tempHit.u = tempHit.v = 0.0f;
+ }
+ } else
+ overlap = rayCollider.overlap(v0, v1, v2, tempHit) && tempHit.distance <= maxT;
+ if(!overlap)
+ continue;
+ }
+ tempHit.faceIndex = triangleIndex;
+ tempHit.flags = PxHitFlag::ePOSITION|PxHitFlag::eDISTANCE;
+ // Intersection point is valid if dist < segment's length
+ // We know dist>0 so we can use integers
+ if (closestMode)
+ {
+ if(tempHit.distance < closestHit.distance)
+ {
+ closestHit = tempHit;
+ newMaxT = PxMin(tempHit.distance, newMaxT);
+ cv0 = v0; cv1 = v1; cv2 = v2;
+ cis[0] = vinds[0]; cis[1] = vinds[1]; cis[2] = vinds[2];
+ hadClosestHit = true;
+ }
+ } else
+ {
+ PxReal shrunkMaxT = newMaxT;
+ PxAgain again = outerCallback.processHit(tempHit, v0, v1, v2, shrunkMaxT, vinds);
+ if (!again)
+ return false;
+ if (shrunkMaxT < newMaxT)
+ {
+ newMaxT = shrunkMaxT;
+ maxT = shrunkMaxT;
+ }
+ }
+
+ if (outerCallback.inAnyMode()) // early out if in ANY mode
+ return false;
+ }
+
+ } // for(PxU32 leaf = 0; leaf<NumTouched; leaf++)
+
+ return true;
+ }
+
+ virtual bool processResults(PxU32 numTouched, PxU32* touched)
+ {
+ PxF32 dummy;
+ return RayRTreeCallback::processResults(numTouched, touched, dummy);
+ }
+
+
+ virtual ~RayRTreeCallback()
+ {
+ if (hadClosestHit)
+ {
+ PX_ASSERT(outerCallback.inClosestMode());
+ outerCallback.processHit(closestHit, cv0, cv1, cv2, maxT, cis);
+ }
+ }
+
+private:
+ RayRTreeCallback& operator=(const RayRTreeCallback&);
+};
+
+#if PX_VC
+ #pragma warning(pop)
+#endif
+
+void MeshRayCollider::collideOBB(
+ const Box& obb, bool bothTriangleSidesCollide, const RTreeTriangleMesh* mi, MeshHitCallback<PxRaycastHit>& callback,
+ bool checkObbIsAligned)
+{
+ const PxU32 maxResults = RTREE_N; // maxResults=rtree page size for more efficient early out
+ PxU32 buf[maxResults];
+ RayRTreeCallback<false, false> rTreeCallback(
+ mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(),
+ PxVec3(0), PxVec3(0), 0.0f, bothTriangleSidesCollide, NULL);
+ if (checkObbIsAligned && PxAbs(PxQuat(obb.rot).w) > 0.9999f)
+ {
+ PxVec3 aabbExtents = obb.computeAABBExtent();
+ mi->getRTree().traverseAABB(obb.center - aabbExtents, obb.center + aabbExtents, maxResults, buf, &rTreeCallback);
+ } else
+ mi->getRTree().traverseOBB(obb, maxResults, buf, &rTreeCallback);
+}
+
+template <int tInflate, int tRayTest>
+void MeshRayCollider::collide(
+ const PxVec3& orig, const PxVec3& dir, PxReal maxT, bool bothSides,
+ const RTreeTriangleMesh* mi, MeshHitCallback<PxRaycastHit>& callback,
+ const PxVec3* inflate)
+{
+ const PxU32 maxResults = RTREE_N; // maxResults=rtree page size for more efficient early out
+ PxU32 buf[maxResults];
+ if (maxT == 0.0f) // AABB traversal path
+ {
+ RayRTreeCallback<tInflate, false> rTreeCallback(
+ mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(),
+ orig, dir, maxT, bothSides, inflate);
+ PxVec3 inflate1 = tInflate ? *inflate : PxVec3(0); // both maxT and inflate can be zero, so need to check tInflate
+ mi->getRTree().traverseAABB(orig-inflate1, orig+inflate1, maxResults, buf, &rTreeCallback);
+ }
+ else // ray traversal path
+ {
+ RayRTreeCallback<tInflate, tRayTest> rTreeCallback(
+ mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(),
+ orig, dir, maxT, bothSides, inflate);
+ mi->getRTree().traverseRay<tInflate>(orig, dir, maxResults, buf, &rTreeCallback, inflate, maxT);
+ }
+}
+
+
+#define TINST(a,b) \
+template void MeshRayCollider::collide<a,b>( \
+ const PxVec3& orig, const PxVec3& dir, PxReal maxT, bool bothSides, const RTreeTriangleMesh* mesh, \
+ MeshHitCallback<PxRaycastHit>& callback, const PxVec3* inflate);
+
+TINST(0,0)
+TINST(1,0)
+TINST(0,1)
+TINST(1,1)
+
+#undef TINST
+
+#include "GuRaycastTests.h"
+#include "PxTriangleMeshGeometry.h"
+#include "GuTriangleMesh.h"
+#include "CmScaling.h"
+
+struct RayMeshColliderCallback : public MeshHitCallback<PxRaycastHit>
+{
+ PxRaycastHit* mDstBase;
+ PxU32 mHitNum;
+ PxU32 mMaxHits;
+ const PxMeshScale* mScale;
+ const PxTransform* mPose;
+ const Matrix34* mWorld2vertexSkew;
+ PxU32 mHitFlags;
+ const PxVec3& mRayDir;
+ bool mIsDoubleSided;
+ float mDistCoeff;
+
+ RayMeshColliderCallback(
+ CallbackMode::Enum mode_, PxRaycastHit* hits, PxU32 maxHits, const PxMeshScale* scale, const PxTransform* pose,
+ const Matrix34* world2vertexSkew, PxU32 hitFlags, const PxVec3& rayDir, bool isDoubleSided, float distCoeff) :
+ MeshHitCallback<PxRaycastHit> (mode_),
+ mDstBase (hits),
+ mHitNum (0),
+ mMaxHits (maxHits),
+ mScale (scale),
+ mPose (pose),
+ mWorld2vertexSkew (world2vertexSkew),
+ mHitFlags (hitFlags),
+ mRayDir (rayDir),
+ mIsDoubleSided (isDoubleSided),
+ mDistCoeff (distCoeff)
+ {
+ }
+
+ // return false for early out
+ virtual bool processHit(
+ const PxRaycastHit& lHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal&, const PxU32*)
+ {
+ const PxReal u = lHit.u, v = lHit.v;
+ const PxVec3 localImpact = (1.0f - u - v)*lp0 + u*lp1 + v*lp2;
+
+ //not worth concatenating to do 1 transform: PxMat34Legacy vertex2worldSkew = scaling.getVertex2WorldSkew(absPose);
+ // PT: TODO: revisit this for N hits
+ PxRaycastHit hit = lHit;
+ hit.position = mPose->transform(mScale->transform(localImpact));
+ hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eDISTANCE|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX;
+ hit.normal = PxVec3(0.0f);
+ hit.distance *= mDistCoeff;
+
+ // Compute additional information if needed
+ if(mHitFlags & PxHitFlag::eNORMAL)
+ {
+ // User requested impact normal
+ const PxVec3 localNormal = (lp1 - lp0).cross(lp2 - lp0);
+
+ if(mWorld2vertexSkew)
+ {
+ hit.normal = mWorld2vertexSkew->rotateTranspose(localNormal);
+ if (mScale->hasNegativeDeterminant())
+ Ps::swap<PxReal>(hit.u, hit.v); // have to swap the UVs though since they were computed in mesh local space
+ }
+ else
+ hit.normal = hit.normal = mPose->rotate(localNormal);
+ hit.normal.normalize();
+
+ // PT: figure out correct normal orientation (DE7458)
+ // - if the mesh is single-sided the normal should be the regular triangle normal N, regardless of eMESH_BOTH_SIDES.
+ // - if the mesh is double-sided the correct normal can be either N or -N. We take the one opposed to ray direction.
+ if(mIsDoubleSided && hit.normal.dot(mRayDir) > 0.0f)
+ hit.normal = -hit.normal;
+
+ hit.flags |= PxHitFlag::eNORMAL;
+ }
+
+ // PT: no callback => store results in provided buffer
+ if(mHitNum == mMaxHits)
+ return false;
+
+ mDstBase[mHitNum++] = hit;
+ return true;
+ }
+
+private:
+ RayMeshColliderCallback& operator=(const RayMeshColliderCallback&);
+};
+
+PxU32 physx::Gu::raycast_triangleMesh_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose,
+ const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist,
+ PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits)
+{
+ PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33);
+
+ const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh);
+
+ //scaling: transform the ray to vertex space
+
+ PxVec3 orig, dir;
+ Matrix34 world2vertexSkew;
+ Matrix34* world2vertexSkewP = NULL;
+ PxReal distCoeff = 1.0f;
+ if(meshGeom.scale.isIdentity())
+ {
+ orig = pose.transformInv(rayOrigin);
+ dir = pose.rotateInv(rayDir);
+ }
+ else
+ {
+ world2vertexSkew = meshGeom.scale.getInverse() * pose.getInverse();
+ world2vertexSkewP = &world2vertexSkew;
+ orig = world2vertexSkew.transform(rayOrigin);
+ dir = world2vertexSkew.rotate(rayDir);
+ {
+ distCoeff = dir.normalize();
+ maxDist *= distCoeff;
+ maxDist += 1e-3f;
+ distCoeff = 1.0f / distCoeff;
+ }
+ }
+
+ const bool isDoubleSided = meshGeom.meshFlags.isSet(PxMeshGeometryFlag::eDOUBLE_SIDED);
+ const bool bothSides = isDoubleSided || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES);
+
+ RayMeshColliderCallback callback(
+ (maxHits > 1) ? CallbackMode::eMULTIPLE : (hitFlags & PxHitFlag::eMESH_ANY ? CallbackMode::eANY : CallbackMode::eCLOSEST),
+ hits, maxHits, &meshGeom.scale, &pose, world2vertexSkewP, hitFlags, rayDir, isDoubleSided, distCoeff);
+
+ MeshRayCollider::collide<0, 1>(orig, dir, maxDist, bothSides, static_cast<const RTreeTriangleMesh*>(meshData), callback, NULL);
+ return callback.mHitNum;
+}
+
+
+static PX_INLINE void computeSweptAABBAroundOBB(
+ const Box& obb, PxVec3& sweepOrigin, PxVec3& sweepExtents, PxVec3& sweepDir, PxReal& sweepLen)
+{
+ PxU32 other1, other2;
+ // largest axis of the OBB is the sweep direction, sum of abs of two other is the swept AABB extents
+ PxU32 lai = Ps::largestAxis(obb.extents, other1, other2);
+ PxVec3 longestAxis = obb.rot[lai]*obb.extents[lai];
+ PxVec3 absOther1 = obb.rot[other1].abs()*obb.extents[other1];
+ PxVec3 absOther2 = obb.rot[other2].abs()*obb.extents[other2];
+ sweepOrigin = obb.center - longestAxis;
+ sweepExtents = absOther1 + absOther2 + PxVec3(GU_MIN_AABB_EXTENT); // see comments for GU_MIN_AABB_EXTENT
+ sweepLen = 2.0f; // length is already included in longestAxis
+ sweepDir = longestAxis;
+}
+
+enum { eSPHERE, eCAPSULE, eBOX }; // values for tSCB
+
+#if PX_VC
+ #pragma warning(push)
+ #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value.
+ #pragma warning( disable : 4512 ) // assignment operator could not be generated
+#endif
+
+namespace
+{
+struct IntersectShapeVsMeshCallback : MeshHitCallback<PxRaycastHit>
+{
+ PX_NOCOPY(IntersectShapeVsMeshCallback)
+public:
+ IntersectShapeVsMeshCallback(const PxMat33& vertexToShapeSkew, LimitedResults* results, bool flipNormal)
+ : MeshHitCallback<PxRaycastHit>(CallbackMode::eMULTIPLE),
+ mVertexToShapeSkew (vertexToShapeSkew),
+ mResults (results),
+ mAnyHits (false),
+ mFlipNormal (flipNormal)
+ {
+ }
+ virtual ~IntersectShapeVsMeshCallback(){}
+
+ const PxMat33& mVertexToShapeSkew; // vertex to box without translation for boxes
+ LimitedResults* mResults;
+ bool mAnyHits;
+ bool mFlipNormal;
+
+ PX_FORCE_INLINE bool recordHit(const PxRaycastHit& aHit, Ps::IntBool hit)
+ {
+ if(hit)
+ {
+ mAnyHits = true;
+ if(mResults)
+ mResults->add(aHit.faceIndex);
+ else
+ return false; // abort traversal if we are only interested in firstContact (mResults is NULL)
+ }
+ return true; // if we are here, either no triangles were hit or multiple results are expected => continue traversal
+ }
+};
+
+template<bool tScaleIsIdentity>
+struct IntersectSphereVsMeshCallback : IntersectShapeVsMeshCallback
+{
+ IntersectSphereVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {}
+ virtual ~IntersectSphereVsMeshCallback(){}
+ PxF32 mMinDist2;
+ PxVec3 mLocalCenter; // PT: sphere center in local/mesh space
+
+ virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position
+ const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*)
+ {
+ const Vec3V v0 = V3LoadU(tScaleIsIdentity ? av0 : mVertexToShapeSkew * av0);
+ const Vec3V v1 = V3LoadU(tScaleIsIdentity ? av1 : mVertexToShapeSkew * (mFlipNormal ? av2 : av1));
+ const Vec3V v2 = V3LoadU(tScaleIsIdentity ? av2 : mVertexToShapeSkew * (mFlipNormal ? av1 : av2));
+
+ FloatV dummy1, dummy2;
+ Vec3V closestP;
+ PxReal dist2;
+ FStore(distancePointTriangleSquared(V3LoadU(mLocalCenter), v0, v1, v2, dummy1, dummy2, closestP), &dist2);
+ return recordHit(aHit, dist2 <= mMinDist2);
+ }
+};
+
+template<bool tScaleIsIdentity>
+struct IntersectCapsuleVsMeshCallback : IntersectShapeVsMeshCallback
+{
+ IntersectCapsuleVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {}
+ virtual ~IntersectCapsuleVsMeshCallback(){}
+
+ Capsule mLocalCapsule; // PT: capsule in mesh/local space
+ CapsuleTriangleOverlapData mParams;
+
+ virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position
+ const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*)
+ {
+ bool hit;
+ if(tScaleIsIdentity)
+ {
+ const PxVec3 normal = (av0 - av1).cross(av0 - av2);
+ hit = intersectCapsuleTriangle(normal, av0, av1, av2, mLocalCapsule, mParams);
+ }
+ else
+ {
+ const PxVec3 v0 = mVertexToShapeSkew * av0;
+ const PxVec3 v1 = mVertexToShapeSkew * (mFlipNormal ? av2 : av1);
+ const PxVec3 v2 = mVertexToShapeSkew * (mFlipNormal ? av1 : av2);
+ const PxVec3 normal = (v0 - v1).cross(v0 - v2);
+ hit = intersectCapsuleTriangle(normal, v0, v1, v2, mLocalCapsule, mParams);
+ }
+ return recordHit(aHit, hit);
+ }
+};
+
+template<bool tScaleIsIdentity>
+struct IntersectBoxVsMeshCallback : IntersectShapeVsMeshCallback
+{
+ IntersectBoxVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {}
+ virtual ~IntersectBoxVsMeshCallback(){}
+
+ Matrix34 mVertexToBox;
+ Vec3p mBoxExtents, mBoxCenter;
+
+ virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position
+ const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*)
+ {
+ Vec3p v0, v1, v2;
+ if(tScaleIsIdentity)
+ {
+ v0 = mVertexToShapeSkew * av0; // transform from skewed mesh vertex to box space,
+ v1 = mVertexToShapeSkew * av1; // this includes inverse skew, inverse mesh shape transform and inverse box basis
+ v2 = mVertexToShapeSkew * av2;
+ }
+ else
+ {
+ v0 = mVertexToBox.transform(av0);
+ v1 = mVertexToBox.transform(mFlipNormal ? av2 : av1);
+ v2 = mVertexToBox.transform(mFlipNormal ? av1 : av2);
+ }
+
+ // PT: this one is safe because we're using Vec3p for all parameters
+ const Ps::IntBool hit = intersectTriangleBox_Unsafe(mBoxCenter, mBoxExtents, v0, v1, v2);
+ return recordHit(aHit, hit);
+ }
+};
+}
+
+#if PX_VC
+ #pragma warning(pop)
+#endif
+
+template<int tSCB, bool idtMeshScale>
+static bool intersectAnyVsMeshT(
+ const Sphere* worldSphere, const Capsule* worldCapsule, const Box* worldOBB,
+ const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale,
+ LimitedResults* results)
+{
+ const bool flipNormal = meshScale.hasNegativeDeterminant();
+ PxMat33 shapeToVertexSkew, vertexToShapeSkew;
+ if (!idtMeshScale && tSCB != eBOX)
+ {
+ vertexToShapeSkew = meshScale.toMat33();
+ shapeToVertexSkew = vertexToShapeSkew.getInverse();
+ }
+
+ if (tSCB == eSPHERE)
+ {
+ IntersectSphereVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal);
+ // transform sphere center from world to mesh shape space
+ const PxVec3 center = meshTransform.transformInv(worldSphere->center);
+
+ // callback will transform verts
+ callback.mLocalCenter = center;
+ callback.mMinDist2 = worldSphere->radius*worldSphere->radius;
+
+ PxVec3 sweepOrigin, sweepDir, sweepExtents;
+ PxReal sweepLen;
+ if (!idtMeshScale)
+ {
+ // AP: compute a swept AABB around an OBB around a skewed sphere
+ // TODO: we could do better than an AABB around OBB actually because we can slice off the corners..
+ const Box worldOBB_(worldSphere->center, PxVec3(worldSphere->radius), PxMat33(PxIdentity));
+ Box vertexOBB;
+ computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale);
+ computeSweptAABBAroundOBB(vertexOBB, sweepOrigin, sweepExtents, sweepDir, sweepLen);
+ } else
+ {
+ sweepOrigin = center;
+ sweepDir = PxVec3(1.0f,0,0);
+ sweepLen = 0.0f;
+ sweepExtents = PxVec3(PxMax(worldSphere->radius, GU_MIN_AABB_EXTENT));
+ }
+
+ MeshRayCollider::collide<1, 1>(sweepOrigin, sweepDir, sweepLen, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback, &sweepExtents);
+
+ return callback.mAnyHits;
+ }
+ else if (tSCB == eCAPSULE)
+ {
+ IntersectCapsuleVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal);
+ const PxF32 radius = worldCapsule->radius;
+
+ // transform world capsule to mesh shape space
+ callback.mLocalCapsule.p0 = meshTransform.transformInv(worldCapsule->p0);
+ callback.mLocalCapsule.p1 = meshTransform.transformInv(worldCapsule->p1);
+ callback.mLocalCapsule.radius = radius;
+ callback.mParams.init(callback.mLocalCapsule);
+
+ if (idtMeshScale)
+ {
+ // traverse a sweptAABB around the capsule
+ const PxVec3 radius3(radius);
+ MeshRayCollider::collide<1, 0>(callback.mLocalCapsule.p0, callback.mLocalCapsule.p1-callback.mLocalCapsule.p0, 1.0f, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback, &radius3);
+ }
+ else
+ {
+ // make vertex space OBB
+ Box vertexOBB;
+ Box worldOBB_;
+ worldOBB_.create(*worldCapsule); // AP: potential optimization (meshTransform.inverse is already in callback.mCapsule)
+ computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale);
+
+ MeshRayCollider::collideOBB(vertexOBB, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback);
+ }
+ return callback.mAnyHits;
+ }
+ else if (tSCB == eBOX)
+ {
+ Box vertexOBB; // query box in vertex space
+ if (idtMeshScale)
+ {
+ // mesh scale is identity - just inverse transform the box without optimization
+ vertexOBB = transformBoxOrthonormal(*worldOBB, meshTransform.getInverse());
+ // mesh vertices will be transformed from skewed vertex space directly to box AABB space
+ // box inverse rotation is baked into the vertexToShapeSkew transform
+ // if meshScale is not identity, vertexOBB already effectively includes meshScale transform
+ PxVec3 boxCenter;
+ getInverse(vertexToShapeSkew, boxCenter, vertexOBB.rot, vertexOBB.center);
+ IntersectBoxVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal);
+
+ callback.mBoxCenter = -boxCenter;
+ callback.mBoxExtents = worldOBB->extents; // extents do not change
+
+ MeshRayCollider::collideOBB(vertexOBB, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback);
+
+ return callback.mAnyHits;
+ } else
+ {
+ computeVertexSpaceOBB(vertexOBB, *worldOBB, meshTransform, meshScale);
+
+ // mesh scale needs to be included - inverse transform and optimize the box
+ const PxMat33 vertexToWorldSkew_Rot = PxMat33Padded(meshTransform.q) * meshScale.toMat33();
+ const PxVec3& vertexToWorldSkew_Trans = meshTransform.p;
+
+ Matrix34 tmp;
+ buildMatrixFromBox(tmp, *worldOBB);
+ const Matrix34 inv = tmp.getInverseRT();
+ const Matrix34 _vertexToWorldSkew(vertexToWorldSkew_Rot, vertexToWorldSkew_Trans);
+
+ IntersectBoxVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal);
+ callback.mVertexToBox = inv * _vertexToWorldSkew;
+ callback.mBoxCenter = PxVec3(0.0f);
+ callback.mBoxExtents = worldOBB->extents; // extents do not change
+
+ MeshRayCollider::collideOBB(vertexOBB, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback);
+
+ return callback.mAnyHits;
+ }
+ }
+ else
+ {
+ PX_ASSERT(0);
+ return false;
+ }
+}
+
+template<int tSCB>
+static bool intersectAnyVsMesh(
+ const Sphere* worldSphere, const Capsule* worldCapsule, const Box* worldOBB,
+ const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale,
+ LimitedResults* results)
+{
+ PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33);
+ if (meshScale.isIdentity())
+ return intersectAnyVsMeshT<tSCB, true>(worldSphere, worldCapsule, worldOBB, triMesh, meshTransform, meshScale, results);
+ else
+ return intersectAnyVsMeshT<tSCB, false>(worldSphere, worldCapsule, worldOBB, triMesh, meshTransform, meshScale, results);
+}
+
+bool physx::Gu::intersectSphereVsMesh_RTREE(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results)
+{
+ return intersectAnyVsMesh<eSPHERE>(&sphere, NULL, NULL, triMesh, meshTransform, meshScale, results);
+}
+
+bool physx::Gu::intersectBoxVsMesh_RTREE(const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results)
+{
+ return intersectAnyVsMesh<eBOX>(NULL, NULL, &box, triMesh, meshTransform, meshScale, results);
+}
+
+bool physx::Gu::intersectCapsuleVsMesh_RTREE(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results)
+{
+ return intersectAnyVsMesh<eCAPSULE>(NULL, &capsule, NULL, triMesh, meshTransform, meshScale, results);
+}
+
+void physx::Gu::intersectOBB_RTREE(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned)
+{
+ MeshRayCollider::collideOBB(obb, bothTriangleSidesCollide, static_cast<const RTreeTriangleMesh*>(mesh), callback, checkObbIsAligned);
+}
+
+// PT: TODO: refactor/share bits of this
+bool physx::Gu::sweepCapsule_MeshGeom_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
+ const Capsule& lss, const PxVec3& unitDir, const PxReal distance,
+ PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation)
+{
+ PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33);
+ const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh);
+
+ const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation);
+
+ const bool isIdentity = triMeshGeom.scale.isIdentity();
+ bool isDoubleSided = (triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED);
+ const PxU32 meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES;
+
+ // compute sweptAABB
+ const PxVec3 localP0 = pose.transformInv(inflatedCapsule.p0);
+ const PxVec3 localP1 = pose.transformInv(inflatedCapsule.p1);
+ PxVec3 sweepOrigin = (localP0+localP1)*0.5f;
+ PxVec3 sweepDir = pose.rotateInv(unitDir);
+ PxVec3 sweepExtents = PxVec3(inflatedCapsule.radius) + (localP0-localP1).abs()*0.5f;
+ PxReal distance1 = distance;
+ PxReal distCoeff = 1.0f;
+ Matrix34 poseWithScale;
+ if(!isIdentity)
+ {
+ poseWithScale = pose * triMeshGeom.scale;
+ distance1 = computeSweepData(triMeshGeom, sweepOrigin, sweepExtents, sweepDir, distance);
+ distCoeff = distance1 / distance;
+ } else
+ poseWithScale = Matrix34(pose);
+
+ SweepCapsuleMeshHitCallback callback(sweepHit, poseWithScale, distance, isDoubleSided, inflatedCapsule, unitDir, hitFlags, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff);
+
+ MeshRayCollider::collide<1, 1>(sweepOrigin, sweepDir, distance1, true, meshData, callback, &sweepExtents);
+
+ if(meshBothSides)
+ isDoubleSided = true;
+
+ return callback.finalizeHit(sweepHit, inflatedCapsule, triMeshGeom, pose, isDoubleSided);
+}
+
+#include "GuSweepSharedTests.h"
+
+// PT: TODO: refactor/share bits of this
+bool physx::Gu::sweepBox_MeshGeom_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
+ const Box& box, const PxVec3& unitDir, const PxReal distance,
+ PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation)
+{
+ PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33);
+ const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh);
+
+ const bool isIdentity = triMeshGeom.scale.isIdentity();
+
+ const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES;
+ const bool isDoubleSided = triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED;
+
+ Matrix34 meshToWorldSkew;
+ PxVec3 sweptAABBMeshSpaceExtents, meshSpaceOrigin, meshSpaceDir;
+
+ // Input sweep params: geom, pose, box, unitDir, distance
+ // We convert the origin from world space to mesh local space
+ // and convert the box+pose to mesh space AABB
+ if(isIdentity)
+ {
+ meshToWorldSkew = Matrix34(pose);
+ PxMat33 worldToMeshRot(pose.q.getConjugate()); // extract rotation matrix from pose.q
+ meshSpaceOrigin = worldToMeshRot.transform(box.center - pose.p);
+ meshSpaceDir = worldToMeshRot.transform(unitDir) * distance;
+ PxMat33 boxToMeshRot = worldToMeshRot * box.rot;
+ sweptAABBMeshSpaceExtents = boxToMeshRot.column0.abs() * box.extents.x +
+ boxToMeshRot.column1.abs() * box.extents.y +
+ boxToMeshRot.column2.abs() * box.extents.z;
+ }
+ else
+ {
+ meshToWorldSkew = pose * triMeshGeom.scale;
+ const PxMat33 meshToWorldSkew_Rot = PxMat33Padded(pose.q) * triMeshGeom.scale.toMat33();
+ const PxVec3& meshToWorldSkew_Trans = pose.p;
+
+ PxMat33 worldToVertexSkew_Rot;
+ PxVec3 worldToVertexSkew_Trans;
+ getInverse(worldToVertexSkew_Rot, worldToVertexSkew_Trans, meshToWorldSkew_Rot, meshToWorldSkew_Trans);
+
+ //make vertex space OBB
+ Box vertexSpaceBox1;
+ const Matrix34 worldToVertexSkew(worldToVertexSkew_Rot, worldToVertexSkew_Trans);
+ vertexSpaceBox1 = transform(worldToVertexSkew, box);
+ // compute swept aabb
+ sweptAABBMeshSpaceExtents = vertexSpaceBox1.computeAABBExtent();
+
+ meshSpaceOrigin = worldToVertexSkew.transform(box.center);
+ meshSpaceDir = worldToVertexSkew.rotate(unitDir*distance); // also applies scale to direction/length
+ }
+
+ sweptAABBMeshSpaceExtents += PxVec3(inflation); // inflate the bounds with additive inflation
+ sweptAABBMeshSpaceExtents *= 1.01f; // fatten the bounds to account for numerical discrepancies
+
+ PxReal dirLen = PxMax(meshSpaceDir.magnitude(), 1e-5f);
+ PxReal distCoeff = 1.0f;
+ if (!isIdentity)
+ distCoeff = dirLen / distance;
+
+ // Move to AABB space
+ Matrix34 worldToBox;
+ computeWorldToBoxMatrix(worldToBox, box);
+
+ const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides;
+
+ const Matrix34Padded meshToBox = worldToBox*meshToWorldSkew;
+ const PxTransform boxTransform = box.getTransform();
+
+ const PxVec3 localDir = worldToBox.rotate(unitDir);
+ const PxVec3 localDirDist = localDir*distance;
+ SweepBoxMeshHitCallback callback( // using eMULTIPLE with shrinkMaxT
+ CallbackMode::eMULTIPLE, meshToBox, distance, bothTriangleSidesCollide, box, localDirDist, localDir, unitDir, hitFlags, inflation, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff);
+
+ MeshRayCollider::collide<1, 1>(meshSpaceOrigin, meshSpaceDir/dirLen, dirLen, bothTriangleSidesCollide, meshData, callback, &sweptAABBMeshSpaceExtents);
+
+ return callback.finalizeHit(sweepHit, triMeshGeom, pose, boxTransform, localDir, meshBothSides, isDoubleSided);
+}
+
+#include "GuInternal.h"
+void physx::Gu::sweepConvex_MeshGeom_RTREE(const TriangleMesh* mesh, const Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool)
+{
+ PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33);
+ const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh);
+
+ // create temporal bounds
+ Box querySweptBox;
+ computeSweptBox(querySweptBox, hullBox.extents, hullBox.center, hullBox.rot, localDir, distance);
+
+ MeshRayCollider::collideOBB(querySweptBox, true, meshData, callback);
+}