| 1 | // |
| 2 | // Redistribution and use in source and binary forms, with or without |
| 3 | // modification, are permitted provided that the following conditions |
| 4 | // are met: |
| 5 | // * Redistributions of source code must retain the above copyright |
| 6 | // notice, this list of conditions and the following disclaimer. |
| 7 | // * Redistributions in binary form must reproduce the above copyright |
| 8 | // notice, this list of conditions and the following disclaimer in the |
| 9 | // documentation and/or other materials provided with the distribution. |
| 10 | // * Neither the name of NVIDIA CORPORATION nor the names of its |
| 11 | // contributors may be used to endorse or promote products derived |
| 12 | // from this software without specific prior written permission. |
| 13 | // |
| 14 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY |
| 15 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 17 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| 18 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 19 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 20 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 21 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 22 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 24 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 25 | // |
| 26 | // Copyright (c) 2008-2021 NVIDIA Corporation. All rights reserved. |
| 27 | // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. |
| 28 | // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. |
| 29 | |
| 30 | #include "GuSweepTests.h" |
| 31 | #include "GuHeightFieldUtil.h" |
| 32 | #include "GuEntityReport.h" |
| 33 | #include "GuVecCapsule.h" |
| 34 | #include "GuSweepMTD.h" |
| 35 | #include "GuSweepTriangleUtils.h" |
| 36 | #include "GuVecBox.h" |
| 37 | #include "CmScaling.h" |
| 38 | #include "GuSweepCapsuleTriangle.h" |
| 39 | #include "GuInternal.h" |
| 40 | #include "GuGJKRaycast.h" |
| 41 | |
| 42 | using namespace physx; |
| 43 | using namespace Gu; |
| 44 | using namespace Cm; |
| 45 | using namespace physx::shdfnd::aos; |
| 46 | |
| 47 | #include "GuSweepConvexTri.h" |
| 48 | |
| 49 | #define AbortTraversal false |
| 50 | #define ContinueTraversal true |
| 51 | |
| 52 | /////////////////////////////////////////////////////////////////////////////// |
| 53 | |
| 54 | class HeightFieldTraceSegmentSweepHelper |
| 55 | { |
| 56 | PX_NOCOPY(HeightFieldTraceSegmentSweepHelper) |
| 57 | public: |
| 58 | HeightFieldTraceSegmentSweepHelper(const HeightFieldTraceUtil& hfUtil, const PxVec3& aabbExtentHfLocalSpace) |
| 59 | : mHfUtil(hfUtil), mOverlapObjectExtent(aabbExtentHfLocalSpace) |
| 60 | { |
| 61 | mHfUtil.computeLocalBounds(bounds&: mLocalBounds); |
| 62 | // extend the bounds |
| 63 | mLocalBounds.minimum = mLocalBounds.minimum - aabbExtentHfLocalSpace; |
| 64 | mLocalBounds.maximum = mLocalBounds.maximum + aabbExtentHfLocalSpace; |
| 65 | } |
| 66 | |
| 67 | template<class T> |
| 68 | PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& rayDirNorm, const float rayLength, T* aCallback) const |
| 69 | { |
| 70 | mHfUtil.traceSegment<T, false, true>(aP0, rayDirNorm, rayLength, aCallback, mLocalBounds, false, &mOverlapObjectExtent); |
| 71 | } |
| 72 | |
| 73 | private: |
| 74 | const HeightFieldTraceUtil& mHfUtil; |
| 75 | const PxVec3& mOverlapObjectExtent; |
| 76 | PxBounds3 mLocalBounds; |
| 77 | }; |
| 78 | |
| 79 | /////////////////////////////////////////////////////////////////////////////// |
| 80 | |
| 81 | class HeightFieldTraceSegmentReport : public EntityReport<PxU32> |
| 82 | { |
| 83 | PX_NOCOPY(HeightFieldTraceSegmentReport) |
| 84 | public: |
| 85 | |
| 86 | HeightFieldTraceSegmentReport(const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags) : |
| 87 | mHfUtil (hfUtil), |
| 88 | mHitFlags (hitFlags), |
| 89 | mStatus (false), |
| 90 | mInitialOverlap (false), |
| 91 | mIsDoubleSided ((hfUtil.getHeightFieldGeometry().heightFieldFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES)), |
| 92 | mIsAnyHit (hitFlags & PxHitFlag::eMESH_ANY) |
| 93 | { |
| 94 | } |
| 95 | |
| 96 | bool underFaceHit(const Gu::HeightFieldUtil&, const PxVec3&, const PxVec3&, PxF32, PxF32, PxF32, PxU32) |
| 97 | { |
| 98 | return true; |
| 99 | } |
| 100 | |
| 101 | bool faceHit(const Gu::HeightFieldUtil&, const PxVec3&, PxU32, PxReal, PxReal) |
| 102 | { |
| 103 | return true; |
| 104 | } |
| 105 | |
| 106 | protected: |
| 107 | const HeightFieldUtil& mHfUtil; |
| 108 | const PxHitFlags mHitFlags; |
| 109 | bool mStatus; |
| 110 | bool mInitialOverlap; |
| 111 | const bool mIsDoubleSided; |
| 112 | const bool mIsAnyHit; |
| 113 | }; |
| 114 | |
| 115 | /////////////////////////////////////////////////////////////////////////////// |
| 116 | |
| 117 | class CapsuleTraceSegmentReport : public HeightFieldTraceSegmentReport |
| 118 | { |
| 119 | PX_NOCOPY(CapsuleTraceSegmentReport) |
| 120 | public: |
| 121 | CapsuleTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags, |
| 122 | const Capsule& inflatedCapsule, |
| 123 | const PxVec3& unitDir, PxSweepHit& sweepHit, const PxTransform& pose, PxReal distance) : |
| 124 | HeightFieldTraceSegmentReport (hfUtil, hitFlags), |
| 125 | mInflatedCapsule (inflatedCapsule), |
| 126 | mUnitDir (unitDir), |
| 127 | mSweepHit (sweepHit), |
| 128 | mPose (pose), |
| 129 | mDistance (distance) |
| 130 | { |
| 131 | mSweepHit.faceIndex = 0xFFFFffff; |
| 132 | } |
| 133 | |
| 134 | virtual PxAgain onEvent(PxU32 nb, PxU32* indices) |
| 135 | { |
| 136 | PX_ALIGN_PREFIX(16) PxU8 tribuf[HF_SWEEP_REPORT_BUFFER_SIZE*sizeof(PxTriangle)] PX_ALIGN_SUFFIX(16); |
| 137 | PxTriangle* tmpT = reinterpret_cast<PxTriangle*>(tribuf); |
| 138 | PX_ASSERT(nb <= HF_SWEEP_REPORT_BUFFER_SIZE); |
| 139 | for(PxU32 i=0; i<nb; i++) |
| 140 | { |
| 141 | const PxU32 triangleIndex = indices[i]; |
| 142 | mHfUtil.getTriangle(mPose, worldTri&: tmpT[i], NULL, NULL, triangleIndex, worldSpaceTranslation: true); |
| 143 | } |
| 144 | |
| 145 | PxSweepHit h; // PT: TODO: ctor! |
| 146 | // PT: this one is safe because cullbox is NULL (no need to allocate one more triangle) |
| 147 | // PT: TODO: is it ok to pass the initial distance here? |
| 148 | PxVec3 bestNormal; |
| 149 | const bool status = sweepCapsuleTriangles_Precise(nbTris: nb, triangles: tmpT, capsule: mInflatedCapsule, unitDir: mUnitDir, distance: mDistance, NULL, hit&: h, triNormalOut&: bestNormal, hitFlags: mHitFlags, isDoubleSided: mIsDoubleSided); |
| 150 | if(status && (h.distance <= mSweepHit.distance)) |
| 151 | { |
| 152 | mSweepHit.faceIndex = indices[h.faceIndex]; |
| 153 | mSweepHit.normal = h.normal; |
| 154 | mSweepHit.position = h.position; |
| 155 | mSweepHit.distance = h.distance; |
| 156 | |
| 157 | mStatus = true; |
| 158 | if(h.distance == 0.0f) |
| 159 | { |
| 160 | mInitialOverlap = true; |
| 161 | return AbortTraversal; |
| 162 | } |
| 163 | |
| 164 | if(mIsAnyHit) |
| 165 | return AbortTraversal; |
| 166 | } |
| 167 | return ContinueTraversal; |
| 168 | } |
| 169 | |
| 170 | bool finalizeHit(PxSweepHit& sweepHit, const PxHeightFieldGeometry& hfGeom, const PxTransform& pose, const Capsule& lss, const Capsule& inflatedCapsule, const PxVec3& unitDir) |
| 171 | { |
| 172 | if(!mStatus) |
| 173 | return false; |
| 174 | |
| 175 | if(mInitialOverlap) |
| 176 | { |
| 177 | // PT: TODO: consider using 'setInitialOverlapResults' here |
| 178 | sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; |
| 179 | |
| 180 | if(mHitFlags & PxHitFlag::eMTD) |
| 181 | { |
| 182 | const Vec3V p0 = V3LoadU(f: lss.p0); |
| 183 | const Vec3V p1 = V3LoadU(f: lss.p1); |
| 184 | const FloatV radius = FLoad(f: lss.radius); |
| 185 | CapsuleV capsuleV; |
| 186 | capsuleV.initialize(p0: p0, p1: p1, radius: radius); |
| 187 | |
| 188 | //calculate MTD |
| 189 | const bool hasContacts = computeCapsule_HeightFieldMTD(heightFieldGeom: hfGeom, pose, capsuleV, inflatedRadius: inflatedCapsule.radius, isDoubleSided: mIsDoubleSided, flags: GuHfQueryFlags::eWORLD_SPACE, hit&: sweepHit); |
| 190 | |
| 191 | //ML: the center of mass is below the surface, we won't have MTD contact generate |
| 192 | if(!hasContacts) |
| 193 | { |
| 194 | sweepHit.distance = 0.0f; |
| 195 | sweepHit.normal = -unitDir; |
| 196 | } |
| 197 | else |
| 198 | { |
| 199 | sweepHit.flags |= PxHitFlag::ePOSITION; |
| 200 | } |
| 201 | } |
| 202 | else |
| 203 | { |
| 204 | sweepHit.distance = 0.0f; |
| 205 | sweepHit.normal = -unitDir; |
| 206 | } |
| 207 | } |
| 208 | else |
| 209 | { |
| 210 | sweepHit.flags = PxHitFlag::eNORMAL| PxHitFlag::ePOSITION | PxHitFlag::eFACE_INDEX; |
| 211 | } |
| 212 | return true; |
| 213 | } |
| 214 | |
| 215 | private: |
| 216 | const Capsule& mInflatedCapsule; |
| 217 | const PxVec3& mUnitDir; |
| 218 | PxSweepHit& mSweepHit; |
| 219 | const PxTransform& mPose; |
| 220 | const PxReal mDistance; |
| 221 | }; |
| 222 | |
| 223 | bool sweepCapsule_HeightFieldGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) |
| 224 | { |
| 225 | PX_UNUSED(capsuleGeom_); |
| 226 | PX_UNUSED(capsulePose_); |
| 227 | |
| 228 | PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); |
| 229 | const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom); |
| 230 | |
| 231 | const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation); |
| 232 | |
| 233 | // Compute swept box |
| 234 | Box capsuleBox; |
| 235 | computeBoxAroundCapsule(capsule: inflatedCapsule, box&: capsuleBox); |
| 236 | |
| 237 | const PxVec3 capsuleAABBExtents = capsuleBox.computeAABBExtent(); |
| 238 | |
| 239 | const HeightFieldTraceUtil hfUtil(hfGeom); |
| 240 | CapsuleTraceSegmentReport myReport(hfUtil, hitFlags, inflatedCapsule, unitDir, sweepHit, pose, distance); |
| 241 | |
| 242 | sweepHit.distance = PX_MAX_F32; |
| 243 | |
| 244 | // need hf local space stuff |
| 245 | const PxTransform inversePose = pose.getInverse(); |
| 246 | const PxVec3 centerLocalSpace = inversePose.transform(input: capsuleBox.center); |
| 247 | const PxVec3 sweepDirLocalSpace = inversePose.rotate(input: unitDir); |
| 248 | const PxVec3 capsuleAABBBExtentHfLocalSpace = PxBounds3::basisExtent(center: centerLocalSpace, basis: PxMat33Padded(inversePose.q), extent: capsuleAABBExtents).getExtents(); |
| 249 | |
| 250 | HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, capsuleAABBBExtentHfLocalSpace); |
| 251 | traceSegmentHelper.traceSegment<CapsuleTraceSegmentReport>(aP0: centerLocalSpace, rayDirNorm: sweepDirLocalSpace, rayLength: distance, aCallback: &myReport); |
| 252 | |
| 253 | return myReport.finalizeHit(sweepHit, hfGeom, pose, lss, inflatedCapsule, unitDir); |
| 254 | } |
| 255 | |
| 256 | /////////////////////////////////////////////////////////////////////////////// |
| 257 | |
| 258 | class : public HeightFieldTraceSegmentReport |
| 259 | { |
| 260 | PX_NOCOPY() |
| 261 | public: |
| 262 | ( const HeightFieldUtil& hfUtil, const ConvexHullData& hull, const PxMeshScale& convexScale, |
| 263 | const PxTransform& convexPose, const PxTransform& heightFieldPose, |
| 264 | const PxVec3& unitDir, PxReal distance, PxHitFlags hitFlags, PxReal inflation) : |
| 265 | HeightFieldTraceSegmentReport (hfUtil, hitFlags), |
| 266 | mUnitDir (unitDir), |
| 267 | mInflation (inflation) |
| 268 | { |
| 269 | using namespace Ps::aos; |
| 270 | mSweepHit.faceIndex = 0xFFFFffff; |
| 271 | mSweepHit.distance = distance; |
| 272 | const Vec3V worldDir = V3LoadU(f: unitDir); |
| 273 | const FloatV dist = FLoad(f: distance); |
| 274 | const QuatV q0 = QuatVLoadU(v: &heightFieldPose.q.x); |
| 275 | const Vec3V p0 = V3LoadU(i: &heightFieldPose.p.x); |
| 276 | |
| 277 | const QuatV q1 = QuatVLoadU(v: &convexPose.q.x); |
| 278 | const Vec3V p1 = V3LoadU(i: &convexPose.p.x); |
| 279 | |
| 280 | const PsTransformV meshTransf(p0, q0); |
| 281 | const PsTransformV (p1, q1); |
| 282 | |
| 283 | mMeshToConvex = convexTransf.transformInv(src: meshTransf); |
| 284 | mConvexPoseV = convexTransf; |
| 285 | mConvexSpaceDir = convexTransf.rotateInv(input: V3Neg(f: V3Scale(a: worldDir, b: dist))); |
| 286 | mDistance = dist; |
| 287 | |
| 288 | const Vec3V vScale = V3LoadU_SafeReadW(f: convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale |
| 289 | const QuatV vQuat = QuatVLoadU(v: &convexScale.rotation.x); |
| 290 | |
| 291 | mMeshSpaceUnitDir = heightFieldPose.rotateInv(input: unitDir); |
| 292 | mConvexHull.initialize(hullData: &hull, center: V3Zero(), scale: vScale, scaleRot: vQuat, idtScale: convexScale.isIdentity()); |
| 293 | } |
| 294 | |
| 295 | virtual PxAgain (PxU32 nbEntities, PxU32* entities) |
| 296 | { |
| 297 | const PxTransform idt(PxIdentity); |
| 298 | for(PxU32 i=0; i<nbEntities; i++) |
| 299 | { |
| 300 | PxTriangle tri; |
| 301 | mHfUtil.getTriangle(idt, worldTri&: tri, NULL, NULL, triangleIndex: entities[i], worldSpaceTranslation: false, worldSpaceRotation: false); // First parameter not needed if local space triangle is enough |
| 302 | |
| 303 | // use mSweepHit.distance as max sweep distance so far, mSweepHit.distance will be clipped by this function |
| 304 | if(sweepConvexVsTriangle( v0: tri.verts[0], v1: tri.verts[1], v2: tri.verts[2], convexHull&: mConvexHull, meshToConvex: mMeshToConvex, convexTransfV: mConvexPoseV, |
| 305 | convexSpaceDir: mConvexSpaceDir, unitDir: mUnitDir, meshSpaceUnitDir: mMeshSpaceUnitDir, fullDistance: mDistance, shrunkDistance: mSweepHit.distance, hit&: mSweepHit, isDoubleSided: mIsDoubleSided, |
| 306 | inflation: mInflation, initialOverlap&: mInitialOverlap, faceIndex: entities[i])) |
| 307 | { |
| 308 | mStatus = true; |
| 309 | if(mIsAnyHit || mSweepHit.distance == 0.0f) |
| 310 | return AbortTraversal; |
| 311 | } |
| 312 | } |
| 313 | return ContinueTraversal; |
| 314 | } |
| 315 | |
| 316 | bool (PxSweepHit& sweepHit, |
| 317 | const PxHeightFieldGeometry& hfGeom, const PxTransform& pose, |
| 318 | const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, |
| 319 | const PxVec3& unitDir, PxReal inflation) |
| 320 | { |
| 321 | if(!mStatus) |
| 322 | return false; |
| 323 | |
| 324 | if(mInitialOverlap) |
| 325 | { |
| 326 | if(mHitFlags & PxHitFlag::eMTD) |
| 327 | { |
| 328 | const bool hasContacts = computeConvex_HeightFieldMTD(heightFieldGeom: hfGeom, pose, convexGeom, convexTransform: convexPose, inflation, isDoubleSided: mIsDoubleSided, flags: GuHfQueryFlags::eWORLD_SPACE, hit&: sweepHit); |
| 329 | |
| 330 | sweepHit.faceIndex = mSweepHit.faceIndex; |
| 331 | sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; |
| 332 | if(!hasContacts) |
| 333 | { |
| 334 | sweepHit.distance = 0.0f; |
| 335 | sweepHit.normal = -unitDir; |
| 336 | } |
| 337 | else |
| 338 | { |
| 339 | sweepHit.flags |= PxHitFlag::ePOSITION; |
| 340 | } |
| 341 | } |
| 342 | else |
| 343 | { |
| 344 | setInitialOverlapResults(hit&: sweepHit, unitDir, faceIndex: mSweepHit.faceIndex); // hit index must be set to closest for IO |
| 345 | } |
| 346 | } |
| 347 | else |
| 348 | { |
| 349 | sweepHit = mSweepHit; |
| 350 | sweepHit.normal = -sweepHit.normal; |
| 351 | sweepHit.normal.normalize(); |
| 352 | } |
| 353 | return true; |
| 354 | } |
| 355 | |
| 356 | private: |
| 357 | PsMatTransformV ; |
| 358 | PsTransformV ; |
| 359 | ConvexHullV ; |
| 360 | PxSweepHit ; |
| 361 | Vec3V ; |
| 362 | FloatV ; |
| 363 | const PxVec3 ; |
| 364 | PxVec3 ; |
| 365 | const PxReal ; |
| 366 | }; |
| 367 | |
| 368 | bool sweepConvex_HeightFieldGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) |
| 369 | { |
| 370 | PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); |
| 371 | const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom); |
| 372 | |
| 373 | const Matrix34 convexTM(convexPose); |
| 374 | const Matrix34 meshTM(pose); |
| 375 | |
| 376 | ConvexMesh* convexMesh = static_cast<ConvexMesh*>(convexGeom.convexMesh); |
| 377 | |
| 378 | const bool idtScaleConvex = convexGeom.scale.isIdentity(); |
| 379 | |
| 380 | FastVertex2ShapeScaling convexScaling; |
| 381 | if(!idtScaleConvex) |
| 382 | convexScaling.init(scale: convexGeom.scale); |
| 383 | |
| 384 | PX_ASSERT(!convexMesh->getLocalBoundsFast().isEmpty()); |
| 385 | const PxBounds3 hullAABBLocalSpace = convexMesh->getLocalBoundsFast().transformFast(matrix: convexScaling.getVertex2ShapeSkew()); |
| 386 | |
| 387 | const HeightFieldTraceUtil hfUtil(hfGeom); |
| 388 | ConvexTraceSegmentReport entityReport( |
| 389 | hfUtil, convexMesh->getHull(), convexGeom.scale, convexPose, pose, -unitDir, distance, hitFlags, inflation); |
| 390 | |
| 391 | // need hf local space stuff |
| 392 | const PxBounds3 hullAABB = PxBounds3::transformFast(transform: convexPose, bounds: hullAABBLocalSpace); |
| 393 | const PxVec3 aabbExtents = hullAABB.getExtents() + PxVec3(inflation); |
| 394 | const PxTransform inversePose = pose.getInverse(); |
| 395 | const PxVec3 centerLocalSpace = inversePose.transform(input: hullAABB.getCenter()); |
| 396 | const PxVec3 sweepDirLocalSpace = inversePose.rotate(input: unitDir); |
| 397 | const PxVec3 convexAABBExtentHfLocalSpace = PxBounds3::basisExtent(center: centerLocalSpace, basis: PxMat33Padded(inversePose.q), extent: aabbExtents).getExtents(); |
| 398 | |
| 399 | HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, convexAABBExtentHfLocalSpace); |
| 400 | traceSegmentHelper.traceSegment<ConvexTraceSegmentReport>(aP0: centerLocalSpace, rayDirNorm: sweepDirLocalSpace, rayLength: distance, aCallback: &entityReport); |
| 401 | |
| 402 | return entityReport.finalizeHit(sweepHit, hfGeom, pose, convexGeom, convexPose, unitDir, inflation); |
| 403 | } |
| 404 | |
| 405 | /////////////////////////////////////////////////////////////////////////////// |
| 406 | |
| 407 | #if PX_VC |
| 408 | #pragma warning(push) |
| 409 | #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. |
| 410 | #endif |
| 411 | |
| 412 | class BoxTraceSegmentReport : public HeightFieldTraceSegmentReport |
| 413 | { |
| 414 | PX_NOCOPY(BoxTraceSegmentReport) |
| 415 | public: |
| 416 | BoxTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags, |
| 417 | const PsTransformV& worldToBoxV, const PxTransform& pose, const BoxV& box, const PxVec3& localMotion, |
| 418 | PxSweepHit& sweepHit, PxReal inflation) : |
| 419 | HeightFieldTraceSegmentReport (hfUtil, hitFlags), |
| 420 | mWorldToBoxV (worldToBoxV), |
| 421 | mPose (pose), |
| 422 | mBox (box), |
| 423 | mLocalMotion (localMotion), |
| 424 | mSweepHit (sweepHit), |
| 425 | mInflation (inflation) |
| 426 | { |
| 427 | mMinToi = FMax(); |
| 428 | mSweepHit.faceIndex = 0xFFFFffff; |
| 429 | } |
| 430 | |
| 431 | virtual PxAgain onEvent(PxU32 nb, PxU32* indices) |
| 432 | { |
| 433 | const FloatV zero = FZero(); |
| 434 | const Vec3V zeroV = V3Zero(); |
| 435 | const Vec3V dir = V3LoadU(f: mLocalMotion); |
| 436 | //FloatV minToi = FMax(); |
| 437 | FloatV toi; |
| 438 | Vec3V closestA, normal;//closestA and normal is in the local space of box |
| 439 | |
| 440 | for(PxU32 i=0; i<nb; i++) |
| 441 | { |
| 442 | const PxU32 triangleIndex = indices[i]; |
| 443 | |
| 444 | PxTriangle currentTriangle; // in world space |
| 445 | mHfUtil.getTriangle(mPose, worldTri&: currentTriangle, NULL, NULL, triangleIndex, worldSpaceTranslation: true, worldSpaceRotation: true); |
| 446 | |
| 447 | const Vec3V localV0 = V3LoadU(f: currentTriangle.verts[0]); |
| 448 | const Vec3V localV1 = V3LoadU(f: currentTriangle.verts[1]); |
| 449 | const Vec3V localV2 = V3LoadU(f: currentTriangle.verts[2]); |
| 450 | |
| 451 | const Vec3V triV0 = mWorldToBoxV.transform(input: localV0); |
| 452 | const Vec3V triV1 = mWorldToBoxV.transform(input: localV1); |
| 453 | const Vec3V triV2 = mWorldToBoxV.transform(input: localV2); |
| 454 | |
| 455 | if(!mIsDoubleSided) |
| 456 | { |
| 457 | const Vec3V triNormal = V3Cross(a: V3Sub(a: triV2, b: triV1),b: V3Sub(a: triV0, b: triV1)); |
| 458 | if(FAllGrtrOrEq(a: V3Dot(a: triNormal, b: dir), b: zero)) |
| 459 | continue; |
| 460 | } |
| 461 | |
| 462 | const TriangleV triangle(triV0, triV1, triV2); |
| 463 | |
| 464 | ////move triangle to box space |
| 465 | //const Vec3V localV0 = Vec3V_From_PxVec3(WorldToBox.transform(currentTriangle.verts[0])); |
| 466 | //const Vec3V localV1 = Vec3V_From_PxVec3(WorldToBox.transform(currentTriangle.verts[1])); |
| 467 | //const Vec3V localV2 = Vec3V_From_PxVec3(WorldToBox.transform(currentTriangle.verts[2])); |
| 468 | |
| 469 | //TriangleV triangle(localV0, localV1, localV2); |
| 470 | const LocalConvex<TriangleV> convexA(triangle); |
| 471 | const LocalConvex<BoxV> convexB(mBox); |
| 472 | const Vec3V initialSearchDir = V3Sub(a: triangle.getCenter(), b: mBox.getCenter()); |
| 473 | |
| 474 | if(gjkRaycastPenetration< LocalConvex<TriangleV>, LocalConvex<BoxV> >(a: convexA, b: convexB, initialDir: initialSearchDir, initialLambda: zero, s: zeroV, r: dir, lambda&: toi, normal, closestA, inflation: mInflation, initialOverlap: false)) |
| 475 | { |
| 476 | mStatus = true; |
| 477 | if(FAllGrtr(a: toi, b: zero)) |
| 478 | { |
| 479 | if(FAllGrtr(a: mMinToi, b: toi)) |
| 480 | { |
| 481 | mMinToi = toi; |
| 482 | FStore(a: toi, f: &mSweepHit.distance); |
| 483 | V3StoreU(a: normal, f&: mSweepHit.normal); |
| 484 | V3StoreU(a: closestA, f&: mSweepHit.position); |
| 485 | mSweepHit.faceIndex = triangleIndex; |
| 486 | |
| 487 | if(mIsAnyHit) |
| 488 | return AbortTraversal; |
| 489 | } |
| 490 | } |
| 491 | else |
| 492 | { |
| 493 | mSweepHit.distance = 0.0f; |
| 494 | mSweepHit.faceIndex = triangleIndex; |
| 495 | mInitialOverlap = true; |
| 496 | return AbortTraversal; |
| 497 | } |
| 498 | } |
| 499 | } |
| 500 | return ContinueTraversal; |
| 501 | } |
| 502 | |
| 503 | bool finalizeHit(PxSweepHit& sweepHit, |
| 504 | const PxHeightFieldGeometry& hfGeom, const PxTransform& pose, |
| 505 | const PxTransform& boxPose_, const Box& box, |
| 506 | const PxVec3& unitDir, PxReal distance, PxReal inflation) |
| 507 | { |
| 508 | if(!mStatus) |
| 509 | return false; |
| 510 | |
| 511 | if(mInitialOverlap) |
| 512 | { |
| 513 | // PT: TODO: consider using 'setInitialOverlapResults' here |
| 514 | |
| 515 | sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; |
| 516 | |
| 517 | if(mHitFlags & PxHitFlag::eMTD) |
| 518 | { |
| 519 | const bool hasContacts = computeBox_HeightFieldMTD(heightFieldGeom: hfGeom, pose, box, boxTransform: boxPose_, inflation, isDoubleSided: mIsDoubleSided, flags: GuHfQueryFlags::eWORLD_SPACE, hit&: sweepHit); |
| 520 | |
| 521 | //ML: the center of mass is below the surface, we won't have MTD contact generate |
| 522 | if(!hasContacts) |
| 523 | { |
| 524 | sweepHit.distance = 0.0f; |
| 525 | sweepHit.normal = -unitDir; |
| 526 | } |
| 527 | else |
| 528 | { |
| 529 | sweepHit.flags |= PxHitFlag::ePOSITION; |
| 530 | } |
| 531 | } |
| 532 | else |
| 533 | { |
| 534 | sweepHit.distance = 0.0f; |
| 535 | sweepHit.normal = -unitDir; |
| 536 | } |
| 537 | } |
| 538 | else |
| 539 | { |
| 540 | PxVec3 n = sweepHit.normal.getNormalized(); |
| 541 | if((n.dot(v: mLocalMotion))>0.0f) |
| 542 | n = -n; |
| 543 | |
| 544 | sweepHit.distance *= distance; // stored as toi [0,1] during computation -> scale |
| 545 | sweepHit.normal = boxPose_.rotate(input: n); |
| 546 | sweepHit.position = boxPose_.transform(input: sweepHit.position); |
| 547 | sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; |
| 548 | } |
| 549 | return true; |
| 550 | } |
| 551 | |
| 552 | private: |
| 553 | const PsTransformV& mWorldToBoxV; |
| 554 | const PxTransform& mPose; |
| 555 | const BoxV& mBox; |
| 556 | FloatV mMinToi; |
| 557 | const PxVec3 mLocalMotion; |
| 558 | PxSweepHit& mSweepHit; |
| 559 | const PxReal mInflation; |
| 560 | }; |
| 561 | |
| 562 | #if PX_VC |
| 563 | #pragma warning(pop) |
| 564 | #endif |
| 565 | |
| 566 | bool sweepBox_HeightFieldGeom(GU_BOX_SWEEP_FUNC_PARAMS) |
| 567 | { |
| 568 | PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); |
| 569 | PX_UNUSED(boxGeom_); |
| 570 | PX_UNUSED(hitFlags); |
| 571 | |
| 572 | const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom); |
| 573 | |
| 574 | const PxVec3 boxAABBExtent = box.computeAABBExtent() + PxVec3(inflation); |
| 575 | |
| 576 | // Move to AABB space |
| 577 | PX_ALIGN_PREFIX(16) PxTransform WorldToBox PX_ALIGN_SUFFIX(16); |
| 578 | WorldToBox = boxPose_.getInverse(); |
| 579 | |
| 580 | const QuatV q1 = QuatVLoadA(v: &WorldToBox.q.x); |
| 581 | const Vec3V p1 = V3LoadA(f: &WorldToBox.p.x); |
| 582 | const PsTransformV WorldToBoxV(p1, q1); |
| 583 | |
| 584 | const PxVec3 motion = unitDir * distance; |
| 585 | const PxVec3 localMotion = WorldToBox.rotate(input: motion); |
| 586 | |
| 587 | const BoxV boxV(V3Zero(), V3LoadU(f: box.extents)); |
| 588 | |
| 589 | sweepHit.distance = PX_MAX_F32; |
| 590 | |
| 591 | const HeightFieldTraceUtil hfUtil(hfGeom); |
| 592 | BoxTraceSegmentReport myReport(hfUtil, hitFlags, WorldToBoxV, pose, boxV, localMotion, sweepHit, inflation); |
| 593 | |
| 594 | // need hf local space stuff |
| 595 | const PxTransform inversePose = pose.getInverse(); |
| 596 | const PxVec3 centerLocalSpace = inversePose.transform(input: box.center); |
| 597 | const PxVec3 sweepDirLocalSpace = inversePose.rotate(input: unitDir); |
| 598 | const PxVec3 boxAABBExtentInHfLocalSpace = PxBounds3::basisExtent(center: centerLocalSpace, basis: PxMat33Padded(inversePose.q), extent: boxAABBExtent).getExtents(); |
| 599 | |
| 600 | HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, boxAABBExtentInHfLocalSpace); |
| 601 | traceSegmentHelper.traceSegment<BoxTraceSegmentReport>(aP0: centerLocalSpace, rayDirNorm: sweepDirLocalSpace, rayLength: distance, aCallback: &myReport); |
| 602 | |
| 603 | return myReport.finalizeHit(sweepHit, hfGeom, pose, boxPose_, box, unitDir, distance, inflation); |
| 604 | } |
| 605 | |
| 606 | /////////////////////////////////////////////////////////////////////////////// |
| 607 | |