| 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 | #ifndef GU_GJKRAYCAST_H |
| 31 | #define GU_GJKRAYCAST_H |
| 32 | |
| 33 | #include "GuGJKType.h" |
| 34 | #include "GuGJKSimplex.h" |
| 35 | #include "GuConvexSupportTable.h" |
| 36 | #include "GuGJKPenetration.h" |
| 37 | #include "GuEPA.h" |
| 38 | |
| 39 | |
| 40 | namespace physx |
| 41 | { |
| 42 | |
| 43 | |
| 44 | namespace Gu |
| 45 | { |
| 46 | |
| 47 | /* |
| 48 | ConvexA is in the local space of ConvexB |
| 49 | lambda : the time of impact(TOI) |
| 50 | initialLambda : the start time of impact value (disable) |
| 51 | s : the sweep ray origin |
| 52 | r : the normalized sweep ray direction scaled by the sweep distance. r should be in ConvexB's space |
| 53 | normal : the contact normal |
| 54 | inflation : the amount by which we inflate the swept shape. If the inflated shapes aren't initially-touching, |
| 55 | the TOI will return the time at which both shapes are at a distance equal to inflation separated. If inflation is 0 |
| 56 | the TOI will return the time at which both shapes are touching. |
| 57 | |
| 58 | */ |
| 59 | template<class ConvexA, class ConvexB> |
| 60 | bool gjkRaycast(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg initialDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, Ps::aos::FloatV& lambda, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal _inflation) |
| 61 | { |
| 62 | PX_UNUSED(initialLambda); |
| 63 | |
| 64 | using namespace Ps::aos; |
| 65 | |
| 66 | const FloatV inflation = FLoad(f: _inflation); |
| 67 | const Vec3V zeroV = V3Zero(); |
| 68 | const FloatV zero = FZero(); |
| 69 | const FloatV one = FOne(); |
| 70 | const BoolV bTrue = BTTTT(); |
| 71 | |
| 72 | const FloatV maxDist = FLoad(PX_MAX_REAL); |
| 73 | |
| 74 | FloatV _lambda = zero;//initialLambda; |
| 75 | Vec3V x = V3ScaleAdd(a: r, b: _lambda, c: s); |
| 76 | PxU32 size=1; |
| 77 | |
| 78 | //const Vec3V dir = V3Sub(a.getCenter(), b.getCenter()); |
| 79 | const Vec3V _initialSearchDir = V3Sel(c: FIsGrtr(a: V3Dot(a: initialDir, b: initialDir), b: FEps()), a: initialDir, b: V3UnitX()); |
| 80 | const Vec3V initialSearchDir = V3Normalize(a: _initialSearchDir); |
| 81 | |
| 82 | const Vec3V initialSupportA(a.ConvexA::support(V3Neg(f: initialSearchDir))); |
| 83 | const Vec3V initialSupportB( b.ConvexB::support(initialSearchDir)); |
| 84 | |
| 85 | Vec3V Q[4] = {V3Sub(a: initialSupportA, b: initialSupportB), zeroV, zeroV, zeroV}; //simplex set |
| 86 | Vec3V A[4] = {initialSupportA, zeroV, zeroV, zeroV}; //ConvexHull a simplex set |
| 87 | Vec3V B[4] = {initialSupportB, zeroV, zeroV, zeroV}; //ConvexHull b simplex set |
| 88 | |
| 89 | |
| 90 | Vec3V v = V3Neg(f: Q[0]); |
| 91 | Vec3V supportA = initialSupportA; |
| 92 | Vec3V supportB = initialSupportB; |
| 93 | Vec3V support = Q[0]; |
| 94 | |
| 95 | const FloatV minMargin = FMin(a.ConvexA::getSweepMargin(), b.ConvexB::getSweepMargin()); |
| 96 | const FloatV eps1 = FMul(a: minMargin, b: FLoad(f: 0.1f)); |
| 97 | const FloatV inflationPlusEps(FAdd(a: eps1, b: inflation)); |
| 98 | const FloatV eps2 = FMul(a: eps1, b: eps1); |
| 99 | |
| 100 | const FloatV inflation2 = FMul(a: inflationPlusEps, b: inflationPlusEps); |
| 101 | |
| 102 | Vec3V clos(Q[0]); |
| 103 | Vec3V preClos = clos; |
| 104 | //Vec3V closA(initialSupportA); |
| 105 | FloatV sDist = V3Dot(a: v, b: v); |
| 106 | FloatV minDist = sDist; |
| 107 | //Vec3V closAA = initialSupportA; |
| 108 | //Vec3V closBB = initialSupportB; |
| 109 | |
| 110 | BoolV bNotTerminated = FIsGrtr(a: sDist, b: eps2); |
| 111 | BoolV bNotDegenerated = bTrue; |
| 112 | |
| 113 | Vec3V nor = v; |
| 114 | |
| 115 | while(BAllEqTTTT(a: bNotTerminated)) |
| 116 | { |
| 117 | |
| 118 | minDist = sDist; |
| 119 | preClos = clos; |
| 120 | |
| 121 | const Vec3V vNorm = V3Normalize(a: v); |
| 122 | const Vec3V nvNorm = V3Neg(f: vNorm); |
| 123 | |
| 124 | supportA=a.ConvexA::support(vNorm); |
| 125 | supportB=V3Add(x, b.ConvexB::support(nvNorm)); |
| 126 | |
| 127 | //calculate the support point |
| 128 | support = V3Sub(a: supportA, b: supportB); |
| 129 | const Vec3V w = V3Neg(f: support); |
| 130 | const FloatV vw = FSub(a: V3Dot(a: vNorm, b: w), b: inflationPlusEps); |
| 131 | if(FAllGrtr(a: vw, b: zero)) |
| 132 | { |
| 133 | const FloatV vr = V3Dot(a: vNorm, b: r); |
| 134 | if(FAllGrtrOrEq(a: vr, b: zero)) |
| 135 | { |
| 136 | return false; |
| 137 | } |
| 138 | else |
| 139 | { |
| 140 | const FloatV _oldLambda = _lambda; |
| 141 | _lambda = FSub(a: _lambda, b: FDiv(a: vw, b: vr)); |
| 142 | if(FAllGrtr(a: _lambda, b: _oldLambda)) |
| 143 | { |
| 144 | if(FAllGrtr(a: _lambda, b: one)) |
| 145 | { |
| 146 | return false; |
| 147 | } |
| 148 | const Vec3V bPreCenter = x; |
| 149 | x = V3ScaleAdd(a: r, b: _lambda, c: s); |
| 150 | |
| 151 | const Vec3V offSet =V3Sub(a: x, b: bPreCenter); |
| 152 | const Vec3V b0 = V3Add(a: B[0], b: offSet); |
| 153 | const Vec3V b1 = V3Add(a: B[1], b: offSet); |
| 154 | const Vec3V b2 = V3Add(a: B[2], b: offSet); |
| 155 | |
| 156 | B[0] = b0; |
| 157 | B[1] = b1; |
| 158 | B[2] = b2; |
| 159 | |
| 160 | Q[0]=V3Sub(a: A[0], b: b0); |
| 161 | Q[1]=V3Sub(a: A[1], b: b1); |
| 162 | Q[2]=V3Sub(a: A[2], b: b2); |
| 163 | |
| 164 | supportB = V3Add(x, b.ConvexB::support(nvNorm)); |
| 165 | support = V3Sub(a: supportA, b: supportB); |
| 166 | minDist = maxDist; |
| 167 | nor = v; |
| 168 | //size=0; |
| 169 | } |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | PX_ASSERT(size < 4); |
| 174 | A[size]=supportA; |
| 175 | B[size]=supportB; |
| 176 | Q[size++]=support; |
| 177 | |
| 178 | //calculate the closest point between two convex hull |
| 179 | clos = GJKCPairDoSimplex(Q, A, B, support, size); |
| 180 | v = V3Neg(f: clos); |
| 181 | sDist = V3Dot(a: clos, b: clos); |
| 182 | |
| 183 | bNotDegenerated = FIsGrtr(a: minDist, b: sDist); |
| 184 | bNotTerminated = BAnd(a: FIsGrtr(a: sDist, b: inflation2), b: bNotDegenerated); |
| 185 | } |
| 186 | |
| 187 | |
| 188 | const BoolV aQuadratic = a.isMarginEqRadius(); |
| 189 | //ML:if the Minkowski sum of two objects are too close to the original(eps2 > sDist), we can't take v because we will lose lots of precision. Therefore, we will take |
| 190 | //previous configuration's normal which should give us a reasonable approximation. This effectively means that, when we do a sweep with inflation, we always keep v because |
| 191 | //the shapes converge separated. If we do a sweep without inflation, we will usually use the previous configuration's normal. |
| 192 | nor = V3Sel(c: BAnd(a: FIsGrtr(a: sDist, b: eps2), b: bNotDegenerated), a: v, b: nor); |
| 193 | nor = V3Neg(f: V3NormalizeSafe(a: nor, unsafeReturnValue: V3Zero())); |
| 194 | normal = nor; |
| 195 | lambda = _lambda; |
| 196 | |
| 197 | const Vec3V closestP = V3Sel(c: bNotDegenerated, a: clos, b: preClos); |
| 198 | Vec3V closA = zeroV, closB = zeroV; |
| 199 | getClosestPoint(Q, A, B, closest: closestP, closestA&: closA, closestB&: closB, size); |
| 200 | closestA = V3Sel(aQuadratic, V3NegScaleSub(nor, a.getMargin(), closA), closA); |
| 201 | |
| 202 | return true; |
| 203 | } |
| 204 | |
| 205 | |
| 206 | |
| 207 | /* |
| 208 | ConvexA is in the local space of ConvexB |
| 209 | lambda : the time of impact(TOI) |
| 210 | initialLambda : the start time of impact value (disable) |
| 211 | s : the sweep ray origin in ConvexB's space |
| 212 | r : the normalized sweep ray direction scaled by the sweep distance. r should be in ConvexB's space |
| 213 | normal : the contact normal in ConvexB's space |
| 214 | closestA : the tounching contact in ConvexB's space |
| 215 | inflation : the amount by which we inflate the swept shape. If the inflated shapes aren't initially-touching, |
| 216 | the TOI will return the time at which both shapes are at a distance equal to inflation separated. If inflation is 0 |
| 217 | the TOI will return the time at which both shapes are touching. |
| 218 | |
| 219 | */ |
| 220 | template<class ConvexA, class ConvexB> |
| 221 | bool gjkRaycastPenetration(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg initialDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, Ps::aos::FloatV& lambda, |
| 222 | Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal _inflation, const bool initialOverlap) |
| 223 | { |
| 224 | using namespace Ps::aos; |
| 225 | Vec3V closA; |
| 226 | Vec3V norm; |
| 227 | FloatV _lambda; |
| 228 | if(gjkRaycast(a, b, initialDir, initialLambda, s, r, _lambda, norm, closA, _inflation)) |
| 229 | { |
| 230 | const FloatV zero = FZero(); |
| 231 | lambda = _lambda; |
| 232 | if(FAllEq(a: _lambda, b: zero) && initialOverlap) |
| 233 | { |
| 234 | |
| 235 | //time of impact is zero, the sweep shape is intesect, use epa to get the normal and contact point |
| 236 | const FloatV contactDist = getSweepContactEps(a.getMargin(), b.getMargin()); |
| 237 | |
| 238 | FloatV sDist(zero); |
| 239 | PxU8 aIndices[4]; |
| 240 | PxU8 bIndices[4]; |
| 241 | PxU8 size=0; |
| 242 | GjkOutput output; |
| 243 | |
| 244 | //PX_COMPILE_TIME_ASSERT(typename Shrink<ConvexB>::Type != Gu::BoxV); |
| 245 | |
| 246 | typename ConvexA::ConvexGeomType convexA = a.getGjkConvex(); |
| 247 | typename ConvexB::ConvexGeomType convexB = b.getGjkConvex(); |
| 248 | |
| 249 | GjkStatus status = gjkPenetration<typename ConvexA::ConvexGeomType, typename ConvexB::ConvexGeomType>(convexA, convexB, |
| 250 | initialDir, contactDist, false, aIndices, bIndices, size, output); |
| 251 | //norm = V3Neg(norm); |
| 252 | if(status == GJK_CONTACT) |
| 253 | { |
| 254 | closA = output.closestA; |
| 255 | sDist = output.penDep; |
| 256 | norm = output.normal; |
| 257 | } |
| 258 | else if(status == EPA_CONTACT) |
| 259 | { |
| 260 | status = epaPenetration(a, b, aIndices, bIndices, size, false, FLoad(f: 1.f), output); |
| 261 | |
| 262 | if (status == EPA_CONTACT || status == EPA_DEGENERATE) |
| 263 | { |
| 264 | closA = output.closestA; |
| 265 | sDist = output.penDep; |
| 266 | norm = output.normal; |
| 267 | } |
| 268 | else |
| 269 | { |
| 270 | //ML: if EPA fail, we will use the ray direction as the normal and set pentration to be zero |
| 271 | closA = V3Zero(); |
| 272 | sDist = zero; |
| 273 | norm = V3Normalize(a: V3Neg(f: r)); |
| 274 | } |
| 275 | } |
| 276 | else |
| 277 | { |
| 278 | //ML:: this will be gjk degenerate case. However, we can still |
| 279 | //reply on the previous iteration's closest feature information |
| 280 | closA = output.closestA; |
| 281 | sDist = output.penDep; |
| 282 | norm = output.normal; |
| 283 | } |
| 284 | lambda = FMin(a: zero, b: sDist); |
| 285 | } |
| 286 | closestA = closA; |
| 287 | normal = norm; |
| 288 | return true; |
| 289 | } |
| 290 | return false; |
| 291 | } |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | #endif |
| 296 | |