// // 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-2021 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 "geometry/PxMeshScale.h" #include "geometry/PxConvexMeshGeometry.h" #include "geomutils/GuContactBuffer.h" #include "foundation/PxMathUtils.h" #include "GuMTD.h" #include "GuSphere.h" #include "GuCapsule.h" #include "GuDistancePointSegment.h" #include "GuDistanceSegmentSegment.h" #include "GuDistanceSegmentBox.h" #include "GuVecBox.h" #include "GuVecCapsule.h" #include "GuVecConvexHull.h" #include "GuVecConvexHullNoScale.h" #include "GuInternal.h" #include "GuContactMethodImpl.h" #include "GuBoxConversion.h" #include "GuGeometryUnion.h" #include "GuShapeConvex.h" #include "GuPCMShapeConvex.h" #include "GuPCMContactGen.h" #include "GuConvexMesh.h" #include "GuGJK.h" #include "PsUtilities.h" #include "PsVecTransform.h" #include "PsMathUtils.h" using namespace physx; using namespace Gu; static PX_FORCE_INLINE PxF32 manualNormalize(PxVec3& mtd, const PxVec3& normal, PxReal lenSq) { const PxF32 len = PxSqrt(lenSq); // We do a *manual* normalization to check for singularity condition if(lenSq < 1e-6f) mtd = PxVec3(1.0f, 0.0f, 0.0f); // PT: zero normal => pick up random one else mtd = normal * 1.0f / len; return len; } static PX_FORCE_INLINE float validateDepth(float depth) { // PT: penetration depth must always be positive or null, but FPU accuracy being what it is, we sometimes // end up with very small, epsilon-sized negative depths. We clamp those to zero, since they don't indicate // real bugs in the MTD functions. However anything larger than epsilon is wrong, and caught with an assert. const float epsilon = 1.e-3f; //ML: because we are shrunking the shape in this moment, so the depth might be larger than eps, this condition is no longer valid //PX_ASSERT(depth>=-epsilon); PX_UNUSED(epsilon); return PxMax(depth, 0.0f); } /////////////////////////////////////////////////////////////////////////////// // PT: the function names should follow the order in which the PxGeometryTypes are listed, // i.e. computeMTD_Type0Type1 with Type0<=Type1. This is to guarantee that the proper results // (following the desired convention) are returned from the PxGeometryQuery-level call. /////////////////////////////////////////////////////////////////////////////// static bool computeMTD_SphereSphere(PxVec3& mtd, PxF32& depth, const Sphere& sphere0, const Sphere& sphere1) { const PxVec3 delta = sphere0.center - sphere1.center; const PxReal d2 = delta.magnitudeSquared(); const PxReal radiusSum = sphere0.radius + sphere1.radius; if(d2 > radiusSum*radiusSum) return false; const PxF32 d = manualNormalize(mtd, delta, d2); depth = validateDepth(radiusSum - d); return true; } /////////////////////////////////////////////////////////////////////////////// static bool computeMTD_SphereCapsule(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const Capsule& capsule) { const PxReal radiusSum = sphere.radius + capsule.radius; PxReal u; const PxReal d2 = distancePointSegmentSquared(capsule, sphere.center, &u); if(d2 > radiusSum*radiusSum) return false; const PxVec3 normal = sphere.center - capsule.getPointAt(u); const PxReal lenSq = normal.magnitudeSquared(); const PxF32 d = manualNormalize(mtd, normal, lenSq); depth = validateDepth(radiusSum - d); return true; } /////////////////////////////////////////////////////////////////////////////// //This version is ported 1:1 from novodex static PX_FORCE_INLINE bool ContactSphereBox(const PxVec3& sphereOrigin, PxReal sphereRadius, const PxVec3& boxExtents, // const PxcCachedTransforms& boxCacheTransform, const PxTransform& boxTransform, PxVec3& point, PxVec3& normal, PxReal& separation, PxReal contactDistance) { //returns true on contact const PxVec3 delta = sphereOrigin - boxTransform.p; // s1.center - s2.center; PxVec3 dRot = boxTransform.rotateInv(delta); //transform delta into OBB body coords. //check if delta is outside ABB - and clip the vector to the ABB. bool outside = false; if (dRot.x < -boxExtents.x) { outside = true; dRot.x = -boxExtents.x; } else if (dRot.x > boxExtents.x) { outside = true; dRot.x = boxExtents.x; } if (dRot.y < -boxExtents.y) { outside = true; dRot.y = -boxExtents.y; } else if (dRot.y > boxExtents.y) { outside = true; dRot.y = boxExtents.y; } if (dRot.z < -boxExtents.z) { outside = true; dRot.z =-boxExtents.z; } else if (dRot.z > boxExtents.z) { outside = true; dRot.z = boxExtents.z; } if (outside) //if clipping was done, sphere center is outside of box. { point = boxTransform.rotate(dRot); //get clipped delta back in world coords. normal = delta - point; //what we clipped away. const PxReal lenSquared = normal.magnitudeSquared(); const PxReal inflatedDist = sphereRadius + contactDistance; if (lenSquared > inflatedDist * inflatedDist) return false; //disjoint //normalize to make it into the normal: separation = PxRecipSqrt(lenSquared); normal *= separation; separation *= lenSquared; //any plane that touches the sphere is tangential, so a vector from contact point to sphere center defines normal. //we could also use point here, which has same direction. //this is either a faceFace or a vertexFace contact depending on whether the box's face or vertex collides, but we did not distinguish. //We'll just use vertex face for now, this info isn't really being used anyway. //contact point is point on surface of cube closest to sphere center. point += boxTransform.p; separation -= sphereRadius; return true; } else { //center is in box, we definitely have a contact. PxVec3 locNorm; //local coords contact normal PxVec3 absdRot; absdRot = PxVec3(PxAbs(dRot.x), PxAbs(dRot.y), PxAbs(dRot.z)); PxVec3 distToSurface = boxExtents - absdRot; //dist from embedded center to box surface along 3 dimensions. //find smallest element of distToSurface if (distToSurface.y < distToSurface.x) { if (distToSurface.y < distToSurface.z) { //y locNorm = PxVec3(0.0f, dRot.y > 0.0f ? 1.0f : -1.0f, 0.0f); separation = -distToSurface.y; } else { //z locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); separation = -distToSurface.z; } } else { if (distToSurface.x < distToSurface.z) { //x locNorm = PxVec3(dRot.x > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f); separation = -distToSurface.x; } else { //z locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); separation = -distToSurface.z; } } //separation so far is just the embedding of the center point; we still have to push out all of the radius. point = sphereOrigin; normal = boxTransform.rotate(locNorm); separation -= sphereRadius; return true; } } static bool computeMTD_SphereBox(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const Box& box) { PxVec3 point; if(!ContactSphereBox( sphere.center, sphere.radius, box.extents, PxTransform(box.center, PxQuat(box.rot)), point, mtd, depth, 0.0f)) return false; depth = validateDepth(-depth); return true; } /////////////////////////////////////////////////////////////////////////////// static bool computeMTD_CapsuleCapsule(PxVec3& mtd, PxF32& depth, const Capsule& capsule0, const Capsule& capsule1) { PxReal s,t; const PxReal d2 = distanceSegmentSegmentSquared(capsule0, capsule1, &s, &t); const PxReal radiusSum = capsule0.radius + capsule1.radius; if(d2 > radiusSum*radiusSum) return false; const PxVec3 normal = capsule0.getPointAt(s) - capsule1.getPointAt(t); const PxReal lenSq = normal.magnitudeSquared(); const PxF32 d = manualNormalize(mtd, normal, lenSq); depth = validateDepth(radiusSum - d); return true; } /////////////////////////////////////////////////////////////////////////////// static PX_FORCE_INLINE void reorderMTD(PxVec3& mtd, const PxVec3& center0, const PxVec3& center1) { const PxVec3 witness = center0 - center1; if(mtd.dot(witness) < 0.0f) mtd = -mtd; } static PX_FORCE_INLINE void projectBox(PxReal& min, PxReal& max, const PxVec3& axis, const Box& box) { const PxReal boxCen = box.center.dot(axis); const PxReal boxExt = PxAbs(box.rot.column0.dot(axis)) * box.extents.x + PxAbs(box.rot.column1.dot(axis)) * box.extents.y + PxAbs(box.rot.column2.dot(axis)) * box.extents.z; min = boxCen - boxExt; max = boxCen + boxExt; } static bool PxcTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, const Box& box, PxReal& depth) { // Project capsule PxReal min0 = segment.p0.dot(axis); PxReal max0 = segment.p1.dot(axis); if(min0>max0) Ps::swap(min0, max0); min0 -= radius; max0 += radius; // Project box PxReal Min1, Max1; projectBox(Min1, Max1, axis, box); // Test projections if(max0=0.0f); const PxReal d1 = Max1 - min0; PX_ASSERT(d1>=0.0f); depth = physx::intrinsics::selectMin(d0, d1); return true; } static bool PxcCapsuleOBBOverlap3(const Segment& segment, PxReal radius, const Box& box, PxReal* t=NULL, PxVec3* pp=NULL) { PxVec3 Sep(0.0f); PxReal PenDepth = PX_MAX_REAL; // Test normals for(PxU32 i=0;i<3;i++) { PxReal d; if(!PxcTestAxis(box.rot[i], segment, radius, box, d)) return false; if(d capsule.radius*capsule.radius) return false; if(d2 != 0.0f) { // PT: the capsule segment doesn't intersect the box => distance-based version const PxVec3 onSegment = capsule.getPointAt(t); onBox = box.center + box.rot.transform(onBox); PxVec3 normal = onSegment - onBox; PxReal normalLen = normal.magnitude(); if(normalLen != 0.0f) { normal *= 1.0f/normalLen; mtd = normal; depth = validateDepth(capsule.radius - PxSqrt(d2)); return true; } } // PT: the capsule segment intersects the box => penetration-based version return PxcCapsuleOBBOverlap3(capsule, capsule.radius, box, &depth, &mtd); } /////////////////////////////////////////////////////////////////////////////// static bool PxcTestAxis(const PxVec3& axis, const Box& box0, const Box& box1, PxReal& depth) { // Project box PxReal min0, max0; projectBox(min0, max0, axis, box0); // Project box PxReal Min1, Max1; projectBox(Min1, Max1, axis, box1); // Test projections if(max0=0.0f); const PxReal d1 = Max1 - min0; PX_ASSERT(d1>=0.0f); depth = physx::intrinsics::selectMin(d0, d1); return true; } static PX_FORCE_INLINE bool testBoxBoxAxis(PxVec3& mtd, PxF32& depth, const PxVec3& axis, const Box& box0, const Box& box1) { PxF32 d; if(!PxcTestAxis(axis, box0, box1, d)) return false; if(dgetHull(); const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); const ConvexHullV convexHull_(hullData, zeroV, vScale, vQuat, meshScale.isIdentity()); const PsMatTransformV aToB(convexPose.transformInv(transform0)); //const CapsuleV capsule(zeroV, zeroV, FZero());//this is a point const CapsuleV capsule_(aToB.p, FZero());//this is a point LocalConvex capsule(capsule_); LocalConvex convexHull(convexHull_); status = gjk, LocalConvex >(capsule, convexHull, aToB.p, FMax(), closA, closB, normalV, dist); } bool intersect = status == GJK_CONTACT; if(intersect) { sqDistance = 0.0f; } else { const FloatV sqDist = FMul(dist, dist); FStore(sqDist, &sqDistance); V3StoreU(normalV, normal_); V3StoreU(closB, closestPoint_); normal_ = convexPose.rotate(normal_); closestPoint_ = convexPose.transform(closestPoint_); } return intersect; } static bool computeMTD_SphereConvex(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) { PxReal d2; const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); PxVec3 dummy; if(!pointConvexDistance(mtd, dummy, d2, sphere.center, convexMesh, convexGeom.scale, convexPose)) { if(d2 > sphere.radius*sphere.radius) return false; depth = validateDepth(sphere.radius - PxSqrt(d2)); mtd = -mtd; return true; } // PT: if we reach this place, the sphere center touched the convex => switch to penetration-based code PxU32 nbPolygons = convexMesh->getNbPolygonsFast(); const HullPolygonData* polygons = convexMesh->getPolygons(); const PxVec3 localSphereCenter = convexPose.transformInv(sphere.center); PxReal dmax = -PX_MAX_F32; while(nbPolygons--) { const HullPolygonData& polygon = *polygons++; const PxF32 d = polygon.mPlane.distance(localSphereCenter); if(d>dmax) { dmax = d; mtd = convexPose.rotate(polygon.mPlane.n); } } depth = validateDepth(sphere.radius - dmax); return true; } /////////////////////////////////////////////////////////////////////////////// //ML : capsule will be in the local space of convexHullV static bool internalComputeMTD_CapsuleConvex(const CapsuleV& capsule, const bool idtScale, ConvexHullV& convexHullV, const Ps::aos::PsTransformV& transf1, Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) { PolygonalData polyData; getPCMConvexData(convexHullV, idtScale, polyData); PxU8 buff[sizeof(SupportLocalImpl)]; SupportLocal* map = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(static_cast(convexHullV), transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale)) : static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(convexHullV, transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale))); return computeMTD(capsule, polyData, map, penetrationDepth, normal); } static bool computeMTD_CapsuleConvex(PxVec3& mtd, PxF32& depth, const Capsule& capsule, const PxTransform& capsulePose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) { const FloatV capsuleHalfHeight = FLoad(capsule.length()*0.5f); const FloatV capsuleRadius = FLoad(capsule.radius); const Vec3V zeroV = V3Zero(); // Convex mesh const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); const ConvexHullData* hull = &convexMesh->getHull(); const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); ConvexHullV convexHullV(hull, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); //~Convex mesh const QuatV q0 = QuatVLoadU(&capsulePose.q.x); const Vec3V p0 = V3LoadU(&capsulePose.p.x); const QuatV q1 = QuatVLoadU(&convexPose.q.x); const Vec3V p1 = V3LoadU(&convexPose.p.x); const PsTransformV transf0(p0, q0); const PsTransformV transf1(p1, q1); const PsTransformV curRTrans(transf1.transformInv(transf0)); const PsMatTransformV aToB(curRTrans); Vec3V normal = zeroV; FloatV penetrationDepth = FZero(); CapsuleV capsuleV(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); const bool idtScale = convexGeom.scale.isIdentity(); bool hasContacts = internalComputeMTD_CapsuleConvex(capsuleV, idtScale, convexHullV, transf1, penetrationDepth, normal); if(hasContacts) { FStore(penetrationDepth, &depth); depth = validateDepth(depth); V3StoreU(normal, mtd); } return hasContacts; } /////////////////////////////////////////////////////////////////////////////// static bool internalComputeMTD_BoxConvex(const PxVec3 halfExtents, const BoxV& box, const bool idtScale, ConvexHullV& convexHullV, const Ps::aos::PsTransformV& transf0, const Ps::aos::PsTransformV& transf1, Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) { PolygonalData polyData0; PCMPolygonalBox polyBox0(halfExtents); polyBox0.getPolygonalData(&polyData0); polyData0.mPolygonVertexRefs = gPCMBoxPolygonData; PolygonalData polyData1; getPCMConvexData(convexHullV, idtScale, polyData1); Mat33V identity = M33Identity(); SupportLocalImpl map0(box, transf0, identity, identity, true); PxU8 buff[sizeof(SupportLocalImpl)]; SupportLocal* map1 = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(static_cast(convexHullV), transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale)) : static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(convexHullV, transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale))); return computeMTD(polyData0, polyData1, &map0, map1, penetrationDepth, normal); } static bool computeMTD_BoxConvex(PxVec3& mtd, PxF32& depth, const Box& box, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) { const Vec3V zeroV = V3Zero(); const PxTransform boxPose = box.getTransform(); const Vec3V boxExtents = V3LoadU(box.extents); BoxV boxV(zeroV, boxExtents); // Convex mesh const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); const ConvexHullData* hull = &convexMesh->getHull(); const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); ConvexHullV convexHullV(hull, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); //~Convex mesh const QuatV q0 = QuatVLoadU(&boxPose.q.x); const Vec3V p0 = V3LoadU(&boxPose.p.x); const QuatV q1 = QuatVLoadU(&convexPose.q.x); const Vec3V p1 = V3LoadU(&convexPose.p.x); const PsTransformV transf0(p0, q0); const PsTransformV transf1(p1, q1); Vec3V normal=zeroV; FloatV penetrationDepth=FZero(); const bool idtScale = convexGeom.scale.isIdentity(); bool hasContacts = internalComputeMTD_BoxConvex(box.extents, boxV, idtScale, convexHullV, transf0, transf1, penetrationDepth, normal); if(hasContacts) { FStore(penetrationDepth, &depth); depth = validateDepth(depth); V3StoreU(normal, mtd); } return hasContacts; } static bool internalComputeMTD_ConvexConvex(const bool idtScale0, const bool idtScale1, ConvexHullV& convexHullV0, ConvexHullV& convexHullV1, const Ps::aos::PsTransformV& transf0, const Ps::aos::PsTransformV& transf1, Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) { PolygonalData polyData0, polyData1; getPCMConvexData(convexHullV0, idtScale0, polyData0); getPCMConvexData(convexHullV1, idtScale1, polyData1); PxU8 buff0[sizeof(SupportLocalImpl)]; PxU8 buff1[sizeof(SupportLocalImpl)]; SupportLocal* map0 = (idtScale0 ? static_cast(PX_PLACEMENT_NEW(buff0, SupportLocalImpl)(static_cast(convexHullV0), transf0, convexHullV0.vertex2Shape, convexHullV0.shape2Vertex, idtScale0)) : static_cast(PX_PLACEMENT_NEW(buff0, SupportLocalImpl)(convexHullV0, transf0, convexHullV0.vertex2Shape, convexHullV0.shape2Vertex, idtScale0))); SupportLocal* map1 = (idtScale1 ? static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(static_cast(convexHullV1), transf1, convexHullV1.vertex2Shape, convexHullV1.shape2Vertex, idtScale1)) : static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(convexHullV1, transf1, convexHullV1.vertex2Shape, convexHullV1.shape2Vertex, idtScale1))); return computeMTD(polyData0, polyData1, map0, map1, penetrationDepth, normal); } /////////////////////////////////////////////////////////////////////////////// static bool computeMTD_ConvexConvex(PxVec3& mtd, PxF32& depth, const PxConvexMeshGeometry& convexGeom0, const PxTransform& convexPose0, const PxConvexMeshGeometry& convexGeom1, const PxTransform& convexPose1) { using namespace Ps::aos; const Vec3V zeroV = V3Zero(); // Convex mesh const ConvexMesh* convexMesh0 = static_cast(convexGeom0.convexMesh); const ConvexHullData* hull0 = &convexMesh0->getHull(); const Vec3V vScale0 = V3LoadU_SafeReadW(convexGeom0.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale const QuatV vQuat0 = QuatVLoadU(&convexGeom0.scale.rotation.x); ConvexHullV convexHullV0(hull0, zeroV, vScale0, vQuat0, convexGeom0.scale.isIdentity()); //~Convex mesh // Convex mesh const ConvexMesh* convexMesh1 = static_cast(convexGeom1.convexMesh); const ConvexHullData* hull1 = &convexMesh1->getHull(); const Vec3V vScale1 = V3LoadU_SafeReadW(convexGeom1.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale const QuatV vQuat1 = QuatVLoadU(&convexGeom1.scale.rotation.x); ConvexHullV convexHullV1(hull1, zeroV, vScale1, vQuat1, convexGeom1.scale.isIdentity()); //~Convex mesh const QuatV q0 = QuatVLoadU(&convexPose0.q.x); const Vec3V p0 = V3LoadU(&convexPose0.p.x); const QuatV q1 = QuatVLoadU(&convexPose1.q.x); const Vec3V p1 = V3LoadU(&convexPose1.p.x); const PsTransformV transf0(p0, q0); const PsTransformV transf1(p1, q1); Vec3V normal = zeroV; FloatV penetrationDepth = FZero(); const bool idtScale0 = convexGeom0.scale.isIdentity(); const bool idtScale1 = convexGeom1.scale.isIdentity(); bool hasContacts = internalComputeMTD_ConvexConvex(idtScale0, idtScale1, convexHullV0, convexHullV1, transf0, transf1, penetrationDepth, normal); if(hasContacts) { FStore(penetrationDepth, &depth); depth = validateDepth(depth); V3StoreU(normal, mtd); } return hasContacts; } /////////////////////////////////////////////////////////////////////////////// static bool computeMTD_SpherePlane(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const PxPlane& plane) { const PxReal d = plane.distance(sphere.center); if(d>sphere.radius) return false; mtd = plane.n; depth = validateDepth(sphere.radius - d); return true; } static bool computeMTD_PlaneBox(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const Box& box) { PxVec3 pts[8]; box.computeBoxPoints(pts); PxReal dmin = plane.distance(pts[0]); for(PxU32 i=1;i<8;i++) { const PxReal d = plane.distance(pts[i]); dmin = physx::intrinsics::selectMin(dmin, d); } if(dmin>0.0f) return false; mtd = -plane.n; depth = validateDepth(-dmin); return true; } static bool computeMTD_PlaneCapsule(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const Capsule& capsule) { const PxReal d0 = plane.distance(capsule.p0); const PxReal d1 = plane.distance(capsule.p1); const PxReal dmin = physx::intrinsics::selectMin(d0, d1) - capsule.radius; if(dmin>0.0f) return false; mtd = -plane.n; depth = validateDepth(-dmin); return true; } static bool computeMTD_PlaneConvex(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) { const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); PxU32 nbVerts = convexMesh->getNbVerts(); const PxVec3* PX_RESTRICT verts = convexMesh->getVerts(); PxReal dmin = plane.distance(convexPose.transform(verts[0])); for(PxU32 i=1;i0.0f) return false; mtd = -plane.n; depth = validateDepth(-dmin); return true; } /////////////////////////////////////////////////////////////////////////////// static bool processContacts(PxVec3& mtd, PxF32& depth, PxU32 nbContacts, const ContactPoint* contacts) { if(nbContacts) { PxVec3 mn(0.0f), mx(0.0f); for(PxU32 i=0; i(geom0); const PxSphereGeometry& sphereGeom1 = static_cast(geom1); return computeMTD_SphereSphere(mtd, depth, Sphere(pose0.p, sphereGeom0.radius), Sphere(pose1.p, sphereGeom1.radius)); } static bool GeomMTDCallback_SpherePlane(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); PX_ASSERT(geom1.getType()==PxGeometryType::ePLANE); PX_UNUSED(geom1); const PxSphereGeometry& sphereGeom = static_cast(geom0); return computeMTD_SpherePlane(mtd, depth, Sphere(pose0.p, sphereGeom.radius), getPlane(pose1)); } static bool GeomMTDCallback_SphereCapsule(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); const PxSphereGeometry& sphereGeom = static_cast(geom0); const PxCapsuleGeometry& capsuleGeom = static_cast(geom1); Capsule capsule; getCapsuleSegment(pose1, capsuleGeom, capsule); capsule.radius = capsuleGeom.radius; return computeMTD_SphereCapsule(mtd, depth, Sphere(pose0.p, sphereGeom.radius), capsule); } static bool GeomMTDCallback_SphereBox(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); const PxSphereGeometry& sphereGeom = static_cast(geom0); const PxBoxGeometry& boxGeom = static_cast(geom1); Box obb; buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); return computeMTD_SphereBox(mtd, depth, Sphere(pose0.p, sphereGeom.radius), obb); } static bool GeomMTDCallback_SphereConvex(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); const PxSphereGeometry& sphereGeom = static_cast(geom0); const PxConvexMeshGeometry& convexGeom = static_cast(geom1); return computeMTD_SphereConvex(mtd, depth, Sphere(pose0.p, sphereGeom.radius), convexGeom, pose1); } static bool GeomMTDCallback_SphereMesh(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); const PxSphereGeometry& sphereGeom = static_cast(geom0); const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); return computeMTD_SphereMesh(mtd, depth, Sphere(pose0.p, sphereGeom.radius), meshGeom, pose1); } static bool GeomMTDCallback_PlaneCapsule(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); PX_UNUSED(geom0); const PxCapsuleGeometry& capsuleGeom = static_cast(geom1); Capsule capsule; getCapsuleSegment(pose1, capsuleGeom, capsule); capsule.radius = capsuleGeom.radius; return computeMTD_PlaneCapsule(mtd, depth, getPlane(pose0), capsule); } static bool GeomMTDCallback_PlaneBox(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); PX_UNUSED(geom0); const PxBoxGeometry& boxGeom = static_cast(geom1); Box obb; buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); return computeMTD_PlaneBox(mtd, depth, getPlane(pose0), obb); } static bool GeomMTDCallback_PlaneConvex(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); PX_UNUSED(geom0); const PxConvexMeshGeometry& convexGeom = static_cast(geom1); return computeMTD_PlaneConvex(mtd, depth, getPlane(pose0), convexGeom, pose1); } static bool GeomMTDCallback_CapsuleCapsule(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); const PxCapsuleGeometry& capsuleGeom0 = static_cast(geom0); const PxCapsuleGeometry& capsuleGeom1 = static_cast(geom1); Capsule capsule0; getCapsuleSegment(pose0, capsuleGeom0, capsule0); capsule0.radius = capsuleGeom0.radius; Capsule capsule1; getCapsuleSegment(pose1, capsuleGeom1, capsule1); capsule1.radius = capsuleGeom1.radius; return computeMTD_CapsuleCapsule(mtd, depth, capsule0, capsule1); } static bool GeomMTDCallback_CapsuleBox(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); const PxBoxGeometry& boxGeom = static_cast(geom1); Capsule capsule; getCapsuleSegment(pose0, capsuleGeom, capsule); capsule.radius = capsuleGeom.radius; Box obb; buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); return computeMTD_CapsuleBox(mtd, depth, capsule, obb); } static bool GeomMTDCallback_CapsuleConvex(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); const PxConvexMeshGeometry& convexGeom = static_cast(geom1); Capsule capsule; getCapsuleSegment(pose0, capsuleGeom, capsule); capsule.radius = capsuleGeom.radius; return computeMTD_CapsuleConvex(mtd, depth, capsule, pose0, convexGeom, pose1); } static bool GeomMTDCallback_CapsuleMesh(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); Capsule capsule; getCapsuleSegment(pose0, capsuleGeom, capsule); capsule.radius = capsuleGeom.radius; return computeMTD_CapsuleMesh(mtd, depth, capsule, meshGeom, pose1); } static bool GeomMTDCallback_BoxBox(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); const PxBoxGeometry& boxGeom0 = static_cast(geom0); const PxBoxGeometry& boxGeom1 = static_cast(geom1); Box obb0; buildFrom(obb0, pose0.p, boxGeom0.halfExtents, pose0.q); Box obb1; buildFrom(obb1, pose1.p, boxGeom1.halfExtents, pose1.q); return computeMTD_BoxBox(mtd, depth, obb0, obb1); } static bool GeomMTDCallback_BoxConvex(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); const PxBoxGeometry& boxGeom = static_cast(geom0); const PxConvexMeshGeometry& convexGeom = static_cast(geom1); Box obb; buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); return computeMTD_BoxConvex(mtd, depth, obb, convexGeom, pose1); } static bool GeomMTDCallback_BoxMesh(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); const PxBoxGeometry& boxGeom = static_cast(geom0); const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); Box obb; buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); return computeMTD_BoxMesh(mtd, depth, obb, meshGeom, pose1); } static bool GeomMTDCallback_ConvexConvex(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); const PxConvexMeshGeometry& convexGeom0 = static_cast(geom0); const PxConvexMeshGeometry& convexGeom1 = static_cast(geom1); return computeMTD_ConvexConvex(mtd, depth, convexGeom0, pose0, convexGeom1, pose1); } static bool GeomMTDCallback_ConvexMesh(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); const PxConvexMeshGeometry& convexGeom = static_cast(geom0); const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); return computeMTD_ConvexMesh(mtd, depth, convexGeom, pose0, meshGeom, pose1); } static bool GeomMTDCallback_SphereHeightField(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); const PxSphereGeometry& sphereGeom = static_cast(geom0); const PxHeightFieldGeometry& meshGeom = static_cast(geom1); const Sphere sphere(pose0.p, sphereGeom.radius); return computeMTD_SphereHeightField(mtd, depth, sphere, meshGeom, pose1); } static bool GeomMTDCallback_CapsuleHeightField(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); const PxHeightFieldGeometry& meshGeom = static_cast(geom1); Capsule capsule; getCapsuleSegment(pose0, capsuleGeom, capsule); capsule.radius = capsuleGeom.radius; return computeMTD_CapsuleHeightField(mtd, depth, capsule, meshGeom, pose1); } static bool GeomMTDCallback_BoxHeightField(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); const PxBoxGeometry& boxGeom = static_cast(geom0); const PxHeightFieldGeometry& meshGeom = static_cast(geom1); Box obb; buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); return computeMTD_BoxHeightField(mtd, depth, obb, meshGeom, pose1); } static bool GeomMTDCallback_ConvexHeightField(GU_MTD_FUNC_PARAMS) { PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); const PxConvexMeshGeometry& convexGeom = static_cast(geom0); const PxHeightFieldGeometry& meshGeom = static_cast(geom1); return computeMTD_ConvexHeightField(mtd, depth, convexGeom, pose0, meshGeom, pose1); } Gu::GeomMTDFunc gGeomMTDMethodTable[][PxGeometryType::eGEOMETRY_COUNT] = { //PxGeometryType::eSPHERE { GeomMTDCallback_SphereSphere, //PxGeometryType::eSPHERE GeomMTDCallback_SpherePlane, //PxGeometryType::ePLANE GeomMTDCallback_SphereCapsule, //PxGeometryType::eCAPSULE GeomMTDCallback_SphereBox, //PxGeometryType::eBOX GeomMTDCallback_SphereConvex, //PxGeometryType::eCONVEXMESH GeomMTDCallback_SphereMesh, //PxGeometryType::eTRIANGLEMESH GeomMTDCallback_SphereHeightField, //PxGeometryType::eHEIGHTFIELD }, //PxGeometryType::ePLANE { 0, //PxGeometryType::eSPHERE GeomMTDCallback_NotSupported, //PxGeometryType::ePLANE GeomMTDCallback_PlaneCapsule, //PxGeometryType::eCAPSULE GeomMTDCallback_PlaneBox, //PxGeometryType::eBOX GeomMTDCallback_PlaneConvex, //PxGeometryType::eCONVEXMESH GeomMTDCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD }, //PxGeometryType::eCAPSULE { 0, //PxGeometryType::eSPHERE 0, //PxGeometryType::ePLANE GeomMTDCallback_CapsuleCapsule, //PxGeometryType::eCAPSULE GeomMTDCallback_CapsuleBox, //PxGeometryType::eBOX GeomMTDCallback_CapsuleConvex, //PxGeometryType::eCONVEXMESH GeomMTDCallback_CapsuleMesh, //PxGeometryType::eTRIANGLEMESH GeomMTDCallback_CapsuleHeightField, //PxGeometryType::eHEIGHTFIELD }, //PxGeometryType::eBOX { 0, //PxGeometryType::eSPHERE 0, //PxGeometryType::ePLANE 0, //PxGeometryType::eCAPSULE GeomMTDCallback_BoxBox, //PxGeometryType::eBOX GeomMTDCallback_BoxConvex, //PxGeometryType::eCONVEXMESH GeomMTDCallback_BoxMesh, //PxGeometryType::eTRIANGLEMESH GeomMTDCallback_BoxHeightField, //PxGeometryType::eHEIGHTFIELD }, //PxGeometryType::eCONVEXMESH { 0, //PxGeometryType::eSPHERE 0, //PxGeometryType::ePLANE 0, //PxGeometryType::eCAPSULE 0, //PxGeometryType::eBOX GeomMTDCallback_ConvexConvex, //PxGeometryType::eCONVEXMESH GeomMTDCallback_ConvexMesh, //PxGeometryType::eTRIANGLEMESH GeomMTDCallback_ConvexHeightField, //PxGeometryType::eHEIGHTFIELD }, //PxGeometryType::eTRIANGLEMESH { 0, //PxGeometryType::eSPHERE 0, //PxGeometryType::ePLANE 0, //PxGeometryType::eCAPSULE 0, //PxGeometryType::eBOX 0, //PxGeometryType::eCONVEXMESH GeomMTDCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD }, //PxGeometryType::eHEIGHTFIELD { 0, //PxGeometryType::eSPHERE 0, //PxGeometryType::ePLANE 0, //PxGeometryType::eCAPSULE 0, //PxGeometryType::eBOX 0, //PxGeometryType::eCONVEXMESH 0, //PxGeometryType::eTRIANGLEMESH GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD }, };