Jolt Physics
A multi core friendly Game Physics Engine
Loading...
Searching...
No Matches
GJKClosestPoint.h
Go to the documentation of this file.
1// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
11
12//#define JPH_GJK_DEBUG
13#ifdef JPH_GJK_DEBUG
16#endif
17
19
23{
24private:
36 template <bool LastPointPartOfClosestFeature>
37 bool GetClosest(float inPrevVLenSq, Vec3 &outV, float &outVLenSq, uint32 &outSet) const
38 {
39#ifdef JPH_GJK_DEBUG
40 for (int i = 0; i < mNumPoints; ++i)
41 Trace("y[%d] = [%s], |y[%d]| = %g", i, ConvertToString(mY[i]).c_str(), i, (double)mY[i].Length());
42#endif
43
44 uint32 set;
45 Vec3 v;
46
47 switch (mNumPoints)
48 {
49 case 1:
50 // Single point
51 set = 0b0001;
52 v = mY[0];
53 break;
54
55 case 2:
56 // Line segment
57 v = ClosestPoint::GetClosestPointOnLine(mY[0], mY[1], set);
58 break;
59
60 case 3:
61 // Triangle
62 v = ClosestPoint::GetClosestPointOnTriangle<LastPointPartOfClosestFeature>(mY[0], mY[1], mY[2], set);
63 break;
64
65 case 4:
66 // Tetrahedron
67 v = ClosestPoint::GetClosestPointOnTetrahedron<LastPointPartOfClosestFeature>(mY[0], mY[1], mY[2], mY[3], set);
68 break;
69
70 default:
71 JPH_ASSERT(false);
72 return false;
73 }
74
75#ifdef JPH_GJK_DEBUG
76 Trace("GetClosest: set = 0b%s, v = [%s], |v| = %g", NibbleToBinary(set), ConvertToString(v).c_str(), (double)v.Length());
77#endif
78
79 float v_len_sq = v.LengthSq();
80 if (v_len_sq < inPrevVLenSq) // Note, comparison order important: If v_len_sq is NaN then this expression will be false so we will return false
81 {
82 // Return closest point
83 outV = v;
84 outVLenSq = v_len_sq;
85 outSet = set;
86 return true;
87 }
88
89 // No better match found
90#ifdef JPH_GJK_DEBUG
91 Trace("New closer point is further away, failed to converge");
92#endif
93 return false;
94 }
95
96 // Get max(|Y_0|^2 .. |Y_n|^2)
97 float GetMaxYLengthSq() const
98 {
99 float y_len_sq = mY[0].LengthSq();
100 for (int i = 1; i < mNumPoints; ++i)
101 y_len_sq = max(y_len_sq, mY[i].LengthSq());
102 return y_len_sq;
103 }
104
105 // Remove points that are not in the set, only updates mY
106 void UpdatePointSetY(uint32 inSet)
107 {
108 int num_points = 0;
109 for (int i = 0; i < mNumPoints; ++i)
110 if ((inSet & (1 << i)) != 0)
111 {
112 mY[num_points] = mY[i];
113 ++num_points;
114 }
115 mNumPoints = num_points;
116 }
117
118 // Remove points that are not in the set, only updates mP
119 void UpdatePointSetP(uint32 inSet)
120 {
121 int num_points = 0;
122 for (int i = 0; i < mNumPoints; ++i)
123 if ((inSet & (1 << i)) != 0)
124 {
125 mP[num_points] = mP[i];
126 ++num_points;
127 }
128 mNumPoints = num_points;
129 }
130
131 // Remove points that are not in the set, only updates mP and mQ
132 void UpdatePointSetPQ(uint32 inSet)
133 {
134 int num_points = 0;
135 for (int i = 0; i < mNumPoints; ++i)
136 if ((inSet & (1 << i)) != 0)
137 {
138 mP[num_points] = mP[i];
139 mQ[num_points] = mQ[i];
140 ++num_points;
141 }
142 mNumPoints = num_points;
143 }
144
145 // Remove points that are not in the set, updates mY, mP and mQ
146 void UpdatePointSetYPQ(uint32 inSet)
147 {
148 int num_points = 0;
149 for (int i = 0; i < mNumPoints; ++i)
150 if ((inSet & (1 << i)) != 0)
151 {
152 mY[num_points] = mY[i];
153 mP[num_points] = mP[i];
154 mQ[num_points] = mQ[i];
155 ++num_points;
156 }
157 mNumPoints = num_points;
158 }
159
160 // Calculate closest points on A and B
161 void CalculatePointAAndB(Vec3 &outPointA, Vec3 &outPointB) const
162 {
163 switch (mNumPoints)
164 {
165 case 1:
166 outPointA = mP[0];
167 outPointB = mQ[0];
168 break;
169
170 case 2:
171 {
172 float u, v;
173 ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], u, v);
174 outPointA = u * mP[0] + v * mP[1];
175 outPointB = u * mQ[0] + v * mQ[1];
176 }
177 break;
178
179 case 3:
180 {
181 float u, v, w;
182 ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], mY[2], u, v, w);
183 outPointA = u * mP[0] + v * mP[1] + w * mP[2];
184 outPointB = u * mQ[0] + v * mQ[1] + w * mQ[2];
185 }
186 break;
187
188 case 4:
189 #ifdef JPH_DEBUG
190 memset(&outPointA, 0xcd, sizeof(outPointA));
191 memset(&outPointB, 0xcd, sizeof(outPointB));
192 #endif
193 break;
194 }
195 }
196
197public:
207 template <typename A, typename B>
208 bool Intersects(const A &inA, const B &inB, float inTolerance, Vec3 &ioV)
209 {
210 float tolerance_sq = Square(inTolerance);
211
212 // Reset state
213 mNumPoints = 0;
214
215#ifdef JPH_GJK_DEBUG
216 for (int i = 0; i < 4; ++i)
217 mY[i] = Vec3::sZero();
218#endif
219
220 // Previous length^2 of v
221 float prev_v_len_sq = FLT_MAX;
222
223 for (;;)
224 {
225#ifdef JPH_GJK_DEBUG
226 Trace("v = [%s], num_points = %d", ConvertToString(ioV).c_str(), mNumPoints);
227#endif
228
229 // Get support points for shape A and B in the direction of v
230 Vec3 p = inA.GetSupport(ioV);
231 Vec3 q = inB.GetSupport(-ioV);
232
233 // Get support point of the minkowski sum A - B of v
234 Vec3 w = p - q;
235
236 // If the support point sA-B(v) is in the opposite direction as v, then we have found a separating axis and there is no intersection
237 if (ioV.Dot(w) < 0.0f)
238 {
239 // Separating axis found
240#ifdef JPH_GJK_DEBUG
241 Trace("Separating axis");
242#endif
243 return false;
244 }
245
246 // Store the point for later use
247 mY[mNumPoints] = w;
248 ++mNumPoints;
249
250#ifdef JPH_GJK_DEBUG
251 Trace("w = [%s]", ConvertToString(w).c_str());
252#endif
253
254 // Determine the new closest point
255 float v_len_sq; // Length^2 of v
256 uint32 set; // Set of points that form the new simplex
257 if (!GetClosest<true>(prev_v_len_sq, ioV, v_len_sq, set))
258 return false;
259
260 // If there are 4 points, the origin is inside the tetrahedron and we're done
261 if (set == 0xf)
262 {
263#ifdef JPH_GJK_DEBUG
264 Trace("Full simplex");
265#endif
266 ioV = Vec3::sZero();
267 return true;
268 }
269
270 // If v is very close to zero, we consider this a collision
271 if (v_len_sq <= tolerance_sq)
272 {
273#ifdef JPH_GJK_DEBUG
274 Trace("Distance zero");
275#endif
276 ioV = Vec3::sZero();
277 return true;
278 }
279
280 // If v is very small compared to the length of y, we also consider this a collision
281 if (v_len_sq <= FLT_EPSILON * GetMaxYLengthSq())
282 {
283#ifdef JPH_GJK_DEBUG
284 Trace("Machine precision reached");
285#endif
286 ioV = Vec3::sZero();
287 return true;
288 }
289
290 // The next separation axis to test is the negative of the closest point of the Minkowski sum to the origin
291 // Note: This must be done before terminating as converged since the separating axis is -v
292 ioV = -ioV;
293
294 // If the squared length of v is not changing enough, we've converged and there is no collision
295 JPH_ASSERT(prev_v_len_sq >= v_len_sq);
296 if (prev_v_len_sq - v_len_sq <= FLT_EPSILON * prev_v_len_sq)
297 {
298 // v is a separating axis
299#ifdef JPH_GJK_DEBUG
300 Trace("Converged");
301#endif
302 return false;
303 }
304 prev_v_len_sq = v_len_sq;
305
306 // Update the points of the simplex
307 UpdatePointSetY(set);
308 }
309 }
310
327 template <typename A, typename B>
328 float GetClosestPoints(const A &inA, const B &inB, float inTolerance, float inMaxDistSq, Vec3 &ioV, Vec3 &outPointA, Vec3 &outPointB)
329 {
330 float tolerance_sq = Square(inTolerance);
331
332 // Reset state
333 mNumPoints = 0;
334
335#ifdef JPH_GJK_DEBUG
336 // Generate the hull of the Minkowski difference for visualization
337 MinkowskiDifference diff(inA, inB);
338 mGeometry = DebugRenderer::sInstance->CreateTriangleGeometryForConvex([&diff](Vec3Arg inDirection) { return diff.GetSupport(inDirection); });
339
340 for (int i = 0; i < 4; ++i)
341 {
342 mY[i] = Vec3::sZero();
343 mP[i] = Vec3::sZero();
344 mQ[i] = Vec3::sZero();
345 }
346#endif
347
348 // Length^2 of v
349 float v_len_sq = ioV.LengthSq();
350
351 // Previous length^2 of v
352 float prev_v_len_sq = FLT_MAX;
353
354 for (;;)
355 {
356#ifdef JPH_GJK_DEBUG
357 Trace("v = [%s], num_points = %d", ConvertToString(ioV).c_str(), mNumPoints);
358#endif
359
360 // Get support points for shape A and B in the direction of v
361 Vec3 p = inA.GetSupport(ioV);
362 Vec3 q = inB.GetSupport(-ioV);
363
364 // Get support point of the minkowski sum A - B of v
365 Vec3 w = p - q;
366
367 float dot = ioV.Dot(w);
368
369#ifdef JPH_GJK_DEBUG
370 // Draw -ioV to show the closest point to the origin from the previous simplex
371 DebugRenderer::sInstance->DrawArrow(mOffset, mOffset - ioV, Color::sOrange, 0.05f);
372
373 // Draw ioV to show where we're probing next
374 DebugRenderer::sInstance->DrawArrow(mOffset, mOffset + ioV, Color::sCyan, 0.05f);
375
376 // Draw w, the support point
377 DebugRenderer::sInstance->DrawArrow(mOffset, mOffset + w, Color::sGreen, 0.05f);
379
380 // Draw the simplex and the Minkowski difference around it
381 DrawState();
382#endif
383
384 // Test if we have a separation of more than inMaxDistSq, in which case we terminate early
385 if (dot < 0.0f && dot * dot > v_len_sq * inMaxDistSq)
386 {
387#ifdef JPH_GJK_DEBUG
388 Trace("Distance bigger than max");
389#endif
390#ifdef JPH_DEBUG
391 memset(&outPointA, 0xcd, sizeof(outPointA));
392 memset(&outPointB, 0xcd, sizeof(outPointB));
393#endif
394 return FLT_MAX;
395 }
396
397 // Store the point for later use
398 mY[mNumPoints] = w;
399 mP[mNumPoints] = p;
400 mQ[mNumPoints] = q;
401 ++mNumPoints;
402
403#ifdef JPH_GJK_DEBUG
404 Trace("w = [%s]", ConvertToString(w).c_str());
405#endif
406
407 uint32 set;
408 if (!GetClosest<true>(prev_v_len_sq, ioV, v_len_sq, set))
409 {
410 --mNumPoints; // Undo add last point
411 break;
412 }
413
414 // If there are 4 points, the origin is inside the tetrahedron and we're done
415 if (set == 0xf)
416 {
417#ifdef JPH_GJK_DEBUG
418 Trace("Full simplex");
419#endif
420 ioV = Vec3::sZero();
421 v_len_sq = 0.0f;
422 break;
423 }
424
425 // Update the points of the simplex
426 UpdatePointSetYPQ(set);
427
428 // If v is very close to zero, we consider this a collision
429 if (v_len_sq <= tolerance_sq)
430 {
431#ifdef JPH_GJK_DEBUG
432 Trace("Distance zero");
433#endif
434 ioV = Vec3::sZero();
435 v_len_sq = 0.0f;
436 break;
437 }
438
439 // If v is very small compared to the length of y, we also consider this a collision
440#ifdef JPH_GJK_DEBUG
441 Trace("Check v small compared to y: %g <= %g", (double)v_len_sq, (double)(FLT_EPSILON * GetMaxYLengthSq()));
442#endif
443 if (v_len_sq <= FLT_EPSILON * GetMaxYLengthSq())
444 {
445#ifdef JPH_GJK_DEBUG
446 Trace("Machine precision reached");
447#endif
448 ioV = Vec3::sZero();
449 v_len_sq = 0.0f;
450 break;
451 }
452
453 // The next separation axis to test is the negative of the closest point of the Minkowski sum to the origin
454 // Note: This must be done before terminating as converged since the separating axis is -v
455 ioV = -ioV;
456
457 // If the squared length of v is not changing enough, we've converged and there is no collision
458#ifdef JPH_GJK_DEBUG
459 Trace("Check v not changing enough: %g <= %g", (double)(prev_v_len_sq - v_len_sq), (double)(FLT_EPSILON * prev_v_len_sq));
460#endif
461 JPH_ASSERT(prev_v_len_sq >= v_len_sq);
462 if (prev_v_len_sq - v_len_sq <= FLT_EPSILON * prev_v_len_sq)
463 {
464 // v is a separating axis
465#ifdef JPH_GJK_DEBUG
466 Trace("Converged");
467#endif
468 break;
469 }
470 prev_v_len_sq = v_len_sq;
471 }
472
473 // Get the closest points
474 CalculatePointAAndB(outPointA, outPointB);
475
476#ifdef JPH_GJK_DEBUG
477 Trace("Return: v = [%s], |v| = %g", ConvertToString(ioV).c_str(), (double)ioV.Length());
478
479 // Draw -ioV to show the closest point to the origin from the previous simplex
480 DebugRenderer::sInstance->DrawArrow(mOffset, mOffset - ioV, Color::sOrange, 0.05f);
481
482 // Draw the closest points
483 DebugRenderer::sInstance->DrawMarker(mOffset + outPointA, Color::sGreen, 1.0f);
484 DebugRenderer::sInstance->DrawMarker(mOffset + outPointB, Color::sPurple, 1.0f);
485
486 // Draw the simplex and the Minkowski difference around it
487 DrawState();
488#endif
489
490 JPH_ASSERT(ioV.LengthSq() == v_len_sq);
491 return v_len_sq;
492 }
493
496 void GetClosestPointsSimplex(Vec3 *outY, Vec3 *outP, Vec3 *outQ, uint &outNumPoints) const
497 {
498 uint size = sizeof(Vec3) * mNumPoints;
499 memcpy(outY, mY, size);
500 memcpy(outP, mP, size);
501 memcpy(outQ, mQ, size);
502 outNumPoints = mNumPoints;
503 }
504
516 template <typename A>
517 bool CastRay(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inTolerance, const A &inA, float &ioLambda)
518 {
519 float tolerance_sq = Square(inTolerance);
520
521 // Reset state
522 mNumPoints = 0;
523
524 float lambda = 0.0f;
525 Vec3 x = inRayOrigin;
526 Vec3 v = x - inA.GetSupport(Vec3::sZero());
527 float v_len_sq = FLT_MAX;
528 bool allow_restart = false;
529
530 for (;;)
531 {
532#ifdef JPH_GJK_DEBUG
533 Trace("v = [%s], num_points = %d", ConvertToString(v).c_str(), mNumPoints);
534#endif
535
536 // Get new support point
537 Vec3 p = inA.GetSupport(v);
538 Vec3 w = x - p;
539
540#ifdef JPH_GJK_DEBUG
541 Trace("w = [%s]", ConvertToString(w).c_str());
542#endif
543
544 float v_dot_w = v.Dot(w);
545#ifdef JPH_GJK_DEBUG
546 Trace("v . w = %g", (double)v_dot_w);
547#endif
548 if (v_dot_w > 0.0f)
549 {
550 // If ray and normal are in the same direction, we've passed A and there's no collision
551 float v_dot_r = v.Dot(inRayDirection);
552#ifdef JPH_GJK_DEBUG
553 Trace("v . r = %g", (double)v_dot_r);
554#endif
555 if (v_dot_r >= 0.0f)
556 return false;
557
558 // Update the lower bound for lambda
559 float delta = v_dot_w / v_dot_r;
560 float old_lambda = lambda;
561 lambda -= delta;
562#ifdef JPH_GJK_DEBUG
563 Trace("lambda = %g, delta = %g", (double)lambda, (double)delta);
564#endif
565
566 // If lambda didn't change, we cannot converge any further and we assume a hit
567 if (old_lambda == lambda)
568 break;
569
570 // If lambda is bigger or equal than max, we don't have a hit
571 if (lambda >= ioLambda)
572 return false;
573
574 // Update x to new closest point on the ray
575 x = inRayOrigin + lambda * inRayDirection;
576
577 // We've shifted x, so reset v_len_sq so that it is not used as early out for GetClosest
578 v_len_sq = FLT_MAX;
579
580 // We allow rebuilding the simplex once after x changes because the simplex was built
581 // for another x and numerical round off builds up as you keep adding points to an
582 // existing simplex
583 allow_restart = true;
584 }
585
586 // Add p to set P: P = P U {p}
587 mP[mNumPoints] = p;
588 ++mNumPoints;
589
590 // Calculate Y = {x} - P
591 for (int i = 0; i < mNumPoints; ++i)
592 mY[i] = x - mP[i];
593
594 // Determine the new closest point from Y to origin
595 uint32 set; // Set of points that form the new simplex
596 if (!GetClosest<false>(v_len_sq, v, v_len_sq, set))
597 {
598#ifdef JPH_GJK_DEBUG
599 Trace("Failed to converge");
600#endif
601
602 // Only allow 1 restart, if we still can't get a closest point
603 // we're so close that we return this as a hit
604 if (!allow_restart)
605 break;
606
607 // If we fail to converge, we start again with the last point as simplex
608#ifdef JPH_GJK_DEBUG
609 Trace("Restarting");
610#endif
611 allow_restart = false;
612 mP[0] = p;
613 mNumPoints = 1;
614 v = x - p;
615 v_len_sq = FLT_MAX;
616 continue;
617 }
618 else if (set == 0xf)
619 {
620#ifdef JPH_GJK_DEBUG
621 Trace("Full simplex");
622#endif
623
624 // We're inside the tetrahedron, we have a hit (verify that length of v is 0)
625 JPH_ASSERT(v_len_sq == 0.0f);
626 break;
627 }
628
629 // Update the points P to form the new simplex
630 // Note: We're not updating Y as Y will shift with x so we have to calculate it every iteration
631 UpdatePointSetP(set);
632
633 // Check if x is close enough to inA
634 if (v_len_sq <= tolerance_sq)
635 {
636#ifdef JPH_GJK_DEBUG
637 Trace("Converged");
638#endif
639 break;
640 }
641 }
642
643 // Store hit fraction
644 ioLambda = lambda;
645 return true;
646 }
647
658 template <typename A, typename B>
659 bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inTolerance, const A &inA, const B &inB, float &ioLambda)
660 {
661 // Transform the shape to be cast to the starting position
662 TransformedConvexObject transformed_a(inStart, inA);
663
664 // Calculate the minkowski difference inB - inA
665 // inA is moving, so we need to add the back side of inB to the front side of inA
666 MinkowskiDifference difference(inB, transformed_a);
667
668 // Do a raycast against the Minkowski difference
669 return CastRay(Vec3::sZero(), inDirection, inTolerance, difference, ioLambda);
670 }
671
689 template <typename A, typename B>
690 bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inTolerance, const A &inA, const B &inB, float inConvexRadiusA, float inConvexRadiusB, float &ioLambda, Vec3 &outPointA, Vec3 &outPointB, Vec3 &outSeparatingAxis)
691 {
692 float tolerance_sq = Square(inTolerance);
693
694 // Calculate how close A and B (without their convex radius) need to be to each other in order for us to consider this a collision
695 float sum_convex_radius = inConvexRadiusA + inConvexRadiusB;
696
697 // Transform the shape to be cast to the starting position
698 TransformedConvexObject transformed_a(inStart, inA);
699
700 // Reset state
701 mNumPoints = 0;
702
703 float lambda = 0.0f;
704 Vec3 x = Vec3::sZero(); // Since A is already transformed we can start the cast from zero
705 Vec3 v = -inB.GetSupport(Vec3::sZero()) + transformed_a.GetSupport(Vec3::sZero()); // See CastRay: v = x - inA.GetSupport(Vec3::sZero()) where inA is the Minkowski difference inB - transformed_a (see CastShape above) and x is zero
706 float v_len_sq = FLT_MAX;
707 bool allow_restart = false;
708
709 // Keeps track of separating axis of the previous iteration.
710 // Initialized at zero as we don't know if our first v is actually a separating axis.
711 Vec3 prev_v = Vec3::sZero();
712
713 for (;;)
714 {
715#ifdef JPH_GJK_DEBUG
716 Trace("v = [%s], num_points = %d", ConvertToString(v).c_str(), mNumPoints);
717#endif
718
719 // Calculate the minkowski difference inB - inA
720 // inA is moving, so we need to add the back side of inB to the front side of inA
721 // Keep the support points on A and B separate so that in the end we can calculate a contact point
722 Vec3 p = transformed_a.GetSupport(-v);
723 Vec3 q = inB.GetSupport(v);
724 Vec3 w = x - (q - p);
725
726#ifdef JPH_GJK_DEBUG
727 Trace("w = [%s]", ConvertToString(w).c_str());
728#endif
729
730 // Difference from article to this code:
731 // We did not include the convex radius in p and q in order to be able to calculate a good separating axis at the end of the algorithm.
732 // However when moving forward along inDirection we do need to take this into account so that we keep A and B separated by the sum of their convex radii.
733 // From p we have to subtract: inConvexRadiusA * v / |v|
734 // To q we have to add: inConvexRadiusB * v / |v|
735 // This means that to w we have to add: -(inConvexRadiusA + inConvexRadiusB) * v / |v|
736 // So to v . w we have to add: v . (-(inConvexRadiusA + inConvexRadiusB) * v / |v|) = -(inConvexRadiusA + inConvexRadiusB) * |v|
737 float v_dot_w = v.Dot(w) - sum_convex_radius * v.Length();
738#ifdef JPH_GJK_DEBUG
739 Trace("v . w = %g", (double)v_dot_w);
740#endif
741 if (v_dot_w > 0.0f)
742 {
743 // If ray and normal are in the same direction, we've passed A and there's no collision
744 float v_dot_r = v.Dot(inDirection);
745#ifdef JPH_GJK_DEBUG
746 Trace("v . r = %g", (double)v_dot_r);
747#endif
748 if (v_dot_r >= 0.0f)
749 return false;
750
751 // Update the lower bound for lambda
752 float delta = v_dot_w / v_dot_r;
753 float old_lambda = lambda;
754 lambda -= delta;
755#ifdef JPH_GJK_DEBUG
756 Trace("lambda = %g, delta = %g", (double)lambda, (double)delta);
757#endif
758
759 // If lambda didn't change, we cannot converge any further and we assume a hit
760 if (old_lambda == lambda)
761 break;
762
763 // If lambda is bigger or equal than max, we don't have a hit
764 if (lambda >= ioLambda)
765 return false;
766
767 // Update x to new closest point on the ray
768 x = lambda * inDirection;
769
770 // We've shifted x, so reset v_len_sq so that it is not used as early out when GetClosest returns false
771 v_len_sq = FLT_MAX;
772
773 // Now that we've moved, we know that A and B are not intersecting at lambda = 0, so we can update our tolerance to stop iterating
774 // as soon as A and B are inConvexRadiusA + inConvexRadiusB apart
775 tolerance_sq = Square(inTolerance + sum_convex_radius);
776
777 // We allow rebuilding the simplex once after x changes because the simplex was built
778 // for another x and numerical round off builds up as you keep adding points to an
779 // existing simplex
780 allow_restart = true;
781 }
782
783 // Add p to set P, q to set Q: P = P U {p}, Q = Q U {q}
784 mP[mNumPoints] = p;
785 mQ[mNumPoints] = q;
786 ++mNumPoints;
787
788 // Calculate Y = {x} - (Q - P)
789 for (int i = 0; i < mNumPoints; ++i)
790 mY[i] = x - (mQ[i] - mP[i]);
791
792 // Determine the new closest point from Y to origin
793 uint32 set; // Set of points that form the new simplex
794 if (!GetClosest<false>(v_len_sq, v, v_len_sq, set))
795 {
796#ifdef JPH_GJK_DEBUG
797 Trace("Failed to converge");
798#endif
799
800 // Only allow 1 restart, if we still can't get a closest point
801 // we're so close that we return this as a hit
802 if (!allow_restart)
803 break;
804
805 // If we fail to converge, we start again with the last point as simplex
806#ifdef JPH_GJK_DEBUG
807 Trace("Restarting");
808#endif
809 allow_restart = false;
810 mP[0] = p;
811 mQ[0] = q;
812 mNumPoints = 1;
813 v = x - q;
814 v_len_sq = FLT_MAX;
815 continue;
816 }
817 else if (set == 0xf)
818 {
819#ifdef JPH_GJK_DEBUG
820 Trace("Full simplex");
821#endif
822
823 // We're inside the tetrahedron, we have a hit (verify that length of v is 0)
824 JPH_ASSERT(v_len_sq == 0.0f);
825 break;
826 }
827
828 // Update the points P and Q to form the new simplex
829 // Note: We're not updating Y as Y will shift with x so we have to calculate it every iteration
830 UpdatePointSetPQ(set);
831
832 // Check if A and B are touching according to our tolerance
833 if (v_len_sq <= tolerance_sq)
834 {
835#ifdef JPH_GJK_DEBUG
836 Trace("Converged");
837#endif
838 break;
839 }
840
841 // Store our v to return as separating axis
842 prev_v = v;
843 }
844
845 // Calculate Y = {x} - (Q - P) again so we can calculate the contact points
846 for (int i = 0; i < mNumPoints; ++i)
847 mY[i] = x - (mQ[i] - mP[i]);
848
849 // Calculate the offset we need to apply to A and B to correct for the convex radius
850 Vec3 normalized_v = v.NormalizedOr(Vec3::sZero());
851 Vec3 convex_radius_a = inConvexRadiusA * normalized_v;
852 Vec3 convex_radius_b = inConvexRadiusB * normalized_v;
853
854 // Get the contact point
855 // Note that A and B will coincide when lambda > 0. In this case we calculate only B as it is more accurate as it contains less terms.
856 switch (mNumPoints)
857 {
858 case 1:
859 outPointB = mQ[0] + convex_radius_b;
860 outPointA = lambda > 0.0f? outPointB : mP[0] - convex_radius_a;
861 break;
862
863 case 2:
864 {
865 float bu, bv;
866 ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], bu, bv);
867 outPointB = bu * mQ[0] + bv * mQ[1] + convex_radius_b;
868 outPointA = lambda > 0.0f? outPointB : bu * mP[0] + bv * mP[1] - convex_radius_a;
869 }
870 break;
871
872 case 3:
873 case 4: // A full simplex, we can't properly determine a contact point! As contact point we take the closest point of the previous iteration.
874 {
875 float bu, bv, bw;
876 ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], mY[2], bu, bv, bw);
877 outPointB = bu * mQ[0] + bv * mQ[1] + bw * mQ[2] + convex_radius_b;
878 outPointA = lambda > 0.0f? outPointB : bu * mP[0] + bv * mP[1] + bw * mP[2] - convex_radius_a;
879 }
880 break;
881 }
882
883 // Store separating axis, in case we have a convex radius we can just return v,
884 // otherwise v will be very small and we resort to returning previous v as an approximation.
885 outSeparatingAxis = sum_convex_radius > 0.0f? -v : -prev_v;
886
887 // Store hit fraction
888 ioLambda = lambda;
889 return true;
890 }
891
892private:
893#ifdef JPH_GJK_DEBUG
895 void DrawState()
896 {
897 RMat44 origin = RMat44::sTranslation(mOffset);
898
899 // Draw origin
901
902 // Draw the hull
903 DebugRenderer::sInstance->DrawGeometry(origin, mGeometry->mBounds.Transformed(origin), mGeometry->mBounds.GetExtent().LengthSq(), Color::sYellow, mGeometry);
904
905 // Draw Y
906 for (int i = 0; i < mNumPoints; ++i)
907 {
908 // Draw support point
909 RVec3 y_i = origin * mY[i];
911 for (int j = i + 1; j < mNumPoints; ++j)
912 {
913 // Draw edge
914 RVec3 y_j = origin * mY[j];
916 for (int k = j + 1; k < mNumPoints; ++k)
917 {
918 // Make sure triangle faces the origin
919 RVec3 y_k = origin * mY[k];
920 RVec3 center = (y_i + y_j + y_k) / Real(3);
921 RVec3 normal = (y_j - y_i).Cross(y_k - y_i);
922 if (normal.Dot(center) < Real(0))
923 DebugRenderer::sInstance->DrawTriangle(y_i, y_j, y_k, Color::sLightGrey);
924 else
926 }
927 }
928 }
929
930 // Offset to the right
931 mOffset += Vec3(mGeometry->mBounds.GetSize().GetX() + 2.0f, 0, 0);
932 }
933#endif // JPH_GJK_DEBUG
934
935 Vec3 mY[4];
936 Vec3 mP[4];
937 Vec3 mQ[4];
938 int mNumPoints = 0;
939
940#ifdef JPH_GJK_DEBUG
942 RVec3 mOffset = RVec3::sZero();
943#endif
944};
945
unsigned int uint
Definition: Core.h:452
#define JPH_NAMESPACE_END
Definition: Core.h:378
std::uint32_t uint32
Definition: Core.h:455
#define JPH_NAMESPACE_BEGIN
Definition: Core.h:372
TraceFunction Trace
Definition: IssueReporting.cpp:18
#define JPH_ASSERT(...)
Definition: IssueReporting.h:33
JPH_INLINE constexpr T Square(T inV)
Square a value.
Definition: Math.h:52
float Real
Definition: Real.h:27
const char * NibbleToBinary(uint32 inNibble)
Converts the lower 4 bits of inNibble to a string that represents the number in binary format.
Definition: StringTools.cpp:95
String ConvertToString(const T &inValue)
Convert type to string.
Definition: StringTools.h:15
static const Color sPurple
Definition: Color.h:61
static const Color sGreen
Definition: Color.h:57
static const Color sLightGrey
Definition: Color.h:66
static const Color sOrange
Definition: Color.h:63
static const Color sCyan
Definition: Color.h:62
static const Color sRed
Definition: Color.h:55
static const Color sYellow
Definition: Color.h:60
GeometryRef CreateTriangleGeometryForConvex(SupportFunction inGetSupport)
Definition: DebugRenderer.cpp:688
void DrawMarker(RVec3Arg inPosition, ColorArg inColor, float inSize)
Draw a marker on a position.
Definition: DebugRenderer.cpp:172
void DrawCoordinateSystem(RMat44Arg inTransform, float inSize=1.0f)
Draw coordinate system (3 arrows, x = red, y = green, z = blue)
Definition: DebugRenderer.cpp:206
virtual void DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode=ECullMode::CullBackFace, ECastShadow inCastShadow=ECastShadow::On, EDrawMode inDrawMode=EDrawMode::Solid)=0
static DebugRenderer * sInstance
Singleton instance.
Definition: DebugRenderer.h:168
virtual void DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor)=0
Draw line.
void DrawArrow(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor, float inSize)
Draw an arrow.
Definition: DebugRenderer.cpp:184
virtual void DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow=ECastShadow::Off)=0
Draw a single back face culled triangle.
Definition: GJKClosestPoint.h:23
bool Intersects(const A &inA, const B &inB, float inTolerance, Vec3 &ioV)
Definition: GJKClosestPoint.h:208
bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inTolerance, const A &inA, const B &inB, float &ioLambda)
Definition: GJKClosestPoint.h:659
void GetClosestPointsSimplex(Vec3 *outY, Vec3 *outP, Vec3 *outQ, uint &outNumPoints) const
Definition: GJKClosestPoint.h:496
bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inTolerance, const A &inA, const B &inB, float inConvexRadiusA, float inConvexRadiusB, float &ioLambda, Vec3 &outPointA, Vec3 &outPointB, Vec3 &outSeparatingAxis)
Definition: GJKClosestPoint.h:690
float GetClosestPoints(const A &inA, const B &inB, float inTolerance, float inMaxDistSq, Vec3 &ioV, Vec3 &outPointA, Vec3 &outPointB)
Definition: GJKClosestPoint.h:328
bool CastRay(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inTolerance, const A &inA, float &ioLambda)
Definition: GJKClosestPoint.h:517
Holds a 4x4 matrix of floats, but supports also operations on the 3x3 upper left part of the matrix.
Definition: Mat44.h:13
static JPH_INLINE Mat44 sTranslation(Vec3Arg inV)
Get matrix that translates.
Definition: Mat44.inl:144
Class that makes another class non-copyable. Usage: Inherit from NonCopyable.
Definition: NonCopyable.h:11
Definition: Vec3.h:17
JPH_INLINE float Dot(Vec3Arg inV2) const
Dot product.
Definition: Vec3.inl:645
JPH_INLINE float Length() const
Length of vector.
Definition: Vec3.inl:677
JPH_INLINE Vec3 NormalizedOr(Vec3Arg inZeroValue) const
Normalize vector or return inZeroValue if the length of the vector is zero.
Definition: Vec3.inl:716
JPH_INLINE float LengthSq() const
Squared length of vector.
Definition: Vec3.inl:661
static JPH_INLINE Vec3 sZero()
Vector with all zeros.
Definition: Vec3.inl:107
bool GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, float &outU, float &outV)
Definition: ClosestPoint.h:18
Vec3 GetClosestPointOnLine(Vec3Arg inA, Vec3Arg inB, uint32 &outSet)
Definition: ClosestPoint.h:132
Structure that performs a Minkowski difference A - B.
Definition: ConvexSupport.h:67
Vec3 GetSupport(Vec3Arg inDirection) const
Calculate the support vector for this convex shape.
Definition: ConvexSupport.h:75
Definition: ConvexSupport.h:15
Vec3 GetSupport(Vec3Arg inDirection) const
Calculate the support vector for this convex shape.
Definition: ConvexSupport.h:24