// Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "geometry/PxSphereGeometry.h" #include "geometry/PxCustomGeometry.h" #include "geometry/PxConvexCoreGeometry.h" #include "geometry/PxHeightFieldGeometry.h" #include "GuSweepTests.h" #include "GuVecCapsule.h" #include "GuVecBox.h" #include "GuVecTriangle.h" #include "GuSweepTriangleUtils.h" #include "GuInternal.h" #include "GuGJKRaycast.h" #include "GuConvexGeometry.h" #include "GuConvexSupport.h" #include "GuMidphaseInterface.h" #include "geometry/PxGjkQuery.h" #include "GuHeightField.h" #include "GuEntityReport.h" #include "GuHeightFieldUtil.h" using namespace physx; using namespace Gu; using namespace Cm; using namespace aos; //#define USE_VIRTUAL_GJK #ifdef USE_VIRTUAL_GJK static bool virtualGjkRaycastPenetration(const GjkConvex& a, const GjkConvex& b, const aos::Vec3VArg initialDir, const aos::FloatVArg initialLambda, const aos::Vec3VArg s, const aos::Vec3VArg r, aos::FloatV& lambda, aos::Vec3V& normal, aos::Vec3V& closestA, const PxReal _inflation, const bool initialOverlap) { return gjkRaycastPenetration(a, b, initialDir, initialLambda, s, r, lambda, normal, closestA, _inflation, initialOverlap); } #endif bool sweepCapsule_BoxGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) { PX_UNUSED(hitFlags); PX_UNUSED(threadContext); using namespace aos; PX_ASSERT(geom.getType() == PxGeometryType::eBOX); const PxBoxGeometry& boxGeom = static_cast(geom); const FloatV zero = FZero(); const Vec3V zeroV = V3Zero(); const Vec3V boxExtents0 = V3LoadU(boxGeom.halfExtents); const FloatV dist = FLoad(distance); const Vec3V worldDir = V3LoadU(unitDir); const PxTransformV capPos = loadTransformU(capsulePose_); const PxTransformV boxPos = loadTransformU(pose); const PxMatTransformV aToB(boxPos.transformInv(capPos)); const FloatV capsuleHalfHeight = FLoad(capsuleGeom_.halfHeight); const FloatV capsuleRadius = FLoad(lss.radius); BoxV box(zeroV, boxExtents0); CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); const Vec3V dir = boxPos.rotateInv(V3Neg(V3Scale(worldDir, dist))); const bool isMtd = hitFlags & PxHitFlag::eMTD; FloatV toi = FMax(); Vec3V closestA, normal;//closestA and normal is in the local space of box const LocalConvex convexA(capsule); const LocalConvex convexB(box); const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), box.getCenter()); #ifdef USE_VIRTUAL_GJK if(!virtualGjkRaycastPenetration(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, lss.radius + inflation, isMtd)) return false; #else if(!gjkRaycastPenetration, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, lss.radius + inflation, isMtd)) return false; #endif sweepHit.flags = PxHitFlag::eNORMAL; if(FAllGrtrOrEq(zero, toi)) { //initial overlap if(isMtd) { sweepHit.flags |= PxHitFlag::ePOSITION; const Vec3V worldPointA = boxPos.transform(closestA); const Vec3V destNormal = boxPos.rotate(normal); const FloatV length = toi; const Vec3V destWorldPointA = V3NegScaleSub(destNormal, length, worldPointA); V3StoreU(destWorldPointA, sweepHit.position); V3StoreU(destNormal, sweepHit.normal); FStore(length, &sweepHit.distance); } else { sweepHit.distance = 0.0f; sweepHit.normal = -unitDir; } } else { sweepHit.flags |= PxHitFlag::ePOSITION; const Vec3V worldPointA = boxPos.transform(closestA); const Vec3V destNormal = boxPos.rotate(normal); const FloatV length = FMul(dist, toi); const Vec3V destWorldPointA = V3ScaleAdd(worldDir, length, worldPointA); V3StoreU(destNormal, sweepHit.normal); V3StoreU(destWorldPointA, sweepHit.position); FStore(length, &sweepHit.distance); } return true; } bool sweepBox_SphereGeom(GU_BOX_SWEEP_FUNC_PARAMS) { PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); PX_UNUSED(threadContext); PX_UNUSED(hitFlags); PX_UNUSED(boxGeom_); const PxSphereGeometry& sphereGeom = static_cast(geom); const FloatV zero = FZero(); const Vec3V zeroV = V3Zero(); const Vec3V boxExtents = V3LoadU(box.extents); const FloatV worldDist = FLoad(distance); const Vec3V unitDirV = V3LoadU(unitDir); const FloatV sphereRadius = FLoad(sphereGeom.radius); const PxTransformV spherePos = loadTransformU(pose); const PxTransformV boxPos = loadTransformU(boxPose_); const PxMatTransformV aToB(boxPos.transformInv(spherePos)); const BoxV boxV(zeroV, boxExtents); const CapsuleV capsuleV(aToB.p, sphereRadius); //transform into b space const Vec3V dir = boxPos.rotateInv(V3Scale(unitDirV, worldDist)); const bool isMtd = hitFlags & PxHitFlag::eMTD; FloatV toi; Vec3V closestA, normal;//closestA and normal is in the local space of box const Vec3V initialSearchDir = V3Sub(capsuleV.getCenter(), boxV.getCenter()); const LocalConvex convexA(capsuleV); const LocalConvex convexB(boxV); #ifdef USE_VIRTUAL_GJK if(!virtualGjkRaycastPenetration(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, sphereGeom.radius+inflation, isMtd)) return false; #else if(!gjkRaycastPenetration, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, sphereGeom.radius+inflation, isMtd)) return false; #endif sweepHit.flags = PxHitFlag::eNORMAL; //initial overlap if(FAllGrtrOrEq(zero, toi)) { if(isMtd) { sweepHit.flags |= PxHitFlag::ePOSITION; const Vec3V destWorldPointA = boxPos.transform(closestA); const Vec3V destNormal = V3Neg(boxPos.rotate(normal)); const FloatV length = toi; V3StoreU(destNormal, sweepHit.normal); V3StoreU(destWorldPointA, sweepHit.position); FStore(length, &sweepHit.distance); } else { sweepHit.distance = 0.0f; sweepHit.normal = -unitDir; } } else { sweepHit.flags |= PxHitFlag::ePOSITION; const Vec3V destWorldPointA = boxPos.transform(closestA); const Vec3V destNormal = V3Neg(boxPos.rotate(normal)); const FloatV length = FMul(worldDist, toi); V3StoreU(destNormal, sweepHit.normal); V3StoreU(destWorldPointA, sweepHit.position); FStore(length, &sweepHit.distance); } return true; } bool sweepBox_CapsuleGeom(GU_BOX_SWEEP_FUNC_PARAMS) { using namespace aos; PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); PX_UNUSED(threadContext); PX_UNUSED(hitFlags); PX_UNUSED(boxGeom_); const PxCapsuleGeometry& capsuleGeom = static_cast(geom); const FloatV capsuleHalfHeight = FLoad(capsuleGeom.halfHeight); const FloatV capsuleRadius = FLoad(capsuleGeom.radius); const FloatV zero = FZero(); const Vec3V zeroV = V3Zero(); const Vec3V boxExtents = V3LoadU(box.extents); const FloatV worldDist = FLoad(distance); const Vec3V unitDirV = V3LoadU(unitDir); const PxTransformV capPos = loadTransformU(pose); const PxTransformV boxPos = loadTransformU(boxPose_); const PxMatTransformV aToB(boxPos.transformInv(capPos)); const BoxV boxV(zeroV, boxExtents); const CapsuleV capsuleV(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); //transform into b space const Vec3V dir = boxPos.rotateInv(V3Scale(unitDirV, worldDist)); const bool isMtd = hitFlags & PxHitFlag::eMTD; FloatV toi; Vec3V closestA, normal;//closestA and normal is in the local space of box const Vec3V initialSearchDir = V3Sub(capsuleV.getCenter(), boxV.getCenter()); const LocalConvex convexA(capsuleV); const LocalConvex convexB(boxV); #ifdef USE_VIRTUAL_GJK if(!virtualGjkRaycastPenetration(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, capsuleGeom.radius+inflation, isMtd)) return false; #else if(!gjkRaycastPenetration, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, capsuleGeom.radius+inflation, isMtd)) return false; #endif sweepHit.flags = PxHitFlag::eNORMAL; //initial overlap if(FAllGrtrOrEq(zero, toi)) { if(isMtd) { sweepHit.flags |= PxHitFlag::ePOSITION; //initial overlap is toi < 0 const FloatV length = toi; const Vec3V destWorldPointA = boxPos.transform(closestA); const Vec3V destNormal = boxPos.rotate(normal); V3StoreU(V3Neg(destNormal), sweepHit.normal); V3StoreU(destWorldPointA, sweepHit.position); FStore(length, &sweepHit.distance); } else { sweepHit.distance = 0.0f; sweepHit.normal = -unitDir; } return true; } else { sweepHit.flags |= PxHitFlag::ePOSITION; const Vec3V destWorldPointA = boxPos.transform(closestA); const Vec3V destNormal = boxPos.rotate(normal); const FloatV length = FMul(worldDist, toi); V3StoreU(V3Neg(destNormal), sweepHit.normal); V3StoreU(destWorldPointA, sweepHit.position); FStore(length, &sweepHit.distance); } return true; } bool sweepBox_BoxGeom(GU_BOX_SWEEP_FUNC_PARAMS) { PX_ASSERT(geom.getType() == PxGeometryType::eBOX); PX_UNUSED(threadContext); PX_UNUSED(boxGeom_); const PxBoxGeometry& boxGeom = static_cast(geom); const FloatV zero = FZero(); const Vec3V zeroV = V3Zero(); const Vec3V boxExtents0 = V3LoadU(boxGeom.halfExtents); const Vec3V boxExtents1 = V3LoadU(box.extents); const FloatV worldDist = FLoad(distance); const Vec3V unitDirV = V3LoadU(unitDir); const PxTransformV boxTrans0 = loadTransformU(pose); const PxTransformV boxTrans1 = loadTransformU(boxPose_); const PxMatTransformV aToB(boxTrans1.transformInv(boxTrans0)); const BoxV box0(zeroV, boxExtents0); const BoxV box1(zeroV, boxExtents1); //transform into b space const Vec3V dir = boxTrans1.rotateInv(V3Scale(unitDirV, worldDist)); const bool isMtd = hitFlags & PxHitFlag::eMTD; FloatV toi; Vec3V closestA, normal;//closestA and normal is in the local space of box const RelativeConvex convexA(box0, aToB); const LocalConvex convexB(box1); #ifdef USE_VIRTUAL_GJK if(!virtualGjkRaycastPenetration(convexA, convexB, aToB.p, zero, zeroV, dir, toi, normal, closestA, inflation, isMtd)) return false; #else if(!gjkRaycastPenetration, LocalConvex >(convexA, convexB, aToB.p, zero, zeroV, dir, toi, normal, closestA, inflation, isMtd)) return false; #endif sweepHit.flags = PxHitFlag::eNORMAL; if(FAllGrtrOrEq(zero, toi)) { if(isMtd) { sweepHit.flags |= PxHitFlag::ePOSITION; const FloatV length = toi; const Vec3V destWorldPointA = boxTrans1.transform(closestA); const Vec3V destNormal = V3Normalize(boxTrans1.rotate(normal)); V3StoreU(V3Neg(destNormal), sweepHit.normal); V3StoreU(destWorldPointA, sweepHit.position); FStore(length, &sweepHit.distance); } else { sweepHit.distance = 0.0f; sweepHit.normal = -unitDir; } } else { sweepHit.flags |= PxHitFlag::ePOSITION; const Vec3V destWorldPointA = boxTrans1.transform(closestA); const Vec3V destNormal = V3Normalize(boxTrans1.rotate(normal)); const FloatV length = FMul(worldDist, toi); V3StoreU(V3Neg(destNormal), sweepHit.normal); V3StoreU(destWorldPointA, sweepHit.position); FStore(length, &sweepHit.distance); } return true; } bool Gu::sweepBoxTriangles(GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxBoxGeometry)) { PX_UNUSED(hitFlags); if(!nbTris) return false; const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; const bool doBackfaceCulling = !doubleSided && !meshBothSides; Box box; buildFrom(box, pose.p, geom.halfExtents, pose.q); PxGeomSweepHit sweepHit; // Move to AABB space PxMat34 worldToBox; computeWorldToBoxMatrix(worldToBox, box); const PxVec3 localDir = worldToBox.rotate(unitDir); const PxVec3 localMotion = localDir * distance; const Vec3V base0 = V3LoadU(worldToBox.m.column0); const Vec3V base1 = V3LoadU(worldToBox.m.column1); const Vec3V base2 = V3LoadU(worldToBox.m.column2); const Mat33V matV(base0, base1, base2); const Vec3V p = V3LoadU(worldToBox.p); const PxMatTransformV worldToBoxV(p, matV); const FloatV zero = FZero(); const Vec3V zeroV = V3Zero(); const Vec3V boxExtents = V3LoadU(box.extents); const Vec3V boxDir = V3LoadU(localDir); const FloatV inflationV = FLoad(inflation); const Vec3V absBoxDir = V3Abs(boxDir); const FloatV boxRadiusV = FAdd(V3Dot(absBoxDir, boxExtents), inflationV); BoxV boxV(zeroV, boxExtents); #if PX_DEBUG PxU32 totalTestsExpected = nbTris; PxU32 totalTestsReal = 0; PX_UNUSED(totalTestsExpected); PX_UNUSED(totalTestsReal); #endif Vec3V boxLocalMotion = V3LoadU(localMotion); Vec3V minClosestA = zeroV, minNormal = zeroV; PxU32 minTriangleIndex = 0; PxVec3 bestTriNormal(0.0f); FloatV dist = FLoad(distance); const PxTransformV boxPos = loadTransformU(pose); bool status = false; const PxU32 idx = cachedIndex ? *cachedIndex : 0; for(PxU32 ii=0;ii convexA(triangleV); const LocalConvex convexB(boxV); const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), boxV.getCenter()); #ifdef USE_VIRTUAL_GJK if(virtualGjkRaycastPenetration(convexA, convexB, initialSearchDir, zero, zeroV, boxLocalMotion, lambda, normal, closestA, inflation, false)) #else if(gjkRaycastPenetration, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, boxLocalMotion, lambda, normal, closestA, inflation, false)) #endif { //hitCount++; if(FAllGrtrOrEq(zero, lambda)) { hit.distance = 0.0f; hit.faceIndex = triangleIndex; hit.normal = -unitDir; hit.flags = PxHitFlag::eNORMAL; return true; } dist = FMul(dist, lambda); boxLocalMotion = V3Scale(boxDir, dist); minClosestA = closestA; minNormal = normal; minTriangleIndex = triangleIndex; V3StoreU(triNormal, bestTriNormal); status = true; if(hitFlags & PxHitFlag::eANY_HIT) break; } } if(!status) return false; hit.faceIndex = minTriangleIndex; const Vec3V destNormal = V3Neg(V3Normalize(boxPos.rotate(minNormal))); const Vec3V destWorldPointA = boxPos.transform(minClosestA); V3StoreU(destNormal, hit.normal); V3StoreU(destWorldPointA, hit.position); FStore(dist, &hit.distance); // PT: by design, returned normal is opposed to the sweep direction. if(shouldFlipNormal(hit.normal, meshBothSides, doubleSided, bestTriNormal, unitDir)) hit.normal = -hit.normal; hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eNORMAL; return true; } bool sweepCapsule_SphereGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); bool sweepCapsule_PlaneGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); bool sweepCapsule_CapsuleGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); bool sweepCapsule_BoxGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); bool sweepCapsule_BoxGeom_Precise (GU_CAPSULE_SWEEP_FUNC_PARAMS); bool sweepCapsule_ConvexGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); bool sweepCapsule_MeshGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); bool sweepCapsule_HeightFieldGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); bool sweepBox_SphereGeom (GU_BOX_SWEEP_FUNC_PARAMS); bool sweepBox_SphereGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); bool sweepBox_PlaneGeom (GU_BOX_SWEEP_FUNC_PARAMS); bool sweepBox_CapsuleGeom (GU_BOX_SWEEP_FUNC_PARAMS); bool sweepBox_CapsuleGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); bool sweepBox_BoxGeom (GU_BOX_SWEEP_FUNC_PARAMS); bool sweepBox_BoxGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); bool sweepBox_ConvexGeom (GU_BOX_SWEEP_FUNC_PARAMS); bool sweepBox_MeshGeom (GU_BOX_SWEEP_FUNC_PARAMS); bool sweepBox_HeightFieldGeom (GU_BOX_SWEEP_FUNC_PARAMS); bool sweepBox_HeightFieldGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS); bool sweepConvex_SphereGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); bool sweepConvex_PlaneGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); bool sweepConvex_CapsuleGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); bool sweepConvex_BoxGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); bool sweepConvex_ConvexCoreGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); bool sweepConvex_ConvexGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); bool sweepConvex_MeshGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); bool sweepConvex_HeightFieldGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); static bool sweepCapsule_InvalidGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(capsuleGeom_); PX_UNUSED(capsulePose_); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(lss); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); return false; } static bool sweepBox_InvalidGeom(GU_BOX_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(boxPose_); PX_UNUSED(boxGeom_); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(box); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); return false; } static bool sweepConvexCore_InvalidGeom(GU_CONVEXCORE_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(convexGeom); PX_UNUSED(convexPose); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); return false; } static bool sweepConvex_InvalidGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(convexGeom); PX_UNUSED(convexPose); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); return false; } static bool sweepCapsule_CustomGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) { PX_UNUSED(lss); if(geom.getType() == PxGeometryType::eCUSTOM) return static_cast(geom).callbacks->sweep(unitDir, distance, geom, pose, capsuleGeom_, capsulePose_, sweepHit, hitFlags, inflation, threadContext); return false; } static bool sweepBox_CustomGeom(GU_BOX_SWEEP_FUNC_PARAMS) { PX_UNUSED(box); if(geom.getType() == PxGeometryType::eCUSTOM) return static_cast(geom).callbacks->sweep(unitDir, distance, geom, pose, boxGeom_, boxPose_, sweepHit, hitFlags, inflation, threadContext); return false; } static bool sweepConvexCore_CustomGeom(GU_CONVEXCORE_SWEEP_FUNC_PARAMS) { if (geom.getType() == PxGeometryType::eCUSTOM) return static_cast(geom).callbacks->sweep(unitDir, distance, geom, pose, convexGeom, convexPose, sweepHit, hitFlags, inflation, threadContext); return false; } static bool sweepConvex_CustomGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) { if(geom.getType() == PxGeometryType::eCUSTOM) return static_cast(geom).callbacks->sweep(unitDir, distance, geom, pose, convexGeom, convexPose, sweepHit, hitFlags, inflation, threadContext); return false; } static bool sweepCapsule_ConvexCoreGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(capsuleGeom_); PX_UNUSED(capsulePose_); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(lss); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); struct CapsuleSupport : PxGjkQuery::Support { PxCapsuleGeometry capsule; CapsuleSupport(const PxCapsuleGeometry& c) : capsule(c) {} virtual PxReal getMargin() const { return capsule.radius; } virtual PxVec3 supportLocal(const PxVec3& dir) const { return PxVec3(PxSign(dir.x) * capsule.halfHeight, 0, 0); } }; struct ConvexCoreSupport : PxGjkQuery::Support { Gu::ConvexShape shape; ConvexCoreSupport(const PxConvexCoreGeometry& g) { Gu::makeConvexShape(g, PxTransform(PxIdentity), shape); } virtual PxReal getMargin() const { return shape.margin; } virtual PxVec3 supportLocal(const PxVec3& dir) const { return shape.supportLocal(dir); } }; const PxConvexCoreGeometry& convex = static_cast(geom); if (convex.isValid()) { PxBounds3 bounds = Gu::computeBounds(convex, pose); bounds.include(Gu::computeBounds(capsuleGeom_, capsulePose_)); PxReal wiseDist = PxMin(distance, bounds.getDimensions().magnitude()); PxReal t; PxVec3 n, p; if (PxGjkQuery::sweep(ConvexCoreSupport(convex), CapsuleSupport(capsuleGeom_), pose, capsulePose_, unitDir, wiseDist, t, n, p)) { PxGeomSweepHit& hit = sweepHit; hit.distance = t; hit.position = p; hit.normal = n; hit.flags |= PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; return 1; } } return 0; } static bool sweepBox_ConvexCoreGeom(GU_BOX_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(boxPose_); PX_UNUSED(boxGeom_); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(box); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); struct BoxSupport : PxGjkQuery::Support { PxBoxGeometry box; BoxSupport(const PxBoxGeometry& b) : box(b) {} virtual PxReal getMargin() const { return 0; } virtual PxVec3 supportLocal(const PxVec3& dir) const { return PxVec3(PxSign(dir.x) * box.halfExtents.x, PxSign(dir.y) * box.halfExtents.y, PxSign(dir.z) * box.halfExtents.z); } }; struct ConvexCoreSupport : PxGjkQuery::Support { Gu::ConvexShape shape; ConvexCoreSupport(const PxConvexCoreGeometry& g) { Gu::makeConvexShape(g, PxTransform(PxIdentity), shape); } virtual PxReal getMargin() const { return shape.margin; } virtual PxVec3 supportLocal(const PxVec3& dir) const { return shape.supportLocal(dir); } }; const PxConvexCoreGeometry& convex = static_cast(geom); if (convex.isValid()) { PxBounds3 bounds = Gu::computeBounds(convex, pose); bounds.include(Gu::computeBounds(boxGeom_, boxPose_)); PxReal wiseDist = PxMin(distance, bounds.getDimensions().magnitude()); PxReal t; PxVec3 n, p; if (PxGjkQuery::sweep(ConvexCoreSupport(convex), BoxSupport(boxGeom_), pose, boxPose_, unitDir, wiseDist, t, n, p)) { PxGeomSweepHit& hit = sweepHit; hit.distance = t; hit.position = p; hit.normal = n; hit.flags |= PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; return 1; } } return 0; } static bool sweepConvexCore_Plane(GU_CONVEXCORE_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(convexGeom); PX_UNUSED(convexPose); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); PxPlane plane = getPlane(pose); Gu::ConvexShape shape; Gu::makeConvexShape(convexGeom, convexPose, shape); shape.margin += inflation; PxVec3 closestPoint = shape.support(-plane.n); PxReal closestDist = plane.distance(closestPoint); sweepHit.faceIndex = 0xffffffff; if (closestDist <= 0) { if (hitFlags & PxHitFlag::eMTD) { sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::ePOSITION; sweepHit.distance = closestDist; sweepHit.normal = plane.n; sweepHit.position = closestPoint + plane.n * closestDist; return true; } else if (!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) { sweepHit.flags = PxHitFlag::eNORMAL; sweepHit.distance = 0.0f; sweepHit.normal = -unitDir; return true; } } PxReal dirDotNorm = unitDir.dot(plane.n); const PxReal dotEps = 1e-5f; if (dirDotNorm < -dotEps) { PxReal hitDistance = closestDist / -dirDotNorm; if (hitDistance < distance) { sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::ePOSITION; sweepHit.distance = hitDistance; sweepHit.normal = plane.n; sweepHit.position = closestPoint + unitDir * hitDistance; return true; } } return false; } static bool sweepConvexCore_Convex(GU_CONVEXCORE_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(convexGeom); PX_UNUSED(convexPose); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); bool res = false; switch (geom.getType()) { case PxGeometryType::eSPHERE: res = sweepCapsule_ConvexCoreGeom(convexGeom, convexPose, PxCapsuleGeometry(static_cast(geom).radius, 0), pose, Gu::Capsule(), -unitDir, distance, sweepHit, hitFlags, inflation, threadContext); break; case PxGeometryType::eCAPSULE: res = sweepCapsule_ConvexCoreGeom(convexGeom, convexPose, static_cast(geom), pose, Gu::Capsule(), -unitDir, distance, sweepHit, hitFlags, inflation, threadContext); break; case PxGeometryType::eBOX: res = sweepBox_ConvexCoreGeom(convexGeom, convexPose, static_cast(geom), pose, Gu::Box(), -unitDir, distance, sweepHit, hitFlags, inflation, threadContext); break; case PxGeometryType::eCONVEXMESH: res = sweepConvex_ConvexCoreGeom(convexGeom, convexPose, static_cast(geom), pose, -unitDir, distance, sweepHit, hitFlags, inflation, threadContext); break; default: PX_ASSERT(0); } if (res) { if (sweepHit.flags & PxHitFlag::eNORMAL) sweepHit.normal = -sweepHit.normal; if (sweepHit.flags & PxHitFlag::ePOSITION) sweepHit.position += unitDir * sweepHit.distance; } return res; } static bool sweepConvexCore_ConvexCore(GU_CONVEXCORE_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(convexGeom); PX_UNUSED(convexPose); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); struct ConvexCoreSupport : PxGjkQuery::Support { Gu::ConvexShape shape; ConvexCoreSupport(const PxConvexCoreGeometry& g) { Gu::makeConvexShape(g, PxTransform(PxIdentity), shape); } virtual PxReal getMargin() const { return shape.margin; } virtual PxVec3 supportLocal(const PxVec3& dir) const { return shape.supportLocal(dir); } }; const PxConvexCoreGeometry& convex = static_cast(geom); if (convex.isValid()) { PxBounds3 bounds = Gu::computeBounds(convex, pose); bounds.include(Gu::computeBounds(convexGeom, convexPose)); PxReal wiseDist = PxMin(distance, bounds.getDimensions().magnitude()); PxReal t; PxVec3 n, p; if (PxGjkQuery::sweep(ConvexCoreSupport(convex), ConvexCoreSupport(convexGeom), pose, convexPose, unitDir, wiseDist, t, n, p)) { PxGeomSweepHit& hit = sweepHit; hit.distance = t; hit.position = p; hit.normal = n; hit.flags |= PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; return true; } } return false; } static bool sweepConvexCore_MeshGeom(GU_CONVEXCORE_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(convexGeom); PX_UNUSED(convexPose); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); struct TriSupport : PxGjkQuery::Support { PxVec3 v0, v1, v2; TriSupport(const PxVec3& _v0, const PxVec3& _v1, const PxVec3& _v2) : v0(_v0), v1(_v1), v2(_v2) {} virtual PxReal getMargin() const { return 0; } virtual PxVec3 supportLocal(const PxVec3& dir) const { PxReal d0 = dir.dot(v0), d1 = dir.dot(v1), d2 = dir.dot(v2); return (d0 > d1 && d0 > d2) ? v0 : (d1 > d2) ? v1 : v2; } }; struct ConvexCoreSupport : PxGjkQuery::Support { Gu::ConvexShape shape; PxReal inflation; ConvexCoreSupport(const PxConvexCoreGeometry& g, PxReal inf) : inflation(inf) { Gu::makeConvexShape(g, PxTransform(PxIdentity), shape); } virtual PxReal getMargin() const { return shape.margin + inflation; } virtual PxVec3 supportLocal(const PxVec3& dir) const { return shape.supportLocal(dir); } }; struct Callback : MeshHitCallback { ConvexCoreSupport convexSupport; PxTransform convexPose; PxVec3 unitDir; PxReal maxDist; PxGeomSweepHit closestHit; Callback(const PxConvexCoreGeometry& geom, const PxTransform& pose, const PxVec3& dir, PxReal dist, PxReal inf) : MeshHitCallback(CallbackMode::eMULTIPLE), convexSupport(geom, inf), convexPose(pose), unitDir(dir), maxDist(dist) { closestHit.distance = FLT_MAX; } virtual PxAgain processHit(const PxGeomRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32*) { PxReal t; PxVec3 n, p; TriSupport triSupport(v0, v1, v2); if (PxGjkQuery::sweep(triSupport, convexSupport, PxTransform(PxIdentity), convexPose, unitDir, maxDist, t, n, p)) { if (t < closestHit.distance) { closestHit.distance = t; closestHit.position = p; closestHit.normal = n; closestHit.faceIndex = hit.faceIndex; } } return true; } }; const PxTriangleMeshGeometry& meshGeom = static_cast(geom); const TriangleMesh* meshData = _getMeshData(meshGeom); const bool doubleSided = (meshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES); PxTransform localPose = pose.transformInv(convexPose); PxVec3 localDir = pose.transformInv(unitDir); PxBounds3 bounds = Gu::computeBounds(convexGeom, localPose); bounds.include(Gu::computeBounds(convexGeom, PxTransform(localPose.p + localDir * distance, localPose.q))); Box queryBox; queryBox.extents = bounds.getExtents() + PxVec3(inflation); queryBox.center = bounds.getCenter(); queryBox.rot = PxMat33(PxIdentity); Callback callback(convexGeom, localPose, localDir, distance, inflation); Midphase::intersectOBB(meshData, queryBox, callback, doubleSided); if (callback.closestHit.distance <= distance) { PxGeomSweepHit& hit = sweepHit; hit.distance = callback.closestHit.distance; hit.position = pose.transform(callback.closestHit.position); hit.normal = pose.rotate(callback.closestHit.normal); hit.faceIndex = callback.closestHit.faceIndex; hit.flags |= PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; return true; } return false; } bool sweepConvexCore_HeightFieldGeom(GU_CONVEXCORE_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(convexGeom); PX_UNUSED(convexPose); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); struct TriSupport : PxGjkQuery::Support { PxVec3 v0, v1, v2; TriSupport(const PxVec3& _v0, const PxVec3& _v1, const PxVec3& _v2) : v0(_v0), v1(_v1), v2(_v2) {} virtual PxReal getMargin() const { return 0; } virtual PxVec3 supportLocal(const PxVec3& dir) const { PxReal d0 = dir.dot(v0), d1 = dir.dot(v1), d2 = dir.dot(v2); return (d0 > d1 && d0 > d2) ? v0 : (d1 > d2) ? v1 : v2; } }; struct ConvexCoreSupport : PxGjkQuery::Support { Gu::ConvexShape shape; PxReal inflation; ConvexCoreSupport(const PxConvexCoreGeometry& g, PxReal inf) : inflation(inf) { Gu::makeConvexShape(g, PxTransform(PxIdentity), shape); } virtual PxReal getMargin() const { return shape.margin + inflation; } virtual PxVec3 supportLocal(const PxVec3& dir) const { return shape.supportLocal(dir); } }; struct Callback : Gu::OverlapReport { ConvexCoreSupport convexSupport; PxTransform convexPose; PxVec3 unitDir; PxReal maxDist; const Gu::HeightFieldUtil mHfUtil; const PxTransform& mHFPose; PxGeomSweepHit closestHit; Callback(const PxConvexCoreGeometry& geom, const PxTransform& pose, const PxVec3& dir, PxReal dist, PxReal inf, const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose) : convexSupport(geom, inf), convexPose(pose), unitDir(dir), maxDist(dist), mHfUtil(hfGeom), mHFPose(hfPose) { closestHit.distance = FLT_MAX; } virtual bool reportTouchedTris(PxU32 nb, const PxU32* indices) { while(nb--) { const PxU32 triangleIndex = *indices++; PxTrianglePadded currentTriangle; mHfUtil.getTriangle(mHFPose, currentTriangle, NULL, NULL, triangleIndex, false, false); const PxVec3& v0 = currentTriangle.verts[0]; const PxVec3& v1 = currentTriangle.verts[1]; const PxVec3& v2 = currentTriangle.verts[2]; PxReal t; PxVec3 n, p; TriSupport triSupport(v0, v1, v2); if (PxGjkQuery::sweep(triSupport, convexSupport, PxTransform(PxIdentity), convexPose, unitDir, maxDist, t, n, p)) { if (t < closestHit.distance) { closestHit.distance = t; closestHit.position = p; closestHit.normal = n; closestHit.faceIndex = triangleIndex; } } } return true; } }; const PxHeightFieldGeometry& hfGeom = static_cast(geom); const HeightField* hf = static_cast(hfGeom.heightField); if(!hf) return false; PxTransform localPose = pose.transformInv(convexPose); PxVec3 localDir = pose.rotateInv(unitDir); PxBounds3 bounds = Gu::computeBounds(convexGeom, localPose); bounds.include(Gu::computeBounds(convexGeom, PxTransform(localPose.p + localDir * distance, localPose.q))); Callback callback(convexGeom, localPose, localDir, distance, inflation, hfGeom, pose); callback.mHfUtil.overlapAABBTriangles(bounds, callback, 4); if (callback.closestHit.distance <= distance) { PxGeomSweepHit& hit = sweepHit; hit.distance = callback.closestHit.distance; hit.position = pose.transform(callback.closestHit.position); hit.normal = pose.rotate(callback.closestHit.normal); hit.faceIndex = callback.closestHit.faceIndex; hit.flags |= PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; return true; } return false; } bool sweepConvex_ConvexCoreGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) { PX_UNUSED(threadContext); PX_UNUSED(geom); PX_UNUSED(pose); PX_UNUSED(convexGeom); PX_UNUSED(convexPose); PX_UNUSED(unitDir); PX_UNUSED(distance); PX_UNUSED(sweepHit); PX_UNUSED(hitFlags); PX_UNUSED(inflation); struct ConvexSupport : PxGjkQuery::Support { PxConvexMeshGeometry convex; PxReal inflation; ConvexSupport(const PxConvexMeshGeometry& c, PxReal inf) : convex(c), inflation(inf) {} virtual PxReal getMargin() const { return inflation; } virtual PxVec3 supportLocal(const PxVec3& dir) const { PxVec3 d = convex.scale.rotation.rotateInv(convex.scale.rotation.rotate(dir) .multiply(convex.scale.scale)); const PxVec3* verts = convex.convexMesh->getVertices(); int count = int(convex.convexMesh->getNbVertices()); float maxDot = -FLT_MAX; int index = -1; for (int i = 0; i < count; ++i) { float dot = verts[i].dot(d); if (dot > maxDot) { maxDot = dot; index = i; } } if (index == -1) return PxVec3(0); return convex.scale.rotation.rotateInv(convex.scale.rotation.rotate(verts[index]) .multiply(convex.scale.scale)); } }; struct ConvexCoreSupport : PxGjkQuery::Support { Gu::ConvexShape shape; ConvexCoreSupport(const PxConvexCoreGeometry& g) { Gu::makeConvexShape(g, PxTransform(PxIdentity), shape); } virtual PxReal getMargin() const { return shape.margin; } virtual PxVec3 supportLocal(const PxVec3& dir) const { return shape.supportLocal(dir); } }; const PxConvexCoreGeometry& convex = static_cast(geom); if (convex.isValid()) { PxBounds3 bounds = Gu::computeBounds(convex, pose); bounds.include(Gu::computeBounds(convexGeom, convexPose)); PxReal wiseDist = PxMin(distance, bounds.getDimensions().magnitude()); PxReal t; PxVec3 n, p; if (PxGjkQuery::sweep(ConvexCoreSupport(convex), ConvexSupport(convexGeom, inflation), pose, convexPose, unitDir, wiseDist, t, n, p)) { PxGeomSweepHit& hit = sweepHit; hit.distance = t; hit.position = p; hit.normal = n; hit.flags |= PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; return true; } } return false; } Gu::GeomSweepFuncs gGeomSweepFuncs = { { sweepCapsule_SphereGeom, sweepCapsule_PlaneGeom, sweepCapsule_CapsuleGeom, sweepCapsule_BoxGeom, sweepCapsule_ConvexCoreGeom, sweepCapsule_ConvexGeom, sweepCapsule_InvalidGeom, sweepCapsule_InvalidGeom, sweepCapsule_MeshGeom, sweepCapsule_HeightFieldGeom, sweepCapsule_CustomGeom }, { sweepCapsule_SphereGeom, sweepCapsule_PlaneGeom, sweepCapsule_CapsuleGeom, sweepCapsule_BoxGeom_Precise, sweepCapsule_ConvexCoreGeom, sweepCapsule_ConvexGeom, sweepCapsule_InvalidGeom, sweepCapsule_InvalidGeom, sweepCapsule_MeshGeom , sweepCapsule_HeightFieldGeom, sweepCapsule_CustomGeom }, { sweepBox_SphereGeom, sweepBox_PlaneGeom, sweepBox_CapsuleGeom, sweepBox_BoxGeom, sweepBox_ConvexCoreGeom, sweepBox_ConvexGeom, sweepBox_InvalidGeom, sweepBox_InvalidGeom, sweepBox_MeshGeom, sweepBox_HeightFieldGeom, sweepBox_CustomGeom }, { sweepBox_SphereGeom_Precise, sweepBox_PlaneGeom, sweepBox_CapsuleGeom_Precise, sweepBox_BoxGeom_Precise, sweepBox_ConvexCoreGeom, sweepBox_ConvexGeom, sweepBox_InvalidGeom, sweepBox_InvalidGeom, sweepBox_MeshGeom, sweepBox_HeightFieldGeom_Precise, sweepBox_CustomGeom }, { sweepConvexCore_Convex, // 0 sweepConvexCore_Plane, // 1 sweepConvexCore_Convex, // 2 sweepConvexCore_Convex, // 3 sweepConvexCore_ConvexCore, // 4 sweepConvexCore_Convex, // 5 sweepConvexCore_InvalidGeom, // 6 sweepConvexCore_InvalidGeom, // 7 sweepConvexCore_MeshGeom, // 8 sweepConvexCore_HeightFieldGeom,// 9 sweepConvexCore_CustomGeom // 10 }, { sweepConvex_SphereGeom, // 0 sweepConvex_PlaneGeom, // 1 sweepConvex_CapsuleGeom, // 2 sweepConvex_BoxGeom, // 3 sweepConvex_ConvexCoreGeom, // 4 sweepConvex_ConvexGeom, // 5 sweepConvex_InvalidGeom, // 6 sweepConvex_InvalidGeom, // 7 sweepConvex_MeshGeom, // 8 sweepConvex_HeightFieldGeom,// 9 sweepConvex_CustomGeom // 10 } }; PX_PHYSX_COMMON_API const GeomSweepFuncs& Gu::getSweepFuncTable() { return gGeomSweepFuncs; }