RavEngine
Loading...
Searching...
No Matches
GuGJKPenetration.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_GJK_PENETRATION_H
30#define GU_GJK_PENETRATION_H
31
32
33#include "GuConvexSupportTable.h"
34#include "GuGJKSimplex.h"
35#include "GuVecConvexHullNoScale.h"
36#include "GuGJKUtil.h"
37#include "foundation/PxUtilities.h"
38#include "GuGJKType.h"
39
40#define GJK_VALIDATE 0
41
42
43namespace physx
44{
45namespace Gu
46{
47
48 class ConvexV;
49
50
51 PX_FORCE_INLINE void assignWarmStartValue(PxU8* PX_RESTRICT aIndices, PxU8* PX_RESTRICT bIndices, PxU8& size_, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, PxU32 size )
52 {
53 if(aIndices)
54 {
55 PX_ASSERT(bIndices);
56 size_ = PxTo8(size);
57 for(PxU32 i=0; i<size; ++i)
58 {
59 aIndices[i] = PxTo8(aInd[i]);
60 bIndices[i] = PxTo8(bInd[i]);
61 }
62 }
63 }
64
65
66 PX_FORCE_INLINE void validateDuplicateVertex(const aos::Vec3V* Q, const aos::Vec3VArg support, const PxU32 size)
67 {
68 using namespace aos;
69
70 const FloatV eps = FEps();
71 //Get rid of the duplicate point
72 BoolV match = BFFFF();
73 for(PxU32 na = 0; na < size; ++na)
74 {
75 Vec3V dif = V3Sub(Q[na], support);
76 match = BOr(match, FIsGrtr(eps, V3Dot(dif, dif)));
77 }
78
79 //we have duplicate code
80 if(BAllEqTTTT(match))
81 {
82 PX_ASSERT(0);
83 }
84 }
85
86
87 //*Each convex has
88 //* a support function
89 //* a margin - if the shape is sphere/capsule, margin is the radius
90 //* a minMargin - some percentage of margin, which is used to determine the termination condition for gjk
91
92 //*We'll report:
93 //* GJK_NON_INTERSECT if the minimum distance between the shapes is greater than the sum of the margins and the the contactDistance
94 //* EPA_CONTACT if shapes overlap. We treat sphere/capsule as a point/a segment and we shrunk other shapes by 10% of margin
95 //* GJK_CONTACT if the algorithm converges, and the distance between the shapes is less than the sum of the margins plus the contactDistance. In this case we return the closest points found
96 //* GJK_DEGENERATE if the algorithm doesn't converge, we return this flag to indicate the normal and closest point we return might not be accurated
97 template<typename ConvexA, typename ConvexB >
98 PX_NOINLINE GjkStatus gjkPenetration(const ConvexA& a, const ConvexB& b, const aos::Vec3VArg initialSearchDir, const aos::FloatVArg contactDist, const bool takeCoreShape,
99 PxU8* PX_RESTRICT aIndices, PxU8* PX_RESTRICT bIndices, aos::Vec3V* PX_RESTRICT aPoints, aos::Vec3V* PX_RESTRICT bPoints, PxU8& warmStartSize,
100 GjkOutput& output)
101 {
102 using namespace aos;
103
104 //ML: eps is the threshold that uses to determine whether two (shrunk) shapes overlap. We calculate eps2 based on 10% of the minimum margin of two shapes
105 const FloatV minMargin = FMin(a.ConvexA::getMinMargin(), b.ConvexB::getMinMargin());
106 const FloatV eps = FMul(minMargin, FLoad(0.1f));
107
108 //const FloatV eps2 = FMul(_eps2, _eps2);
109 //ML: epsRel2 is the square of 0.01. This is used to scale the square distance of a closest point to origin to determine whether two shrunk shapes overlap in the margin, but
110 //they don't overlap.
111 //const FloatV epsRel2 = FMax(FLoad(0.0001), eps2);
112
113 // ML:epsRel is square value of 1.5% which applied to the distance of a closest point(v) to the origin.
114 // If |v|- v/|v|.dot(w) < epsRel*|v|=>(|v|*(1-epsRel) < v/|v|.dot(w)),
115 // two shapes are clearly separated, GJK terminate and return non intersect.
116 // This adjusts the termination condition based on the length of v
117 // which avoids ill-conditioned terminations.
118 const FloatV epsRel = FLoad(0.000225f);//1.5%.
119 const FloatV relDif = FSub(FOne(), epsRel);
120
121 const FloatV zero = FZero();
122
123 //capsule/sphere will have margin which is its radius
124 const FloatV marginA = a.getMargin();
125 const FloatV marginB = b.getMargin();
126
127 const BoolV aQuadratic = a.isMarginEqRadius();
128 const BoolV bQuadratic = b.isMarginEqRadius();
129 const FloatV tMarginA = FSel(aQuadratic, marginA, zero);
130 const FloatV tMarginB = FSel(bQuadratic, marginB, zero);
131
132 const FloatV sumMargin = FAdd(tMarginA, tMarginB);
133 const FloatV sumExpandedMargin = FAdd(sumMargin, contactDist);
134
135 FloatV dist = FMax();
136 FloatV prevDist = dist;
137 const Vec3V zeroV = V3Zero();
138 Vec3V prevClos = zeroV;
139
140 const BoolV bTrue = BTTTT();
141 BoolV bNotTerminated = bTrue;
142 BoolV bNotDegenerated = bTrue;
143 Vec3V closest;
144
145 Vec3V Q[4];
146 Vec3V* A = aPoints;
147 Vec3V* B = bPoints;
148 PxI32 aInd[4];
149 PxI32 bInd[4];
150 Vec3V supportA = zeroV, supportB = zeroV, support=zeroV;
151 Vec3V v;
152
153 PxU32 size = 0;//_size;
154
155
156 //ML: if _size!=0, which means we pass in the previous frame simplex so that we can warm-start the simplex.
157 //In this case, GJK will normally terminate in one iteration
158 if(warmStartSize != 0)
159 {
160 for(PxU32 i=0; i<warmStartSize; ++i)
161 {
162 aInd[i] = aIndices[i];
163 bInd[i] = bIndices[i];
164
165 //de-virtualize
166 supportA = a.ConvexA::supportPoint(aIndices[i]);
167 supportB = b.ConvexB::supportPoint(bIndices[i]);
168
169 support = V3Sub(supportA, supportB);
170
171#if GJK_VALIDATE
172 //ML: this is used to varify whether we will have duplicate vertices in the warm-start value. If this function get triggered,
173 //this means something isn't right and we need to investigate
174 validateDuplicateVertex(Q, support, size);
175#endif
176 A[size] = supportA;
177 B[size] = supportB;
178 Q[size++] = support;
179 }
180
181 //run simplex solver to determine whether the point is closest enough so that gjk can terminate
182 closest = GJKCPairDoSimplex(Q, A, B, aInd, bInd, support, size);
183 dist = V3Length(closest);
184 //sDist = V3Dot(closest, closest);
185 v = V3ScaleInv(closest, dist);
186 prevDist = dist;
187 prevClos = closest;
188
189 bNotTerminated = FIsGrtr(dist, eps);
190 }
191 else
192 {
193 //const Vec3V _initialSearchDir = V3Sub(a.getCenter(), b.getCenter());
194 closest = V3Sel(FIsGrtr(V3Dot(initialSearchDir, initialSearchDir), zero), initialSearchDir, V3UnitX());
195 v = V3Normalize(closest);
196 }
197
198
199 // ML : termination condition
200 //(1)two shapes overlap. GJK will terminate based on sq(v) < eps2 and indicate that two shapes are overlapping.
201 //(2)two shapes are separated. If sq(vw) > sqMargin * sq(v), which means the original objects do not intesect, GJK terminate with GJK_NON_INTERSECT.
202 //(3)two shapes don't overlap. However, they interect within margin distance. if sq(v)- vw < epsRel2*sq(v), this means the shrunk shapes interect in the margin,
203 // GJK terminate with GJK_CONTACT.
204 while(BAllEqTTTT(bNotTerminated))
205 {
206 //prevDist, prevClos are used to store the previous iteration's closest point and the square distance from the closest point
207 //to origin in Mincowski space
208 prevDist = dist;
209 prevClos = closest;
210
211 //de-virtualize
212 supportA = a.ConvexA::support(V3Neg(closest), aInd[size]);
213 supportB = b.ConvexB::support(closest, bInd[size]);
214
215 //calculate the support point
216 support = V3Sub(supportA, supportB);
217
218 const FloatV vw = V3Dot(v, support);
219 if(FAllGrtr(vw, sumExpandedMargin))
220 {
221 assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size);
222 return GJK_NON_INTERSECT;
223 }
224
225 //if(FAllGrtr(FMul(epsRel, dist), FSub(dist, vw)))
226 if(FAllGrtr(vw, FMul(dist, relDif)))
227 {
228 assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size);
229 PX_ASSERT(FAllGrtr(dist, FEps()));
230 //const Vec3V n = V3ScaleInv(closest, dist);//normalise
231 output.normal = v;
232 Vec3V closA, closB;
233 getClosestPoint(Q, A, B, closest, closA, closB, size);
234 //ML: if one of the shape is sphere/capsule and the takeCoreShape flag is true, the contact point for sphere/capsule will be the sphere center or a point in the
235 //capsule segment. This will increase the stability for the manifold recycling code. Otherwise, we will return a contact point on the surface for sphere/capsule
236 //while the takeCoreShape flag is set to be false
237 if(takeCoreShape)
238 {
239 output.closestA= closA;
240 output.closestB = closB;
241 output.penDep = dist;
242
243 }
244 else
245 {
246 //This is for capsule/sphere want to take the surface point. For box/convex,
247 //we need to get rid of margin
248 output.closestA = V3NegScaleSub(v, tMarginA, closA);
249 output.closestB = V3ScaleAdd(v, tMarginB, closB);
250 output.penDep = FSub(dist, sumMargin);
251 }
252
253 return GJK_CONTACT;
254 }
255
256 A[size] = supportA;
257 B[size] = supportB;
258 Q[size++]=support;
259 PX_ASSERT(size <= 4);
260
261 //calculate the closest point between two convex hull
262 closest = GJKCPairDoSimplex(Q, A, B, aInd, bInd, support, size);
263
264 dist = V3Length(closest);
265 v = V3ScaleInv(closest, dist);
266
267 bNotDegenerated = FIsGrtr(prevDist, dist);
268 bNotTerminated = BAnd(FIsGrtr(dist, eps), bNotDegenerated);
269 }
270
271 if(BAllEqFFFF(bNotDegenerated))
272 {
273 assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size-1);
274
275 //Reset back to older closest point
276 dist = prevDist;
277 closest = prevClos;//V3Sub(closA, closB);
278 Vec3V closA, closB;
279 getClosestPoint(Q, A, B, closest, closA, closB, size);
280
281 //PX_ASSERT(FAllGrtr(dist, FEps()));
282 const Vec3V n = V3ScaleInv(prevClos, prevDist);//normalise
283 output.normal = n;
284
285 output.searchDir = v;
286
287 if(takeCoreShape)
288 {
289 output.closestA = closA;
290 output.closestB = closB;
291 output.penDep = dist;
292 }
293 else
294 {
295 //This is for capsule/sphere want to take the surface point. For box/convex,
296 //we need to get rid of margin
297 output.closestA = V3NegScaleSub(n, tMarginA, closA);
298 output.closestB = V3ScaleAdd(n, tMarginB, closB);
299 output.penDep = FSub(dist, sumMargin);
300 if (FAllGrtrOrEq(sumMargin, dist))
301 return GJK_CONTACT;
302 }
303
304 return GJK_DEGENERATE;
305 }
306 else
307 {
308 //this two shapes are deeply intersected with each other, we need to use EPA algorithm to calculate MTD
309 assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size);
310 return EPA_CONTACT;
311
312 }
313 }
314 template<typename ConvexA, typename ConvexB >
315 PX_NOINLINE GjkStatus gjkPenetration(const ConvexA& a, const ConvexB& b, const aos::Vec3VArg initialSearchDir, const aos::FloatVArg contactDist, const bool takeCoreShape,
316 PxU8* PX_RESTRICT aIndices, PxU8* PX_RESTRICT bIndices, PxU8& warmStartSize,
317 GjkOutput& output)
318 {
319 aos::Vec3V aPoints[4], bPoints[4];
320 return gjkPenetration(a, b, initialSearchDir, contactDist, takeCoreShape, aIndices, bIndices, aPoints, bPoints, warmStartSize, output);
321 }
322 template<typename ConvexA, typename ConvexB >
323 PX_NOINLINE GjkStatus gjkPenetration(const ConvexA& a, const ConvexB& b, const aos::Vec3VArg initialSearchDir, const aos::FloatVArg contactDist, const bool takeCoreShape,
324 aos::Vec3V* PX_RESTRICT aPoints, aos::Vec3V* PX_RESTRICT bPoints, PxU8& warmStartSize,
325 GjkOutput& output)
326 {
327 PxU8 aIndices[4], bIndices[4];
328 return gjkPenetration(a, b, initialSearchDir, contactDist, takeCoreShape, aIndices, bIndices, aPoints, bPoints, warmStartSize, output);
329 }
330}//Gu
331
332}//physx
333
334#endif
#define PX_RESTRICT
Definition PxPreprocessor.h:355
#define PX_NOINLINE
Definition PxPreprocessor.h:346
#define PX_FORCE_INLINE
Definition PxPreprocessor.h:335
GLM_FUNC_DECL GLM_CONSTEXPR genType zero()
Definition constants.inl:6
Sorts an array of objects in ascending order, assuming that the predicate implements the < operator:
Definition PxBoxController.h:39