| 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_SWEEP_TRIANGLE_UTILS_H |
| 31 | #define GU_SWEEP_TRIANGLE_UTILS_H |
| 32 | |
| 33 | #include "geometry/PxTriangle.h" |
| 34 | #include "PxQueryReport.h" |
| 35 | |
| 36 | #include "CmPhysXCommon.h" |
| 37 | #include "GuSweepSharedTests.h" |
| 38 | #include "GuInternal.h" |
| 39 | |
| 40 | namespace physx |
| 41 | { |
| 42 | |
| 43 | namespace Gu |
| 44 | { |
| 45 | // PT: computes proper impact data for sphere-sweep-vs-tri, after the closest tri has been found. |
| 46 | void computeSphereTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& center, const PxVec3& dir, float t, const PxTriangle& tri); |
| 47 | |
| 48 | // PT: computes proper impact data for box-sweep-vs-tri, after the closest tri has been found. |
| 49 | void computeBoxTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& boxExtents, const PxVec3& localDir, const PxTriangle& triInBoxSpace, PxReal impactDist); |
| 50 | |
| 51 | // PT: computes impact normal between two edges. Produces better normals than just the EE cross product. |
| 52 | // This version properly computes the closest points between two colliding edges and makes a normal from these. |
| 53 | void computeEdgeEdgeNormal(PxVec3& normal, const PxVec3& p1, const PxVec3& p2_p1, const PxVec3& p3, const PxVec3& p4_p3, const PxVec3& dir, float d); |
| 54 | |
| 55 | // PT: small function just to avoid duplicating the code. |
| 56 | // Returns index of first triangle we should process (when processing arrays of input triangles) |
| 57 | PX_FORCE_INLINE PxU32 getInitIndex(const PxU32* PX_RESTRICT cachedIndex, PxU32 nbTris) |
| 58 | { |
| 59 | PxU32 initIndex = 0; // PT: by default the first triangle to process is just the first one in the array |
| 60 | if(cachedIndex) // PT: but if we cached the last closest triangle from a previous call... |
| 61 | { |
| 62 | PX_ASSERT(*cachedIndex < nbTris); |
| 63 | PX_UNUSED(nbTris); |
| 64 | initIndex = *cachedIndex; // PT: ...then we should start with that one, to potentially shrink the ray as early as possible |
| 65 | } |
| 66 | return initIndex; |
| 67 | } |
| 68 | |
| 69 | // PT: quick triangle rejection for sphere-based sweeps. |
| 70 | // Please refer to %SDKRoot%\InternalDocumentation\GU\cullTriangle.png for details & diagram. |
| 71 | PX_FORCE_INLINE bool cullTriangle(const PxVec3* PX_RESTRICT triVerts, const PxVec3& dir, PxReal radius, PxReal t, const PxReal dpc0) |
| 72 | { |
| 73 | // PT: project triangle on axis |
| 74 | const PxReal dp0 = triVerts[0].dot(v: dir); |
| 75 | const PxReal dp1 = triVerts[1].dot(v: dir); |
| 76 | const PxReal dp2 = triVerts[2].dot(v: dir); |
| 77 | |
| 78 | // PT: keep min value = earliest possible impact distance |
| 79 | PxReal dp = dp0; |
| 80 | dp = physx::intrinsics::selectMin(a: dp, b: dp1); |
| 81 | dp = physx::intrinsics::selectMin(a: dp, b: dp2); |
| 82 | |
| 83 | // PT: make sure we keep triangles that are about as close as best current distance |
| 84 | radius += 0.001f + GU_EPSILON_SAME_DISTANCE; |
| 85 | |
| 86 | // PT: if earliest possible impact distance for this triangle is already larger than |
| 87 | // sphere's current best known impact distance, we can skip the triangle |
| 88 | if(dp>dpc0 + t + radius) |
| 89 | { |
| 90 | //PX_ASSERT(resx == 0.0f); |
| 91 | return false; |
| 92 | } |
| 93 | |
| 94 | // PT: if triangle is fully located before the sphere's initial position, skip it too |
| 95 | const PxReal dpc1 = dpc0 - radius; |
| 96 | if(dp0<dpc1 && dp1<dpc1 && dp2<dpc1) |
| 97 | { |
| 98 | //PX_ASSERT(resx == 0.0f); |
| 99 | return false; |
| 100 | } |
| 101 | |
| 102 | //PX_ASSERT(resx != 0.0f); |
| 103 | return true; |
| 104 | } |
| 105 | |
| 106 | // PT: quick quad rejection for sphere-based sweeps. Same as for triangle, adapted for one more vertex. |
| 107 | PX_FORCE_INLINE bool cullQuad(const PxVec3* PX_RESTRICT quadVerts, const PxVec3& dir, PxReal radius, PxReal t, const PxReal dpc0) |
| 108 | { |
| 109 | // PT: project quad on axis |
| 110 | const PxReal dp0 = quadVerts[0].dot(v: dir); |
| 111 | const PxReal dp1 = quadVerts[1].dot(v: dir); |
| 112 | const PxReal dp2 = quadVerts[2].dot(v: dir); |
| 113 | const PxReal dp3 = quadVerts[3].dot(v: dir); |
| 114 | |
| 115 | // PT: keep min value = earliest possible impact distance |
| 116 | PxReal dp = dp0; |
| 117 | dp = physx::intrinsics::selectMin(a: dp, b: dp1); |
| 118 | dp = physx::intrinsics::selectMin(a: dp, b: dp2); |
| 119 | dp = physx::intrinsics::selectMin(a: dp, b: dp3); |
| 120 | |
| 121 | // PT: make sure we keep quads that are about as close as best current distance |
| 122 | radius += 0.001f; |
| 123 | |
| 124 | // PT: if earliest possible impact distance for this quad is already larger than |
| 125 | // sphere's current best known impact distance, we can skip the quad |
| 126 | if(dp>dpc0 + t + radius) |
| 127 | return false; |
| 128 | |
| 129 | // PT: if quad is fully located before the sphere's initial position, skip it too |
| 130 | const float dpc1 = dpc0 - radius; |
| 131 | if(dp0<dpc1 && dp1<dpc1 && dp2<dpc1 && dp3<dpc1) |
| 132 | return false; |
| 133 | |
| 134 | return true; |
| 135 | } |
| 136 | |
| 137 | // PT: computes distance between a point 'point' and a segment. The segment is defined as a starting point 'p0' |
| 138 | // and a direction vector 'dir' plus a length 't'. Segment's endpoint is p0 + dir * t. |
| 139 | // |
| 140 | // point |
| 141 | // o |
| 142 | // __/| |
| 143 | // __/ / | |
| 144 | // __/ / |(B) |
| 145 | // __/ (A)/ | |
| 146 | // __/ / | dir |
| 147 | // p0 o/---------o---------------o-- --> |
| 148 | // t (t<=fT) t (t>fT) |
| 149 | // return (A)^2 return (B)^2 |
| 150 | // |
| 151 | // |<-------------->| |
| 152 | // fT |
| 153 | // |
| 154 | PX_FORCE_INLINE PxReal squareDistance(const PxVec3& p0, const PxVec3& dir, PxReal t, const PxVec3& point) |
| 155 | { |
| 156 | PxVec3 diff = point - p0; |
| 157 | PxReal fT = diff.dot(v: dir); |
| 158 | fT = physx::intrinsics::selectMax(a: fT, b: 0.0f); |
| 159 | fT = physx::intrinsics::selectMin(a: fT, b: t); |
| 160 | diff -= fT*dir; |
| 161 | return diff.magnitudeSquared(); |
| 162 | } |
| 163 | |
| 164 | // PT: quick triangle culling for sphere-based sweeps |
| 165 | // Please refer to %SDKRoot%\InternalDocumentation\GU\coarseCulling.png for details & diagram. |
| 166 | PX_FORCE_INLINE bool coarseCullingTri(const PxVec3& center, const PxVec3& dir, PxReal t, PxReal radius, const PxVec3* PX_RESTRICT triVerts) |
| 167 | { |
| 168 | // PT: compute center of triangle ### could be precomputed? |
| 169 | const PxVec3 triCenter = (triVerts[0] + triVerts[1] + triVerts[2]) * (1.0f/3.0f); |
| 170 | |
| 171 | // PT: distance between the triangle center and the swept path (an LSS) |
| 172 | // Same as: distancePointSegmentSquared(center, center+dir*t, TriCenter); |
| 173 | PxReal d = PxSqrt(a: squareDistance(p0: center, dir, t, point: triCenter)) - radius - 0.0001f; |
| 174 | |
| 175 | if (d < 0.0f) // The triangle center lies inside the swept sphere |
| 176 | return true; |
| 177 | |
| 178 | d*=d; |
| 179 | |
| 180 | // PT: coarse capsule-vs-triangle overlap test ### distances could be precomputed? |
| 181 | if(1) |
| 182 | { |
| 183 | if(d <= (triCenter-triVerts[0]).magnitudeSquared()) |
| 184 | return true; |
| 185 | if(d <= (triCenter-triVerts[1]).magnitudeSquared()) |
| 186 | return true; |
| 187 | if(d <= (triCenter-triVerts[2]).magnitudeSquared()) |
| 188 | return true; |
| 189 | } |
| 190 | else |
| 191 | { |
| 192 | const float d0 = (triCenter-triVerts[0]).magnitudeSquared(); |
| 193 | const float d1 = (triCenter-triVerts[1]).magnitudeSquared(); |
| 194 | const float d2 = (triCenter-triVerts[2]).magnitudeSquared(); |
| 195 | float triRadius = physx::intrinsics::selectMax(a: d0, b: d1); |
| 196 | triRadius = physx::intrinsics::selectMax(a: triRadius, b: d2); |
| 197 | if(d <= triRadius) |
| 198 | return true; |
| 199 | } |
| 200 | return false; |
| 201 | } |
| 202 | |
| 203 | // PT: quick quad culling for sphere-based sweeps. Same as for triangle, adapted for one more vertex. |
| 204 | PX_FORCE_INLINE bool coarseCullingQuad(const PxVec3& center, const PxVec3& dir, PxReal t, PxReal radius, const PxVec3* PX_RESTRICT quadVerts) |
| 205 | { |
| 206 | // PT: compute center of quad ### could be precomputed? |
| 207 | const PxVec3 quadCenter = (quadVerts[0] + quadVerts[1] + quadVerts[2] + quadVerts[3]) * (1.0f/4.0f); |
| 208 | |
| 209 | // PT: distance between the quad center and the swept path (an LSS) |
| 210 | PxReal d = PxSqrt(a: squareDistance(p0: center, dir, t, point: quadCenter)) - radius - 0.0001f; |
| 211 | |
| 212 | if (d < 0.0f) // The quad center lies inside the swept sphere |
| 213 | return true; |
| 214 | |
| 215 | d*=d; |
| 216 | |
| 217 | // PT: coarse capsule-vs-quad overlap test ### distances could be precomputed? |
| 218 | if(1) |
| 219 | { |
| 220 | if(d <= (quadCenter-quadVerts[0]).magnitudeSquared()) |
| 221 | return true; |
| 222 | if(d <= (quadCenter-quadVerts[1]).magnitudeSquared()) |
| 223 | return true; |
| 224 | if(d <= (quadCenter-quadVerts[2]).magnitudeSquared()) |
| 225 | return true; |
| 226 | if(d <= (quadCenter-quadVerts[3]).magnitudeSquared()) |
| 227 | return true; |
| 228 | } |
| 229 | return false; |
| 230 | } |
| 231 | |
| 232 | // PT: combined triangle culling for sphere-based sweeps |
| 233 | PX_FORCE_INLINE bool rejectTriangle(const PxVec3& center, const PxVec3& unitDir, PxReal curT, PxReal radius, const PxVec3* PX_RESTRICT triVerts, const PxReal dpc0) |
| 234 | { |
| 235 | if(!coarseCullingTri(center, dir: unitDir, t: curT, radius, triVerts)) |
| 236 | return true; |
| 237 | if(!cullTriangle(triVerts, dir: unitDir, radius, t: curT, dpc0)) |
| 238 | return true; |
| 239 | return false; |
| 240 | } |
| 241 | |
| 242 | // PT: combined quad culling for sphere-based sweeps |
| 243 | PX_FORCE_INLINE bool rejectQuad(const PxVec3& center, const PxVec3& unitDir, PxReal curT, PxReal radius, const PxVec3* PX_RESTRICT quadVerts, const PxReal dpc0) |
| 244 | { |
| 245 | if(!coarseCullingQuad(center, dir: unitDir, t: curT, radius, quadVerts)) |
| 246 | return true; |
| 247 | if(!cullQuad(quadVerts, dir: unitDir, radius, t: curT, dpc0)) |
| 248 | return true; |
| 249 | return false; |
| 250 | } |
| 251 | |
| 252 | PX_FORCE_INLINE bool shouldFlipNormal(const PxVec3& normal, bool meshBothSides, bool isDoubleSided, const PxVec3& triangleNormal, const PxVec3& dir) |
| 253 | { |
| 254 | // PT: this function assumes that input normal is opposed to the ray/sweep direction. This is always |
| 255 | // what we want except when we hit a single-sided back face with 'meshBothSides' enabled. |
| 256 | |
| 257 | if(!meshBothSides || isDoubleSided) |
| 258 | return false; |
| 259 | |
| 260 | PX_ASSERT(normal.dot(dir) <= 0.0f); // PT: if this fails, the logic below cannot be applied |
| 261 | PX_UNUSED(normal); |
| 262 | return triangleNormal.dot(v: dir) > 0.0f; // PT: true for back-facing hits |
| 263 | } |
| 264 | |
| 265 | PX_FORCE_INLINE bool shouldFlipNormal(const PxVec3& normal, bool meshBothSides, bool isDoubleSided, const PxTriangle& triangle, const PxVec3& dir, const PxTransform* pose) |
| 266 | { |
| 267 | // PT: this function assumes that input normal is opposed to the ray/sweep direction. This is always |
| 268 | // what we want except when we hit a single-sided back face with 'meshBothSides' enabled. |
| 269 | |
| 270 | if(!meshBothSides || isDoubleSided) |
| 271 | return false; |
| 272 | |
| 273 | PX_ASSERT(normal.dot(dir) <= 0.0f); // PT: if this fails, the logic below cannot be applied |
| 274 | PX_UNUSED(normal); |
| 275 | |
| 276 | PxVec3 triangleNormal; |
| 277 | triangle.denormalizedNormal(normal&: triangleNormal); |
| 278 | |
| 279 | if(pose) |
| 280 | triangleNormal = pose->rotate(input: triangleNormal); |
| 281 | |
| 282 | return triangleNormal.dot(v: dir) > 0.0f; // PT: true for back-facing hits |
| 283 | } |
| 284 | |
| 285 | // PT: implements the spec for IO sweeps in a single place (to ensure consistency) |
| 286 | PX_FORCE_INLINE bool setInitialOverlapResults(PxSweepHit& hit, const PxVec3& unitDir, PxU32 faceIndex) |
| 287 | { |
| 288 | // PT: please write these fields in the order they are listed in the struct. |
| 289 | hit.faceIndex = faceIndex; |
| 290 | hit.flags = PxHitFlag::eNORMAL|PxHitFlag::eFACE_INDEX; |
| 291 | hit.normal = -unitDir; |
| 292 | hit.distance = 0.0f; |
| 293 | return true; // PT: true indicates a hit, saves some lines in calling code |
| 294 | } |
| 295 | |
| 296 | PX_FORCE_INLINE void computeBoxLocalImpact( PxVec3& pos, PxVec3& normal, PxHitFlags& outFlags, |
| 297 | const Box& box, const PxVec3& localDir, const PxTriangle& triInBoxSpace, |
| 298 | const PxHitFlags inFlags, bool isDoubleSided, bool meshBothSides, PxReal impactDist) |
| 299 | { |
| 300 | if(inFlags & (PxHitFlag::eNORMAL|PxHitFlag::ePOSITION)) |
| 301 | { |
| 302 | PxVec3 localPos, localNormal; |
| 303 | computeBoxTriImpactData(hit&: localPos, normal&: localNormal, boxExtents: box.extents, localDir, triInBoxSpace, impactDist); |
| 304 | |
| 305 | if(inFlags & PxHitFlag::eNORMAL) |
| 306 | { |
| 307 | localNormal.normalize(); |
| 308 | |
| 309 | // PT: doing this after the 'rotate' minimizes errors when normal and dir are close to perpendicular |
| 310 | // ....but we must do it before the rotate now, because triangleNormal is in box space (and thus we |
| 311 | // need the normal with the proper orientation, in box space. We can't fix it after it's been rotated |
| 312 | // to box space. |
| 313 | // Technically this one is only here because of the EE cross product in the feature-based sweep. |
| 314 | // PT: TODO: revisit corresponding code in computeImpactData, get rid of ambiguity |
| 315 | // PT: TODO: this may not be needed anymore |
| 316 | if((localNormal.dot(v: localDir))>0.0f) |
| 317 | localNormal = -localNormal; |
| 318 | |
| 319 | // PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention |
| 320 | if(shouldFlipNormal(normal: localNormal, meshBothSides, isDoubleSided, triangle: triInBoxSpace, dir: localDir, NULL)) |
| 321 | localNormal = -localNormal; |
| 322 | |
| 323 | normal = box.rotate(src: localNormal); |
| 324 | outFlags |= PxHitFlag::eNORMAL; |
| 325 | } |
| 326 | |
| 327 | if(inFlags & PxHitFlag::ePOSITION) |
| 328 | { |
| 329 | pos = box.transform(src: localPos); |
| 330 | outFlags |= PxHitFlag::ePOSITION; |
| 331 | } |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | } // namespace Gu |
| 336 | |
| 337 | } |
| 338 | |
| 339 | #endif |
| 340 | |