RavEngine
Loading...
Searching...
No Matches
GuSweepTriangleUtils.h
1// Redistribution and use in source and binary forms, with or without
2// modification, are permitted provided that the following conditions
3// are met:
4// * Redistributions of source code must retain the above copyright
5// notice, this list of conditions and the following disclaimer.
6// * Redistributions in binary form must reproduce the above copyright
7// notice, this list of conditions and the following disclaimer in the
8// documentation and/or other materials provided with the distribution.
9// * Neither the name of NVIDIA CORPORATION nor the names of its
10// contributors may be used to endorse or promote products derived
11// from this software without specific prior written permission.
12//
13// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
14// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24//
25// Copyright (c) 2008-2022 NVIDIA Corporation. All rights reserved.
26// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
27// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
28
29#ifndef GU_SWEEP_TRIANGLE_UTILS_H
30#define GU_SWEEP_TRIANGLE_UTILS_H
31
32#include "geometry/PxTriangle.h"
33#include "PxQueryReport.h"
34
35#include "GuSweepSharedTests.h"
36#include "GuInternal.h"
37
38namespace physx
39{
40
41namespace Gu
42{
43 // PT: computes proper impact data for sphere-sweep-vs-tri, after the closest tri has been found.
44 void computeSphereTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& center, const PxVec3& dir, float t, const PxTriangle& tri);
45
46 // PT: computes proper impact data for box-sweep-vs-tri, after the closest tri has been found.
47 void computeBoxTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& boxExtents, const PxVec3& localDir, const PxTriangle& triInBoxSpace, PxReal impactDist);
48
49 // PT: computes impact normal between two edges. Produces better normals than just the EE cross product.
50 // This version properly computes the closest points between two colliding edges and makes a normal from these.
51 void computeEdgeEdgeNormal(PxVec3& normal, const PxVec3& p1, const PxVec3& p2_p1, const PxVec3& p3, const PxVec3& p4_p3, const PxVec3& dir, float d);
52
53 // PT: small function just to avoid duplicating the code.
54 // Returns index of first triangle we should process (when processing arrays of input triangles)
55 PX_FORCE_INLINE PxU32 getInitIndex(const PxU32* PX_RESTRICT cachedIndex, PxU32 nbTris)
56 {
57 PxU32 initIndex = 0; // PT: by default the first triangle to process is just the first one in the array
58 if(cachedIndex) // PT: but if we cached the last closest triangle from a previous call...
59 {
60 PX_ASSERT(*cachedIndex < nbTris);
61 PX_UNUSED(nbTris);
62 initIndex = *cachedIndex; // PT: ...then we should start with that one, to potentially shrink the ray as early as possible
63 }
64 return initIndex;
65 }
66
67 // PT: quick triangle rejection for sphere-based sweeps.
68 // Please refer to %SDKRoot%\InternalDocumentation\GU\cullTriangle.png for details & diagram.
69 PX_FORCE_INLINE bool cullTriangle(const PxVec3* PX_RESTRICT triVerts, const PxVec3& dir, PxReal radius, PxReal t, const PxReal dpc0)
70 {
71 // PT: project triangle on axis
72 const PxReal dp0 = triVerts[0].dot(dir);
73 const PxReal dp1 = triVerts[1].dot(dir);
74 const PxReal dp2 = triVerts[2].dot(dir);
75
76 // PT: keep min value = earliest possible impact distance
77 PxReal dp = dp0;
78 dp = physx::intrinsics::selectMin(dp, dp1);
79 dp = physx::intrinsics::selectMin(dp, dp2);
80
81 // PT: make sure we keep triangles that are about as close as best current distance
82 radius += 0.001f + GU_EPSILON_SAME_DISTANCE;
83
84 // PT: if earliest possible impact distance for this triangle is already larger than
85 // sphere's current best known impact distance, we can skip the triangle
86 if(dp>dpc0 + t + radius)
87 {
88 //PX_ASSERT(resx == 0.0f);
89 return false;
90 }
91
92 // PT: if triangle is fully located before the sphere's initial position, skip it too
93 const PxReal dpc1 = dpc0 - radius;
94 if(dp0<dpc1 && dp1<dpc1 && dp2<dpc1)
95 {
96 //PX_ASSERT(resx == 0.0f);
97 return false;
98 }
99
100 //PX_ASSERT(resx != 0.0f);
101 return true;
102 }
103
104 // PT: quick quad rejection for sphere-based sweeps. Same as for triangle, adapted for one more vertex.
105 PX_FORCE_INLINE bool cullQuad(const PxVec3* PX_RESTRICT quadVerts, const PxVec3& dir, PxReal radius, PxReal t, const PxReal dpc0)
106 {
107 // PT: project quad on axis
108 const PxReal dp0 = quadVerts[0].dot(dir);
109 const PxReal dp1 = quadVerts[1].dot(dir);
110 const PxReal dp2 = quadVerts[2].dot(dir);
111 const PxReal dp3 = quadVerts[3].dot(dir);
112
113 // PT: keep min value = earliest possible impact distance
114 PxReal dp = dp0;
115 dp = physx::intrinsics::selectMin(dp, dp1);
116 dp = physx::intrinsics::selectMin(dp, dp2);
117 dp = physx::intrinsics::selectMin(dp, dp3);
118
119 // PT: make sure we keep quads that are about as close as best current distance
120 radius += 0.001f + GU_EPSILON_SAME_DISTANCE;
121
122 // PT: if earliest possible impact distance for this quad is already larger than
123 // sphere's current best known impact distance, we can skip the quad
124 if(dp>dpc0 + t + radius)
125 return false;
126
127 // PT: if quad is fully located before the sphere's initial position, skip it too
128 const float dpc1 = dpc0 - radius;
129 if(dp0<dpc1 && dp1<dpc1 && dp2<dpc1 && dp3<dpc1)
130 return false;
131
132 return true;
133 }
134
135 // PT: computes distance between a point 'point' and a segment. The segment is defined as a starting point 'p0'
136 // and a direction vector 'dir' plus a length 't'. Segment's endpoint is p0 + dir * t.
137 //
138 // point
139 // o
140 // __/|
141 // __/ / |
142 // __/ / |(B)
143 // __/ (A)/ |
144 // __/ / | dir
145 // p0 o/---------o---------------o-- -->
146 // t (t<=fT) t (t>fT)
147 // return (A)^2 return (B)^2
148 //
149 // |<-------------->|
150 // fT
151 //
152 PX_FORCE_INLINE PxReal squareDistance(const PxVec3& p0, const PxVec3& dir, PxReal t, const PxVec3& point)
153 {
154 PxVec3 diff = point - p0;
155 PxReal fT = diff.dot(dir);
156 fT = physx::intrinsics::selectMax(fT, 0.0f);
157 fT = physx::intrinsics::selectMin(fT, t);
158 diff -= fT*dir;
159 return diff.magnitudeSquared();
160 }
161
162 // PT: quick triangle culling for sphere-based sweeps
163 // Please refer to %SDKRoot%\InternalDocumentation\GU\coarseCulling.png for details & diagram.
164 PX_FORCE_INLINE bool coarseCullingTri(const PxVec3& center, const PxVec3& dir, PxReal t, PxReal radius, const PxVec3* PX_RESTRICT triVerts)
165 {
166 // PT: compute center of triangle ### could be precomputed?
167 const PxVec3 triCenter = (triVerts[0] + triVerts[1] + triVerts[2]) * (1.0f/3.0f);
168
169 // PT: distance between the triangle center and the swept path (an LSS)
170 // Same as: distancePointSegmentSquared(center, center+dir*t, TriCenter);
171 PxReal d = PxSqrt(squareDistance(center, dir, t, triCenter)) - radius - 0.0001f;
172
173 if (d < 0.0f) // The triangle center lies inside the swept sphere
174 return true;
175
176 d*=d;
177
178 // PT: coarse capsule-vs-triangle overlap test ### distances could be precomputed?
179 if(1)
180 {
181 if(d <= (triCenter-triVerts[0]).magnitudeSquared())
182 return true;
183 if(d <= (triCenter-triVerts[1]).magnitudeSquared())
184 return true;
185 if(d <= (triCenter-triVerts[2]).magnitudeSquared())
186 return true;
187 }
188 else
189 {
190 const float d0 = (triCenter-triVerts[0]).magnitudeSquared();
191 const float d1 = (triCenter-triVerts[1]).magnitudeSquared();
192 const float d2 = (triCenter-triVerts[2]).magnitudeSquared();
193 float triRadius = physx::intrinsics::selectMax(d0, d1);
194 triRadius = physx::intrinsics::selectMax(triRadius, d2);
195 if(d <= triRadius)
196 return true;
197 }
198 return false;
199 }
200
201 // PT: quick quad culling for sphere-based sweeps. Same as for triangle, adapted for one more vertex.
202 PX_FORCE_INLINE bool coarseCullingQuad(const PxVec3& center, const PxVec3& dir, PxReal t, PxReal radius, const PxVec3* PX_RESTRICT quadVerts)
203 {
204 // PT: compute center of quad ### could be precomputed?
205 const PxVec3 quadCenter = (quadVerts[0] + quadVerts[1] + quadVerts[2] + quadVerts[3]) * (1.0f/4.0f);
206
207 // PT: distance between the quad center and the swept path (an LSS)
208 PxReal d = PxSqrt(squareDistance(center, dir, t, quadCenter)) - radius - 0.0001f;
209
210 if (d < 0.0f) // The quad center lies inside the swept sphere
211 return true;
212
213 d*=d;
214
215 // PT: coarse capsule-vs-quad overlap test ### distances could be precomputed?
216 if(1)
217 {
218 if(d <= (quadCenter-quadVerts[0]).magnitudeSquared())
219 return true;
220 if(d <= (quadCenter-quadVerts[1]).magnitudeSquared())
221 return true;
222 if(d <= (quadCenter-quadVerts[2]).magnitudeSquared())
223 return true;
224 if(d <= (quadCenter-quadVerts[3]).magnitudeSquared())
225 return true;
226 }
227 return false;
228 }
229
230 // PT: combined triangle culling for sphere-based sweeps
231 PX_FORCE_INLINE bool rejectTriangle(const PxVec3& center, const PxVec3& unitDir, PxReal curT, PxReal radius, const PxVec3* PX_RESTRICT triVerts, const PxReal dpc0)
232 {
233 if(!coarseCullingTri(center, unitDir, curT, radius, triVerts))
234 return true;
235 if(!cullTriangle(triVerts, unitDir, radius, curT, dpc0))
236 return true;
237 return false;
238 }
239
240 // PT: combined quad culling for sphere-based sweeps
241 PX_FORCE_INLINE bool rejectQuad(const PxVec3& center, const PxVec3& unitDir, PxReal curT, PxReal radius, const PxVec3* PX_RESTRICT quadVerts, const PxReal dpc0)
242 {
243 if(!coarseCullingQuad(center, unitDir, curT, radius, quadVerts))
244 return true;
245 if(!cullQuad(quadVerts, unitDir, radius, curT, dpc0))
246 return true;
247 return false;
248 }
249
250 PX_FORCE_INLINE bool shouldFlipNormal(const PxVec3& normal, bool meshBothSides, bool isDoubleSided, const PxVec3& triangleNormal, const PxVec3& dir)
251 {
252 // PT: this function assumes that input normal is opposed to the ray/sweep direction. This is always
253 // what we want except when we hit a single-sided back face with 'meshBothSides' enabled.
254
255 if(!meshBothSides || isDoubleSided)
256 return false;
257
258 PX_ASSERT(normal.dot(dir) <= 0.0f); // PT: if this fails, the logic below cannot be applied
259 PX_UNUSED(normal);
260 return triangleNormal.dot(dir) > 0.0f; // PT: true for back-facing hits
261 }
262
263 PX_FORCE_INLINE bool shouldFlipNormal(const PxVec3& normal, bool meshBothSides, bool isDoubleSided, const PxTriangle& triangle, const PxVec3& dir, const PxTransform* pose)
264 {
265 // PT: this function assumes that input normal is opposed to the ray/sweep direction. This is always
266 // what we want except when we hit a single-sided back face with 'meshBothSides' enabled.
267
268 if(!meshBothSides || isDoubleSided)
269 return false;
270
271 PX_ASSERT(normal.dot(dir) <= 0.0f); // PT: if this fails, the logic below cannot be applied
272 PX_UNUSED(normal);
273
274 PxVec3 triangleNormal;
275 triangle.denormalizedNormal(triangleNormal);
276
277 if(pose)
278 triangleNormal = pose->rotate(triangleNormal);
279
280 return triangleNormal.dot(dir) > 0.0f; // PT: true for back-facing hits
281 }
282
283 // PT: implements the spec for IO sweeps in a single place (to ensure consistency)
284 PX_FORCE_INLINE bool setInitialOverlapResults(PxGeomSweepHit& hit, const PxVec3& unitDir, PxU32 faceIndex)
285 {
286 // PT: please write these fields in the order they are listed in the struct.
287 hit.faceIndex = faceIndex;
289 hit.normal = -unitDir;
290 hit.distance = 0.0f;
291 return true; // PT: true indicates a hit, saves some lines in calling code
292 }
293
294 PX_FORCE_INLINE void computeBoxLocalImpact( PxVec3& pos, PxVec3& normal, PxHitFlags& outFlags,
295 const Box& box, const PxVec3& localDir, const PxTriangle& triInBoxSpace,
296 const PxHitFlags inFlags, bool isDoubleSided, bool meshBothSides, PxReal impactDist)
297 {
299 {
300 PxVec3 localPos, localNormal;
301 computeBoxTriImpactData(localPos, localNormal, box.extents, localDir, triInBoxSpace, impactDist);
302
303 if(inFlags & PxHitFlag::eNORMAL)
304 {
305 localNormal.normalize();
306
307 // PT: doing this after the 'rotate' minimizes errors when normal and dir are close to perpendicular
308 // ....but we must do it before the rotate now, because triangleNormal is in box space (and thus we
309 // need the normal with the proper orientation, in box space. We can't fix it after it's been rotated
310 // to box space.
311 // Technically this one is only here because of the EE cross product in the feature-based sweep.
312 // PT: TODO: revisit corresponding code in computeImpactData, get rid of ambiguity
313 // PT: TODO: this may not be needed anymore
314 if((localNormal.dot(localDir))>0.0f)
315 localNormal = -localNormal;
316
317 // PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention
318 if(shouldFlipNormal(localNormal, meshBothSides, isDoubleSided, triInBoxSpace, localDir, NULL))
319 localNormal = -localNormal;
320
321 normal = box.rotate(localNormal);
322 outFlags |= PxHitFlag::eNORMAL;
323 }
324
325 if(inFlags & PxHitFlag::ePOSITION)
326 {
327 pos = box.transform(localPos);
328 outFlags |= PxHitFlag::ePOSITION;
329 }
330 }
331 }
332
333} // namespace Gu
334
335}
336
337#endif
#define PX_RESTRICT
Definition PxPreprocessor.h:355
#define PX_FORCE_INLINE
Definition PxPreprocessor.h:335
GLM_FUNC_DECL vec< 3, T, Q > triangleNormal(vec< 3, T, Q > const &p1, vec< 3, T, Q > const &p2, vec< 3, T, Q > const &p3)
Definition normal.inl:7
Sorts an array of objects in ascending order, assuming that the predicate implements the < operator:
Definition PxBoxController.h:39
PX_CUDA_CALLABLE PX_FORCE_INLINE float PxSqrt(float a)
Square root.
Definition PxMath.h:146
@ eNORMAL
"normal" member of #PxQueryHit is valid
Definition PxGeometryHit.h:61
@ eFACE_INDEX
"face index" member of #PxQueryHit is valid
Definition PxGeometryHit.h:75
@ ePOSITION
"position" member of #PxQueryHit is valid
Definition PxGeometryHit.h:60