// // 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-2018 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 "GuSweepTests.h" #include "GuHeightFieldUtil.h" #include "GuEntityReport.h" #include "GuVecCapsule.h" #include "GuSweepMTD.h" #include "GuSweepTriangleUtils.h" #include "GuVecBox.h" #include "GuVecShrunkBox.h" #include "CmScaling.h" #include "GuSweepCapsuleTriangle.h" #include "GuInternal.h" #include "GuGJKRaycast.h" using namespace physx; using namespace Gu; using namespace Cm; using namespace physx::shdfnd::aos; #include "GuSweepConvexTri.h" #define AbortTraversal false #define ContinueTraversal true /////////////////////////////////////////////////////////////////////////////// class HeightFieldTraceSegmentSweepHelper { PX_NOCOPY(HeightFieldTraceSegmentSweepHelper) public: HeightFieldTraceSegmentSweepHelper(const HeightFieldTraceUtil& hfUtil, const PxVec3& aabbExtentHfLocalSpace) : mHfUtil(hfUtil), mOverlapObjectExtent(aabbExtentHfLocalSpace) { mHfUtil.computeLocalBounds(mLocalBounds); // extend the bounds mLocalBounds.minimum = mLocalBounds.minimum - aabbExtentHfLocalSpace; mLocalBounds.maximum = mLocalBounds.maximum + aabbExtentHfLocalSpace; } template PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& rayDirNorm, const float rayLength, T* aCallback) const { mHfUtil.traceSegment(aP0, rayDirNorm, rayLength, aCallback, mLocalBounds, false, &mOverlapObjectExtent); } private: const HeightFieldTraceUtil& mHfUtil; const PxVec3& mOverlapObjectExtent; PxBounds3 mLocalBounds; }; /////////////////////////////////////////////////////////////////////////////// class HeightFieldTraceSegmentReport : public EntityReport { PX_NOCOPY(HeightFieldTraceSegmentReport) public: HeightFieldTraceSegmentReport(const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags) : mHfUtil (hfUtil), mHitFlags (hitFlags), mStatus (false), mInitialOverlap (false), mIsDoubleSided ((hfUtil.getHeightFieldGeometry().heightFieldFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES)), mIsAnyHit (hitFlags & PxHitFlag::eMESH_ANY) { } bool underFaceHit(const Gu::HeightFieldUtil&, const PxVec3&, const PxVec3&, PxF32, PxF32, PxF32, PxU32) { return true; } bool faceHit(const Gu::HeightFieldUtil&, const PxVec3&, PxU32, PxReal, PxReal) { return true; } protected: const HeightFieldUtil& mHfUtil; const PxHitFlags mHitFlags; bool mStatus; bool mInitialOverlap; const bool mIsDoubleSided; const bool mIsAnyHit; }; /////////////////////////////////////////////////////////////////////////////// class CapsuleTraceSegmentReport : public HeightFieldTraceSegmentReport { PX_NOCOPY(CapsuleTraceSegmentReport) public: CapsuleTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags, const Capsule& inflatedCapsule, const PxVec3& unitDir, PxSweepHit& sweepHit, const PxTransform& pose, PxReal distance) : HeightFieldTraceSegmentReport (hfUtil, hitFlags), mInflatedCapsule (inflatedCapsule), mUnitDir (unitDir), mSweepHit (sweepHit), mPose (pose), mDistance (distance) { mSweepHit.faceIndex = 0xFFFFffff; } virtual PxAgain onEvent(PxU32 nb, PxU32* indices) { PX_ALIGN_PREFIX(16) PxU8 tribuf[HF_SWEEP_REPORT_BUFFER_SIZE*sizeof(PxTriangle)] PX_ALIGN_SUFFIX(16); PxTriangle* tmpT = reinterpret_cast(tribuf); PX_ASSERT(nb <= HF_SWEEP_REPORT_BUFFER_SIZE); for(PxU32 i=0; i(geom); const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation); // Compute swept box Box capsuleBox; computeBoxAroundCapsule(inflatedCapsule, capsuleBox); const PxVec3 capsuleAABBExtents = capsuleBox.computeAABBExtent(); const HeightFieldTraceUtil hfUtil(hfGeom); CapsuleTraceSegmentReport myReport(hfUtil, hitFlags, inflatedCapsule, unitDir, sweepHit, pose, distance); sweepHit.distance = PX_MAX_F32; // need hf local space stuff const PxTransform inversePose = pose.getInverse(); const PxVec3 centerLocalSpace = inversePose.transform(capsuleBox.center); const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); const PxVec3 capsuleAABBBExtentHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), capsuleAABBExtents).getExtents(); HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, capsuleAABBBExtentHfLocalSpace); traceSegmentHelper.traceSegment(centerLocalSpace, sweepDirLocalSpace, distance, &myReport); return myReport.finalizeHit(sweepHit, hfGeom, pose, lss, inflatedCapsule, unitDir); } /////////////////////////////////////////////////////////////////////////////// class ConvexTraceSegmentReport : public HeightFieldTraceSegmentReport { PX_NOCOPY(ConvexTraceSegmentReport) public: ConvexTraceSegmentReport( const HeightFieldUtil& hfUtil, const ConvexHullData& hull, const PxMeshScale& convexScale, const PxTransform& convexPose, const PxTransform& heightFieldPose, const PxVec3& unitDir, PxReal distance, PxHitFlags hitFlags, PxReal inflation) : HeightFieldTraceSegmentReport (hfUtil, hitFlags), mUnitDir (unitDir), mInflation (inflation) { using namespace Ps::aos; mSweepHit.faceIndex = 0xFFFFffff; mSweepHit.distance = distance; const Vec3V worldDir = V3LoadU(unitDir); const FloatV dist = FLoad(distance); const QuatV q0 = QuatVLoadU(&heightFieldPose.q.x); const Vec3V p0 = V3LoadU(&heightFieldPose.p.x); const QuatV q1 = QuatVLoadU(&convexPose.q.x); const Vec3V p1 = V3LoadU(&convexPose.p.x); const PsTransformV meshTransf(p0, q0); const PsTransformV convexTransf(p1, q1); mMeshToConvex = convexTransf.transformInv(meshTransf); mConvexPoseV = convexTransf; mConvexSpaceDir = convexTransf.rotateInv(V3Neg(V3Scale(worldDir, dist))); mDistance = dist; const Vec3V vScale = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale const QuatV vQuat = QuatVLoadU(&convexScale.rotation.x); mMeshSpaceUnitDir = heightFieldPose.rotateInv(unitDir); mConvexHull.initialize(&hull, V3Zero(), vScale, vQuat, convexScale.isIdentity()); } virtual PxAgain onEvent(PxU32 nbEntities, PxU32* entities) { const PxTransform idt(PxIdentity); for(PxU32 i=0; i(geom); const Matrix34 convexTM(convexPose); const Matrix34 meshTM(pose); ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); const bool idtScaleConvex = convexGeom.scale.isIdentity(); FastVertex2ShapeScaling convexScaling; if(!idtScaleConvex) convexScaling.init(convexGeom.scale); PX_ASSERT(!convexMesh->getLocalBoundsFast().isEmpty()); const PxBounds3 hullAABBLocalSpace = convexMesh->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew()); const HeightFieldTraceUtil hfUtil(hfGeom); ConvexTraceSegmentReport entityReport( hfUtil, convexMesh->getHull(), convexGeom.scale, convexPose, pose, -unitDir, distance, hitFlags, inflation); // need hf local space stuff const PxBounds3 hullAABB = PxBounds3::transformFast(convexPose, hullAABBLocalSpace); const PxVec3 aabbExtents = hullAABB.getExtents() + PxVec3(inflation); const PxTransform inversePose = pose.getInverse(); const PxVec3 centerLocalSpace = inversePose.transform(hullAABB.getCenter()); const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); const PxVec3 convexAABBExtentHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), aabbExtents).getExtents(); HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, convexAABBExtentHfLocalSpace); traceSegmentHelper.traceSegment(centerLocalSpace, sweepDirLocalSpace, distance, &entityReport); return entityReport.finalizeHit(sweepHit, hfGeom, pose, convexGeom, convexPose, unitDir, inflation); } /////////////////////////////////////////////////////////////////////////////// #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 class BoxTraceSegmentReport : public HeightFieldTraceSegmentReport { PX_NOCOPY(BoxTraceSegmentReport) public: BoxTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags, const PsTransformV& worldToBoxV, const PxTransform& pose, const BoxV& box, const PxVec3& localMotion, PxSweepHit& sweepHit, PxReal inflation) : HeightFieldTraceSegmentReport (hfUtil, hitFlags), mWorldToBoxV (worldToBoxV), mPose (pose), mBox (box), mLocalMotion (localMotion), mSweepHit (sweepHit), mInflation (inflation) { mMinToi = FMax(); mSweepHit.faceIndex = 0xFFFFffff; } virtual PxAgain onEvent(PxU32 nb, PxU32* indices) { const FloatV zero = FZero(); const Vec3V zeroV = V3Zero(); const Vec3V dir = V3LoadU(mLocalMotion); //FloatV minToi = FMax(); FloatV toi; Vec3V closestA, normal;//closestA and normal is in the local space of box for(PxU32 i=0; i convexA(triangle); const LocalConvex convexB(mBox); const Vec3V initialSearchDir = V3Sub(triangle.getCenter(), mBox.getCenter()); if(gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, mInflation, false)) { mStatus = true; if(FAllGrtr(toi, zero)) { if(FAllGrtr(mMinToi, toi)) { mMinToi = toi; FStore(toi, &mSweepHit.distance); V3StoreU(normal, mSweepHit.normal); V3StoreU(closestA, mSweepHit.position); mSweepHit.faceIndex = triangleIndex; if(mIsAnyHit) return AbortTraversal; } } else { mSweepHit.distance = 0.0f; mSweepHit.faceIndex = triangleIndex; mInitialOverlap = true; return AbortTraversal; } } } return ContinueTraversal; } bool finalizeHit(PxSweepHit& sweepHit, const PxHeightFieldGeometry& hfGeom, const PxTransform& pose, const PxTransform& boxPose_, const Box& box, const PxVec3& unitDir, PxReal distance, PxReal inflation) { if(!mStatus) return false; if(mInitialOverlap) { // PT: TODO: consider using 'setInitialOverlapResults' here sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; if(mHitFlags & PxHitFlag::eMTD) { const bool hasContacts = computeBox_HeightFieldMTD(hfGeom, pose, box, boxPose_, inflation, mIsDoubleSided, GuHfQueryFlags::eWORLD_SPACE, sweepHit); //ML: the center of mass is below the surface, we won't have MTD contact generate if(!hasContacts) { sweepHit.distance = 0.0f; sweepHit.normal = -unitDir; } else { sweepHit.flags |= PxHitFlag::ePOSITION; } } else { sweepHit.distance = 0.0f; sweepHit.normal = -unitDir; } } else { PxVec3 n = sweepHit.normal.getNormalized(); if((n.dot(mLocalMotion))>0.0f) n = -n; sweepHit.distance *= distance; // stored as toi [0,1] during computation -> scale sweepHit.normal = boxPose_.rotate(n); sweepHit.position = boxPose_.transform(sweepHit.position); sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; } return true; } private: const PsTransformV& mWorldToBoxV; const PxTransform& mPose; const BoxV& mBox; FloatV mMinToi; const PxVec3 mLocalMotion; PxSweepHit& mSweepHit; const PxReal mInflation; }; #if PX_VC #pragma warning(pop) #endif bool sweepBox_HeightFieldGeom(GU_BOX_SWEEP_FUNC_PARAMS) { PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); PX_UNUSED(boxGeom_); PX_UNUSED(hitFlags); const PxHeightFieldGeometry& hfGeom = static_cast(geom); const PxVec3 boxAABBExtent = box.computeAABBExtent() + PxVec3(inflation); // Move to AABB space PX_ALIGN_PREFIX(16) PxTransform WorldToBox PX_ALIGN_SUFFIX(16); WorldToBox = boxPose_.getInverse(); const QuatV q1 = QuatVLoadA(&WorldToBox.q.x); const Vec3V p1 = V3LoadA(&WorldToBox.p.x); const PsTransformV WorldToBoxV(p1, q1); const PxVec3 motion = unitDir * distance; const PxVec3 localMotion = WorldToBox.rotate(motion); const BoxV boxV(V3Zero(), V3LoadU(box.extents)); sweepHit.distance = PX_MAX_F32; const HeightFieldTraceUtil hfUtil(hfGeom); BoxTraceSegmentReport myReport(hfUtil, hitFlags, WorldToBoxV, pose, boxV, localMotion, sweepHit, inflation); // need hf local space stuff const PxTransform inversePose = pose.getInverse(); const PxVec3 centerLocalSpace = inversePose.transform(box.center); const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); const PxVec3 boxAABBExtentInHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), boxAABBExtent).getExtents(); HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, boxAABBExtentInHfLocalSpace); traceSegmentHelper.traceSegment(centerLocalSpace, sweepDirLocalSpace, distance, &myReport); return myReport.finalizeHit(sweepHit, hfGeom, pose, boxPose_, box, unitDir, distance, inflation); } ///////////////////////////////////////////////////////////////////////////////