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