UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
GJK.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4#include "Chaos/Box.h"
5#include "Chaos/Capsule.h"
6#include "Chaos/EPA.h"
7#include "Chaos/GJKShape.h"
9#include "Chaos/Simplex.h"
10#include "Chaos/Sphere.h"
11#include "Math/VectorRegister.h"
12#include "Chaos/VectorUtility.h"
14#include "Chaos/EPAVectorized.h"
15
16
17#include "ChaosCheck.h"
18#include "ChaosLog.h"
19
20// Enable some logging or ensures to trap issues in collision detection, usually related to degeneracies in GJK/EPA
21#define CHAOS_COLLISIONERROR_LOG_ENABLED ((!UE_BUILD_TEST && !UE_BUILD_SHIPPING) && 0)
22#define CHAOS_COLLISIONERROR_ENSURE_ENABLED ((!UE_BUILD_TEST && !UE_BUILD_SHIPPING) && 1)
23
24#if CHAOS_COLLISIONERROR_LOG_ENABLED
25#define CHAOS_COLLISIONERROR_CLOG(Condition, Fmt, ...) UE_CLOG((Condition), LogChaos, Error, Fmt, __VA_ARGS__)
26#else
27#define CHAOS_COLLISIONERROR_CLOG(Condition, Fmt, ...)
28#endif
29
30#if CHAOS_COLLISIONERROR_ENSURE_ENABLED
31#define CHAOS_COLLISIONERROR_ENSURE(X) ensure(X)
32#else
33#define CHAOS_COLLISIONERROR_ENSURE(X)
34#endif
35
36namespace Chaos
37{
38 /*
39 * To avoid code bloat from ~2k instances of templated GJKRaycast2ImplSimd, it was rewritten to operate on support function pointers and void* object pointers
40 * This saved around 4MB in .text section and provided a noticeable CPU performance increase on a bottom line hardware
41 */
43 {
45
46 const void* Geometry;
50
51 template<class T>
52 FGeomGJKHelperSIMD(const T& Geom)
53 : Geometry(&Geom), Margin(Geom.GetMarginf()), Radius(Geom.GetRadiusf()), Func(&SupportCoreSimd<T>)
54 {}
55
56 FRealSingle GetRadius() const { return Radius; }
57 FRealSingle GetMargin() const { return Margin; }
58 FRealSingle GetRadiusf() const { return Radius; }
59 FRealSingle GetMarginf() const { return Margin; }
60
64
65 VectorRegister4Float operator()(const VectorRegister4Float AToBRotation, const VectorRegister4Float BToARotation, const VectorRegister4Float V) const { return SupportFunction(AToBRotation, BToARotation, V); }
71
72 private:
73 template<class T>
74 static VectorRegister4Float SupportCoreSimd(const void* Geom, FRealSingle InMargin, const VectorRegister4Float V)
75 {
76 return ((const T*)Geom)->SupportCoreSimd(V, InMargin);
77 }
78 };
79
85
86 template <typename T, EGJKTestSpace TestSpace>
88 {
89 static void Support(const FGeomGJKHelperSIMD& RESTRICT A, const FGeomGJKHelperSIMD& RESTRICT B, const T& SearchDir, const T& AToBRotation, const T& BToARotation, T& OutSupportA, T& OutSupportB)
90 {
93 }
94 };
95
96 template <typename T>
98 {
99 static void Support(const FGeomGJKHelperSIMD& RESTRICT A, const FGeomGJKHelperSIMD& RESTRICT B, const T& SearchDir, const T& BToARotation, const T& AToBRotation, T& OutSupportA, T& OutSupportB)
100 {
103 }
104 };
105
106 template <EGJKTestSpace TestSpace>
124
125 template <>
137
138 // Check the GJK iteration count against a limit to prevent infinite loops. We should never really hit the limit but
139 // we are seeing it happen in some cases and more often on some platforms than others so for now we have a warning when it hits.
140 // @todo(chaos): track this issue down (see UE-156361)
141 template<typename ConvexTypeA, typename ConvexTypeB>
142 inline bool CheckGJKIterationLimit(const int32 NumIterations, const ConvexTypeA& A, const ConvexTypeB& B)
143 {
144 const int32 MaxIterations = 32;
145 const bool bLimitExceeded = (NumIterations >= MaxIterations);
146
147 CHAOS_COLLISIONERROR_CLOG(bLimitExceeded, TEXT("GJK hit iteration limit with shapes:\n A: %s\n B: %s"), *A.ToString(), *B.ToString());
149
150 return bLimitExceeded;
151 }
152
153
154 // Calculate the margins used for queries based on shape radius, shape margins and shape types
155 template <typename TGeometryA, typename TGeometryB, typename T>
157 {
158 // Margin selection logic: we only need a small margin for sweeps since we only move the sweeping object
159 // to the point where it just touches.
160 // Spheres and Capsules: always use the core shape and full "margin" because it represents the radius
161 // Sphere/Capsule versus OtherShape: no margin on other
162 // OtherShape versus OtherShape: use margin of the smaller shape, zero margin on the other
163 const T RadiusA = A.GetRadiusf();
164 const T RadiusB = B.GetRadiusf();
165 const bool bHasRadiusA = RadiusA > 0;
166 const bool bHasRadiusB = RadiusB > 0;
167
168 // The sweep margins if required. Only one can be non-zero (we keep the smaller one)
169 const T SweepMarginScale = 0.05f;
170 const bool bAIsSmallest = A.GetMarginf() < B.GetMarginf();
171 const T SweepMarginA = (bHasRadiusA || bHasRadiusB) ? 0.0f : (bAIsSmallest ? SweepMarginScale * A.GetMarginf() : 0.0f);
172 const T SweepMarginB = (bHasRadiusA || bHasRadiusB) ? 0.0f : (bAIsSmallest ? 0.0f : SweepMarginScale * B.GetMarginf());
173
174 // Net margin (note: both SweepMargins are zero if either Radius is non-zero, and only one SweepMargin can be non-zero)
177 }
178
189 template <typename T, typename TGeometryA, typename TGeometryB>
191 {
192 const FReal EpsilonScale = FMath::Max<FReal>(A.TGeometryA::BoundingBox().Extents().Max(), B.TGeometryB::BoundingBox().Extents().Max());
194 }
195
196
197
198 template <typename TGeometryA, typename TGeometryB>
200 {
201 const FReal EpsilonScale = FMath::Max<FReal>(A.TGeometryA::BoundingBox().Extents().Max(), B.TGeometryB::BoundingBox().Extents().Max());
203 }
204
206 {
208 V = VectorNormalizeSafe(V, MakeVectorRegisterFloatConstant(-1.f, 0.f, 0.f, 0.f));
209
211 bool bTerminate;
212 bool bNearZero = false;
213 int NumIterations = 0;
215
218 int32 NumVerts = 0;
219
224
225 EpsilonScale = FMath::Min<FRealSingle>(EpsilonScale, 1e5f);
226
230
232 do
233 {
234 if (!ensure(NumIterations++ < 32)) //todo: take this out
235 {
236 break; //if taking too long just stop. This should never happen
237 }
239 const VectorRegister4Float SupportASimd = A.SupportFunction(NegVSimd, ThicknessA);
244
246 {
247 return false;
248 }
249
251
253
256
257 //as simplices become degenerate we will stop making progress. This is a side-effect of precision, in that case take V as the current best approximation
258 //question: should we take previous v in case it's better?
262
263 if (!bTerminate)
264 {
266 }
267
268 } while (!bTerminate);
269
270 return bNearZero;
271 }
272
273 template <typename T>
288
301 template <typename TGeometryA, typename TGeometryB>
303 {
305
306 //ensure(FMath::Abs(VectorDot3Scalar(V, V) - 1.0f) < 1e-3f ); // Check that the vector is normalized, comment out for performance
307
310 int32 NumVerts = 0;
311 VectorRegister4Float Barycentric = VectorZero();
312
313 bool bTerminate;
314 bool bNearZero = false;
315 int NumIterations = 0;
321
322 const FRealSingle Inflation = ThicknessA + ThicknessB + 1e-3f;
324
327 do
328 {
329 if (!ensure(NumIterations++ < 32)) //todo: take this out
330 {
331 break; //if taking too long just stop. This should never happen
332 }
334 const VectorRegister4Float SupportA = A.SupportCoreSimd(NegV, ThicknessA);
335 const VectorRegister4Float VInB = V; // same space
336 const VectorRegister4Float SupportB = B.SupportCoreSimd(VInB, ThicknessB);
338
340
342 {
343 return false;
344 }
345
346 Simplex[NumVerts++] = W;
347
350
352
353 //as simplices become degenerate we will stop making progress. This is a side-effect of precision, in that case take V as the current best approximation
354 //question: should we take previous v in case it's better?
355 const bool bMadeProgress = static_cast<bool>(VectorMaskBits(VectorCompareGT(PrevDist2, NewDist2)));
357
359
360 if (!bTerminate)
361 {
363 }
364
365 } while (!bTerminate);
366
367 return bNearZero;
368 }
369
370
377 template<typename T>
379 {
380 public:
382 : NumVerts(0)
383 {}
384
385 // Clear the data - used to start a GJK search from the default search direction
386 void Reset()
387 {
388 NumVerts = 0;
389 }
390
391 // Save any data that was not directly updated while iterating in GJK
393 {
394 // We don't need to store the simplex vertex order because the indices are always
395 // sorted at the end of each iteration. We just need to know how many vertices we have.
396 NumVerts = InSimplexIDs.NumVerts;
397 }
398
399 // Recompute the Simplex and separating vector from the stored data at the current relative transform
400 // This aborts if we have no simplex data to restore or the origin is inside the simplex. Outputs must already
401 // have reasonable default values for running GJK without a warm-start.
402 void Restore(const TRigidTransform<T, 3>& BToATM, FSimplex& OutSimplexIDs, TVec3<T> OutSimplex[], TVec3<T>& OutV, T& OutDistance, const T Epsilon)
403 {
404 if (NumVerts > 0)
405 {
406 OutSimplexIDs.NumVerts = NumVerts;
407
408 for (int32 VertIndex = 0; VertIndex < NumVerts; ++VertIndex)
409 {
410 OutSimplexIDs.Idxs[VertIndex] = VertIndex;
411 OutSimplex[VertIndex] = As[VertIndex] - BToATM.TransformPositionNoScale(Bs[VertIndex]);
412 }
413
415 const T Distance = V.Size();
416
417 // If the origin is inside the simplex at the new transform, we need to abort the restore
418 // This is necessary to cover the very-small separation case where we use the normal
419 // calculated in the previous iteration in GJKL, but we have no way to restore that.
420 // Note: we have already written to the simplex but that's ok because we reset the vert count.
421 if (Distance > Epsilon)
422 {
423 OutV = V / Distance;
424 OutDistance = Distance;
425 }
426 else
427 {
428 OutSimplexIDs.NumVerts = 0;
429 }
430 }
431 }
432
433 void Restore2(const TRigidTransform<T, 3>& BToATM, int32& OutNumVerts, TVec3<T> OutSimplex[], TVec3<T>& OutV, T& OutDistance, const T Epsilon)
434 {
435 OutNumVerts = 0;
436
437 if (NumVerts > 0)
438 {
439 for (int32 VertIndex = 0; VertIndex < NumVerts; ++VertIndex)
440 {
441 OutSimplex[VertIndex] = As[VertIndex] - BToATM.TransformPositionNoScale(Bs[VertIndex]);
442 }
443
445 const T DistanceSq = V.SizeSquared();
446
447 // If the origin is inside the simplex at the new transform, we need to abort the restore
448 // This is necessary to cover the very-small separation case where we use the normal
449 // calculated in the previous iteration in GJKL, but we have no way to restore that.
450 // Note: we have already written to the simplex but that's ok because we reset the vert count.
451 if (DistanceSq > FMath::Square(Epsilon))
452 {
453 const T Distance = FMath::Sqrt(DistanceSq);
455 OutV = V / Distance;
456 OutDistance = Distance;
457 }
458 }
459 }
460
461
462 // Maximum number of vertices that a GJK simplex can have
463 static const int32 MaxSimplexVerts = 4;
464
465 // Simplex vertices on shape A, in A-local space
467
468 // Simplex vertices on shape B, in B-local space
470
471 // Barycentric coordinates of closest point to origin on the simplex
473
474 // Number of vertices in the simplex. Up to 4.
476 };
477
478 // GJK warm-start data at default numeric precision
480
482 {
483 typedef FVector(*SupportFunc)(const void* Geom, const FVec3& Direction, FReal* OutSupportDelta, int32& VertexIndex);
484
485 const void* Geometry;
488
489 template<class T>
490 FGeomGJKHelper(const T& Geom) : Geometry(&Geom), Func(&SupportCore<T>), Margin(Geom.GetMarginf()) {}
491
492 FVector SupportFunction(const FVec3& V, int32& VertexIndex) const { return Func(Geometry, V, nullptr, VertexIndex); }
493 FVector SupportFunction(const FVec3& V, FReal* OutSupportDelta, int32& VertexIndex) const { return Func(Geometry, V, OutSupportDelta, VertexIndex); }
494
495 FVector SupportFunction(const FRotation3& AToBRotation, const FVec3& V, FReal* OutSupportDelta, int32& VertexIndex) const
496 {
497 const FVec3 VInB = AToBRotation * V;
498 return Func(Geometry, VInB, OutSupportDelta, VertexIndex);
499 }
500
501 FVector SupportFunction(const FRigidTransform3& BToATM, const FRotation3& AToBRotation, const FVec3& V, int32& VertexIndex) const
502 {
503 const FVector VInB = AToBRotation * V;
504 const FVector SupportBLocal = Func(Geometry, VInB, nullptr, VertexIndex);
505 return BToATM.TransformPositionNoScale(SupportBLocal);
506 }
507
508 FVector SupportFunction(const FRigidTransform3& BToATM, const FRotation3& AToBRotation, const FVec3& V, FReal* OutSupportDelta, int32& VertexIndex) const
509 {
510 const FVector VInB = AToBRotation * V;
511 const FVector SupportBLocal = Func(Geometry, VInB, OutSupportDelta, VertexIndex);
512 return BToATM.TransformPositionNoScale(SupportBLocal);
513 }
514
515 FReal GetMargin() const { return Margin; }
516 FReal GetMarginf() const { return Margin; }
517
518 private:
519 template<class T>
520 static FVector SupportCore(const void* Geom, const FVec3& Direction, FReal* OutSupportDelta, int32& VertexIndex)
521 {
522 const T* Geometry = (const T*)Geom;
523 return Geometry->SupportCore(Direction, Geometry->GetMarginf(), OutSupportDelta, VertexIndex);
524 }
525 };
526
557 template <typename T, typename TGeometryA, typename TGeometryB>
559 {
560 T SupportDeltaA = 0;
561 T SupportDeltaB = 0;
562 T MaxSupportDelta = 0;
563
566
567 // Return the support vertex in A-local space for a vector V in A-local space
568 auto SupportAFunc = [&A, &SupportDeltaA, &VertexIndexA](const TVec3<T>& V)
569 {
570 return A.SupportCore(V, A.GetMarginf(), &SupportDeltaA, VertexIndexA);
571 };
572
573 const TRotation<T, 3> AToBRotation = BToATM.GetRotation().Inverse();
574
575 // Return the support point on B, in B-local space, for a vector V in A-local space
576 auto SupportBFunc = [&B, &AToBRotation, &SupportDeltaB, &VertexIndexB](const TVec3<T>& V)
577 {
578 const TVec3<T> VInB = AToBRotation * V;
579 return B.SupportCore(VInB, B.GetMarginf(), &SupportDeltaB, VertexIndexB);
580 };
581
582 // V and Simplex are in A-local space
583 TVec3<T> V = TVec3<T>(-1, 0, 0);
584 TVec3<T> Simplex[4];
586 T Distance = FLT_MAX;
587
588 // If we have warm-start data, rebuild the simplex from the stored data
589 InOutSimplexData.Restore(BToATM, SimplexIDs, Simplex, V, Distance, Epsilon);
590
591 TVec3<T> Normal = -V; // Remember the last good normal (i.e. don't update it if separation goes less than Epsilon and we can no longer normalize)
592 bool bIsDegenerate = false; // True if GJK cannot make any more progress
593 bool bIsContact = false; // True if shapes are within Epsilon or overlapping - GJK cannot provide a solution
594 int32 NumIterations = 0;
595 const T ThicknessA = A.GetMarginf();
596 const T ThicknessB = B.GetMarginf();
597 const T SeparatedDistance = ThicknessA + ThicknessB + Epsilon;
598 while (!bIsContact && !bIsDegenerate)
599 {
600 // If taking too long just stop
601 ++NumIterations;
602 if (CheckGJKIterationLimit(NumIterations, A, B))
603 {
604 break;
605 }
606
607 const TVec3<T> NegV = -V;
609 const TVec3<T> SupportB = SupportBFunc(V);
610 const TVec3<T> SupportBInA = BToATM.TransformPositionNoScale(SupportB);
611 const TVec3<T> W = SupportA - SupportBInA;
613
614 // If we didn't move to at least ConvergedDistance or closer, assume we have reached a minimum
615 const T ConvergenceTolerance = 1.e-4f;
617 const T VW = TVec3<T>::DotProduct(V, W);
618
619 InOutSimplexData.As[SimplexIDs.NumVerts] = SupportA;
620 InOutSimplexData.Bs[SimplexIDs.NumVerts] = SupportB;
621 SimplexIDs[SimplexIDs.NumVerts] = SimplexIDs.NumVerts;
622 Simplex[SimplexIDs.NumVerts++] = W;
623
625 T NewDistance = V.Size();
626
627 // Are we overlapping or too close for GJK to get a good result?
628 bIsContact = (NewDistance < Epsilon);
629
630 // If we did not get closer in this iteration, we are in a degenerate situation
632
633 // If we are not too close, update the separating vector and keep iterating
634 // If we are too close we drop through to EPA, but if EPA determines the shapes are actually separated
635 // after all, we will wend up using the normal from the previous loop
636 if (!bIsContact)
637 {
638 V /= NewDistance;
639 Normal = -V;
640 }
642 }
643 // Save the warm-start data (not much to store since we updated most of it in the loop above)
645 if (bIsContact)
646 {
647 // We did not converge or we detected an overlap situation, so run EPA to get contact data
648 // Rebuild the simplex into a variable sized array - EPA can generate any number of vertices
651 VertsA.Reserve(8);
652 VertsB.Reserve(8);
653 for (int i = 0; i < SimplexIDs.NumVerts; ++i)
654 {
655 VertsA.Add(InOutSimplexData.As[i]);
656 VertsB.Add(BToATM.TransformPositionNoScale(InOutSimplexData.Bs[i]));
657 }
658
659 auto SupportBInAFunc = [&B, &BToATM, &AToBRotation, &SupportDeltaB, &VertexIndexB](const TVec3<T>& V)
660 {
661 const TVec3<T> VInB = AToBRotation * V;
662 const TVec3<T> SupportBLocal = B.SupportCore(VInB, B.GetMarginf(), &SupportDeltaB, VertexIndexB);
663 return BToATM.TransformPositionNoScale(SupportBLocal);
664 };
665
666 T Penetration;
669
670 switch (EPAResult)
671 {
673 // Possibly a solution but with unknown error. Just return the last EPA state.
674 // Fall through...
675 case EEPAResult::Ok:
676 // EPA has a solution - return it now
677 OutNormalA = MTD;
678 OutNormalB = BToATM.InverseTransformVectorNoScale(MTD);
679 OutPenetration = Penetration + ThicknessA + ThicknessB;
681 OutClosestB = BToATM.InverseTransformPositionNoScale(ClosestBInA - MTD * ThicknessB);
683 return true;
685 // The origin is outside the simplex. Must be a touching contact and EPA setup will have calculated the normal
686 // and penetration but we keep the position generated by GJK.
687 Normal = MTD;
688 Distance = -Penetration;
689 //UE_LOG(LogChaos, Warning, TEXT("EPA Touching Case"));
690 break;
692 // We hit a degenerate simplex condition and could not reach a solution so use whatever near touching point GJK came up with.
693 // The result from EPA under these circumstances is not usable.
694 //UE_LOG(LogChaos, Warning, TEXT("EPA Degenerate Case"));
695 break;
696 }
697 }
698
699 // @todo(chaos): handle the case where EPA hits a degenerate triangle in the simplex.
700 // Currently we return a touching contact with the last position and normal from GJK. We could run SAT instead.
701
702 // GJK converged or we have a touching contact
703 {
706 for (int i = 0; i < SimplexIDs.NumVerts; ++i)
707 {
708 ClosestA += InOutSimplexData.As[i] * InOutSimplexData.Barycentric[i];
709 ClosestB += InOutSimplexData.Bs[i] * InOutSimplexData.Barycentric[i];
710 }
711
713 OutNormalB = BToATM.InverseTransformVectorNoScale(Normal);
714
715 T Penetration = ThicknessA + ThicknessB - Distance;
716 OutPenetration = Penetration;
719
721
722 // @todo(chaos): we should pass back failure for the degenerate case so we can decide how to handle it externally.
723 return true;
724 }
725 }
726
727 // Same as GJKPenetrationWarmStartable but with an index-less algorithm
728 template <typename T, typename TGeometryA, typename TGeometryB>
730 {
731 T SupportDeltaA = 0;
732 T SupportDeltaB = 0;
733 T MaxSupportDelta = 0;
734
737
738 // Return the support vertex in A-local space for a vector V in A-local space
739 auto SupportAFunc = [&A, &SupportDeltaA, &VertexIndexA](const TVec3<T>& V)
740 {
741 return A.SupportCore(V, A.GetMargin(), &SupportDeltaA, VertexIndexA);
742 };
743
744 const TRotation<T, 3> AToBRotation = BToATM.GetRotation().Inverse();
745
746 // Return the support point on B, in B-local space, for a vector V in A-local space
747 auto SupportBFunc = [&B, &AToBRotation, &SupportDeltaB, &VertexIndexB](const TVec3<T>& V)
748 {
749 const TVec3<T> VInB = AToBRotation * V;
750 return B.SupportCore(VInB, B.GetMargin(), &SupportDeltaB, VertexIndexB);
751 };
752
753 // V and Simplex are in A-local space
754 TVec3<T> V = TVec3<T>(-1, 0, 0);
755 TVec3<T> Simplex[4];
756 int32 NumVerts = 0;
757 T Distance = FLT_MAX;
758
759 // If we have warm-start data, rebuild the simplex from the stored data
760 InOutSimplexData.Restore2(BToATM, NumVerts, Simplex, V, Distance, Epsilon);
761
762 TVec3<T> Normal = -V; // Remember the last good normal (i.e. don't update it if separation goes less than Epsilon and we can no longer normalize)
763 bool bIsResult = false; // True if GJK cannot make any more progress
764 bool bIsContact = false; // True if shapes are within Epsilon or overlapping - GJK cannot provide a solution
765 int32 NumIterations = 0;
766 const T ThicknessA = A.GetMargin();
767 const T ThicknessB = B.GetMargin();
768 while (!bIsContact && !bIsResult)
769 {
770 // If taking too long just stop
771 ++NumIterations;
772 if (CheckGJKIterationLimit(NumIterations, A, B))
773 {
774 break;
775 }
776
777 const TVec3<T> NegV = -V;
779 const TVec3<T> SupportB = SupportBFunc(V);
780 const TVec3<T> SupportBInA = BToATM.TransformPositionNoScale(SupportB);
781 const TVec3<T> W = SupportA - SupportBInA;
783
784 // If we didn't move to at least ConvergedDistance or closer, assume we have reached a minimum
785 const T ConvergenceTolerance = 1.e-4f;
787 const T VW = TVec3<T>::DotProduct(V, W);
788
791 Simplex[NumVerts++] = W;
792
794 T NewDistance = V.Size();
795
796 // Are we overlapping or too close for GJK to get a good result?
797 bIsContact = (NewDistance < Epsilon);
798
799 // If we did not get closer in this iteration, we are in a degenerate situation or we have the result
800 bIsResult = (NewDistance >= (Distance - Epsilon));
801
802 // If we are not too close, update the separating vector and keep iterating
803 // If we are too close we drop through to EPA, but if EPA determines the shapes are actually separated
804 // after all, we will end up using the normal from the previous loop
805 if (!bIsContact)
806 {
807 V /= NewDistance;
808 Normal = -V;
809 }
811 }
812
813 // Save the warm-start data (not much to store since we updated most of it in the loop above)
814 InOutSimplexData.NumVerts = NumVerts;
815
816 if (bIsContact)
817 {
818 // We did not converge or we detected an overlap situation, so run EPA to get contact data
819 // Rebuild the simplex into a variable sized array - EPA can generate any number of vertices
822 VertsA.Reserve(8);
823 VertsB.Reserve(8);
824 for (int i = 0; i < NumVerts; ++i)
825 {
826 VertsA.Add(InOutSimplexData.As[i]);
827 VertsB.Add(BToATM.TransformPositionNoScale(InOutSimplexData.Bs[i]));
828 }
829
830 auto SupportBInAFunc = [&B, &BToATM, &AToBRotation, &SupportDeltaB, &VertexIndexB](const TVec3<T>& V)
831 {
832 const TVec3<T> VInB = AToBRotation * V;
833 const TVec3<T> SupportBLocal = B.SupportCore(VInB, B.GetMargin(), &SupportDeltaB, VertexIndexB);
834 return BToATM.TransformPositionNoScale(SupportBLocal);
835 };
836
837 T Penetration;
840
841 switch (EPAResult)
842 {
844 // Possibly a solution but with unknown error. Just return the last EPA state.
845 // Fall through...
846 case EEPAResult::Ok:
847 // EPA has a solution - return it now
848 OutNormalA = MTD;
849 OutNormalB = BToATM.InverseTransformVectorNoScale(MTD);
850 OutPenetration = Penetration + ThicknessA + ThicknessB;
852 OutClosestB = BToATM.InverseTransformPositionNoScale(ClosestBInA - MTD * ThicknessB);
854 return true;
856 // The origin is outside the simplex. Must be a touching contact and EPA setup will have calculated the normal
857 // and penetration but we keep the position generated by GJK.
858 Normal = MTD;
859 Distance = -Penetration;
860 break;
862 // We hit a degenerate simplex condition and could not reach a solution so use whatever near touching point GJK came up with.
863 // The result from EPA under these circumstances is not usable.
864 //UE_LOG(LogChaos, Warning, TEXT("EPA Degenerate Case"));
865 break;
866 }
867 }
868
869 // @todo(chaos): handle the case where EPA hits a degenerate triangle in the simplex.
870 // Currently we return a touching contact with the last position and normal from GJK. We could run SAT instead.
871
872 // GJK converged or we have a touching contact
873 {
876 for (int i = 0; i < NumVerts; ++i)
877 {
878 ClosestA += InOutSimplexData.As[i] * InOutSimplexData.Barycentric[i];
879 ClosestB += InOutSimplexData.Bs[i] * InOutSimplexData.Barycentric[i];
880 }
881
883 OutNormalB = BToATM.InverseTransformVectorNoScale(Normal);
884
885 T Penetration = ThicknessA + ThicknessB - Distance;
886 OutPenetration = Penetration;
889
891
892 // @todo(chaos): we should pass back failure for the degenerate case so we can decide how to handle it externally.
893 return true;
894 }
895 }
896
917 template <typename T, typename TGeometryA, typename TGeometryB>
918 bool GJKPenetrationSameSpace(const TGeometryA& A, const TGeometryB& B, T& OutPenetration, TVec3<T>& OutClosestA, TVec3<T>& OutClosestB, TVec3<T>& OutNormal, int32& OutVertexA, int32& OutVertexB, T& OutMaxSupportDelta, const TVec3<T>& InitialDir, const T Epsilon = T(1.e-3), const T EPAEpsilon = T(1.e-2))
919 {
921 T SupportDeltaA = 0;
922 T SupportDeltaB = 0;
923 T MaxSupportDelta = 0;
924
927
928 // Return the support vertex in A-local space for a vector V in A-local space
929 auto SupportAFunc = [&A, &SupportDeltaA, &VertexIndexA](const TVec3<T>& V)
930 {
931 return A.SupportCore(V, A.GetMargin(), &SupportDeltaA, VertexIndexA);
932 };
933
934 // Return the support point on B, in B-local space, for a vector V in A-local space
935 auto SupportBFunc = [&B, &SupportDeltaB, &VertexIndexB](const TVec3<T>& V)
936 {
937 return B.SupportCore(V, B.GetMargin(), &SupportDeltaB, VertexIndexB);
938 };
939
940 // V and Simplex are in A-local space
942 TVec3<T> Simplex[4];
944 T Distance = FLT_MAX;
945
946 TVec3<T> Normal = -V; // Remember the last good normal (i.e. don't update it if separation goes less than Epsilon and we can no longer normalize)
947 bool bIsDegenerate = false; // True if GJK cannot make any more progress
948 bool bIsContact = false; // True if shapes are within Epsilon or overlapping - GJK cannot provide a solution
949 int32 NumIterations = 0;
950 const T ThicknessA = A.GetMargin();
951 const T ThicknessB = B.GetMargin();
952 const T SeparatedDistance = ThicknessA + ThicknessB + Epsilon;
953 while (!bIsContact && !bIsDegenerate)
954 {
955 // If taking too long just stop
956 ++NumIterations;
957 if (CheckGJKIterationLimit(NumIterations, A, B))
958 {
959 break;
960 }
961
962 const TVec3<T> NegV = -V;
964 const TVec3<T> SupportB = SupportBFunc(V);
965 const TVec3<T> W = SupportA - SupportB;
967
968 // If we didn't move to at least ConvergedDistance or closer, assume we have reached a minimum
969 const T ConvergenceTolerance = 1.e-4f;
971 const T VW = TVec3<T>::DotProduct(V, W);
972
973 SimplexData.As[SimplexIDs.NumVerts] = SupportA;
974 SimplexData.Bs[SimplexIDs.NumVerts] = SupportB;
975 SimplexIDs[SimplexIDs.NumVerts] = SimplexIDs.NumVerts;
976 Simplex[SimplexIDs.NumVerts++] = W;
977
979 T NewDistance = V.Size();
980
981 // Are we overlapping or too close for GJK to get a good result?
982 bIsContact = (NewDistance < Epsilon);
983
984 // If we did not get closer in this iteration, we are in a degenerate situation
986
987 // If we are not too close, update the separating vector and keep iterating
988 // If we are too close we drop through to EPA, but if EPA determines the shapes are actually separated
989 // after all, we will wend up using the normal from the previous loop
990 if (!bIsContact)
991 {
992 V /= NewDistance;
993 Normal = -V;
994 }
996 }
997
998 // Save the warm-start data (not much to store since we updated most of it in the loop above)
1000
1001 if (bIsContact)
1002 {
1003 // We did not converge or we detected an overlap situation, so run EPA to get contact data
1004 // Rebuild the simplex into a variable sized array - EPA can generate any mumber of vertices
1007 VertsA.Reserve(8);
1008 VertsB.Reserve(8);
1009 for (int i = 0; i < SimplexIDs.NumVerts; ++i)
1010 {
1011 VertsA.Add(SimplexData.As[i]);
1012 VertsB.Add(SimplexData.Bs[i]);
1013 }
1014
1015 T Penetration;
1018
1019 switch (EPAResult)
1020 {
1022 // Possibly a solution but with unknown error. Just return the last EPA state.
1023 // Fall through...
1024 case EEPAResult::Ok:
1025 // EPA has a solution - return it now
1026 OutNormal = MTD;
1027 OutPenetration = Penetration + ThicknessA + ThicknessB;
1031 return true;
1033 // The origin is outside the simplex. Must be a touching contact and EPA setup will have calculated the normal
1034 // and penetration but we keep the position generated by GJK.
1035 Normal = MTD;
1036 Distance = -Penetration;
1037 //UE_LOG(LogChaos, Warning, TEXT("EPA Touching Case"));
1038 break;
1040 // We hit a degenerate simplex condition and could not reach a solution so use whatever near touching point GJK came up with.
1041 // The result from EPA under these circumstances is not usable.
1042 //UE_LOG(LogChaos, Warning, TEXT("EPA Degenerate Case"));
1043 break;
1044 }
1045 }
1046
1047 // @todo(chaos): handle the case where EPA hits a degenerate triangle in the simplex.
1048 // Currently we return a touching contact with the last position and normal from GJK. We could run SAT instead.
1049
1050 // GJK converged or we have a touching contact
1051 {
1052 TVec3<T> ClosestA(0);
1053 TVec3<T> ClosestB(0);
1054 for (int i = 0; i < SimplexIDs.NumVerts; ++i)
1055 {
1056 ClosestA += SimplexData.As[i] * SimplexData.Barycentric[i];
1057 ClosestB += SimplexData.Bs[i] * SimplexData.Barycentric[i];
1058 }
1059
1063 OutNormal = Normal;
1065
1066 // @todo(chaos): we should pass back failure for the degenerate case so we can decide how to handle it externally.
1067 return true;
1068 }
1069 }
1070
1071 template <typename T, typename TGeometryA, typename TGeometryB>
1073 {
1075 T SupportDeltaA = 0;
1076 T SupportDeltaB = 0;
1077 T MaxSupportDelta = 0;
1078
1081
1082 // Return the support vertex in A-local space for a vector V in A-local space
1083 auto SupportAFunc = [&A, &SupportDeltaA, &VertexIndexA](const TVec3<T>& V)
1084 {
1085 return A.SupportCore(V, A.GetMargin(), &SupportDeltaA, VertexIndexA);
1086 };
1087
1088 // Return the support point on B, in B-local space, for a vector V in A-local space
1089 auto SupportBFunc = [&B, &SupportDeltaB, &VertexIndexB](const TVec3<T>& V)
1090 {
1091 return B.SupportCore(V, B.GetMargin(), &SupportDeltaB, VertexIndexB);
1092 };
1093
1094 // V and Simplex are in A-local space
1095 TVec3<T> V = InitialDir;
1096 TVec3<T> Simplex[4];
1097 int32 NumVerts = 0;
1098 T Distance = FLT_MAX;
1099
1100 TVec3<T> Normal = -V; // Remember the last good normal (i.e. don't update it if separation goes less than Epsilon and we can no longer normalize)
1101 bool bIsResult = false; // True if GJK cannot make any more progress
1102 bool bIsContact = false; // True if shapes are within Epsilon or overlapping - GJK cannot provide a solution
1103 int32 NumIterations = 0;
1104 const T ThicknessA = A.GetMargin();
1105 const T ThicknessB = B.GetMargin();
1106 const T SeparatedDistance = ThicknessA + ThicknessB + Epsilon;
1107 while (!bIsContact && !bIsResult)
1108 {
1109 // If taking too long just stop
1110 ++NumIterations;
1111 if (CheckGJKIterationLimit(NumIterations, A, B))
1112 {
1113 break;
1114 }
1115
1116 const TVec3<T> NegV = -V;
1118 const TVec3<T> SupportB = SupportBFunc(V);
1119 const TVec3<T> W = SupportA - SupportB;
1121
1122 // If we didn't move to at least ConvergedDistance or closer, assume we have reached a minimum
1123 const T ConvergenceTolerance = 1.e-4f;
1124 const T ConvergedDistance = (1.0f - ConvergenceTolerance) * Distance;
1125 const T VW = TVec3<T>::DotProduct(V, W);
1126
1129 Simplex[NumVerts++] = W;
1130
1132 T NewDistance = V.Size();
1133
1134 // If we hit this error, we probably have a degenerate simplex that is not being detected in SimplexFindClosestToOrigin2
1135 CHAOS_COLLISIONERROR_CLOG(V.ContainsNaN(), TEXT("SimplexFindClosestToOrigin2 NaN, NumVerts: %d, Simplex: [[%f, %f, %f], [%f %f %f], [%f %f %f], [%f %f %f]]"),
1136 NumVerts, Simplex[0].X, Simplex[0].Y, Simplex[0].Z, Simplex[1].X, Simplex[1].Y, Simplex[1].Z, Simplex[2].X, Simplex[2].Y, Simplex[2].Z, Simplex[3].X, Simplex[3].Y, Simplex[3].Z);
1138
1139 // Are we overlapping or too close for GJK to get a good result?
1140 bIsContact = (NewDistance < Epsilon);
1141
1142 // If we did not get closer in this iteration, we are in a degenerate situation
1143 bIsResult = (NewDistance >= (Distance - Epsilon));
1144
1145 // If we are not too close, update the separating vector and keep iterating
1146 // If we are too close we drop through to EPA, but if EPA determines the shapes are actually separated
1147 // after all, we will wend up using the normal from the previous loop
1148 if (!bIsContact)
1149 {
1150 V /= NewDistance;
1151 Normal = -V;
1152 }
1154 }
1155
1156 // Save the warm-start data (not much to store since we updated most of it in the loop above)
1157 SimplexData.NumVerts = NumVerts;
1158
1159 if (bIsContact)
1160 {
1161 // We did not converge or we detected an overlap situation, so run EPA to get contact data
1162 // Rebuild the simplex into a variable sized array - EPA can generate any mumber of vertices
1165 VertsA.Reserve(8);
1166 VertsB.Reserve(8);
1167 for (int i = 0; i < NumVerts; ++i)
1168 {
1169 VertsA.Add(SimplexData.As[i]);
1170 VertsB.Add(SimplexData.Bs[i]);
1171 }
1172
1173 T Penetration;
1176
1177 switch (EPAResult)
1178 {
1180 // Possibly a solution but with unknown error. Just return the last EPA state.
1181 // Fall through...
1182 case EEPAResult::Ok:
1183 // EPA has a solution - return it now
1184 OutNormal = MTD;
1185 OutPenetration = Penetration + ThicknessA + ThicknessB;
1189 return true;
1191 // The origin is outside the simplex. Must be a touching contact and EPA setup will have calculated the normal
1192 // and penetration but we keep the position generated by GJK.
1193 Normal = MTD;
1194 Distance = -Penetration;
1195 //UE_LOG(LogChaos, Warning, TEXT("EPA Touching Case"));
1196 break;
1198 // We hit a degenerate simplex condition and could not reach a solution so use whatever near touching point GJK came up with.
1199 // The result from EPA under these circumstances is not usable.
1200 //UE_LOG(LogChaos, Warning, TEXT("EPA Degenerate Case"));
1201 break;
1202 }
1203 }
1204
1205 // @todo(chaos): handle the case where EPA hits a degenerate triangle in the simplex.
1206 // Currently we return a touching contact with the last position and normal from GJK. We could run SAT instead.
1207
1208 // GJK converged or we have a touching contact
1209 {
1210 TVec3<T> ClosestA(0);
1211 TVec3<T> ClosestB(0);
1212 for (int i = 0; i < NumVerts; ++i)
1213 {
1214 ClosestA += SimplexData.As[i] * SimplexData.Barycentric[i];
1215 ClosestB += SimplexData.Bs[i] * SimplexData.Barycentric[i];
1216 }
1217
1221 OutNormal = Normal;
1223
1224 // @todo(chaos): we should pass back failure for the degenerate case so we can decide how to handle it externally.
1225 return true;
1226 }
1227 }
1228
1229 template <bool bNegativePenetrationAllowed = false, typename T>
1231 {
1234
1235 const TRotation<T, 3> AToBRotation = BToATM.GetRotation().Inverse();
1236
1237 //todo: refactor all of these similar functions
1239 if (V.SafeNormalize() == 0)
1240 {
1241 V = TVec3<T>(-1, 0, 0);
1242 }
1243
1244 TVec3<T> As[4];
1245 TVec3<T> Bs[4];
1246
1248 SimplexIDs.NumVerts = 0; // Initialization not needed, but compiler warns on uninitialized access to As/Bs
1250 T Barycentric[4] = { -1,-1,-1,-1 }; // Initialization not needed, but compiler warns
1251 TVec3<T> Normal = -V; // Remember the last good normal (i.e. don't update it if separation goes less than Epsilon and we can no longer normalize)
1252 bool bIsDegenerate = false; // True if GJK cannot make any more progress
1253 bool bIsContact = false; // True if shapes are within Epsilon or overlapping - GJK cannot provide a solution
1254 int NumIterations = 0;
1255 T Distance = FLT_MAX;
1256 const T ThicknessA = InThicknessA + A.GetMargin();
1257 const T ThicknessB = InThicknessB + B.GetMargin();
1258 const T SeparatedDistance = ThicknessA + ThicknessB + Epsilon;
1259 while (!bIsContact && !bIsDegenerate)
1260 {
1261 if (!ensure(NumIterations++ < 32)) //todo: take this out
1262 {
1263 break; //if taking too long just stop. This should never happen
1264 }
1265 const TVector<T, 3> NegV = -V;
1266 const TVector<T, 3> SupportA = A.SupportFunction(NegV, VertexIndexA);
1267 const TVector<T, 3> SupportB = B.SupportFunction(BToATM, AToBRotation, V, VertexIndexB);
1268 const TVector<T, 3> W = SupportA - SupportB;
1269
1270 const T VW = TVector<T, 3>::DotProduct(V, W);
1272 {
1273 // We are separated and don't care about the distance - we can stop now
1274 return false;
1275 }
1276
1277 // If we didn't move to at least ConvergedDistance or closer, assume we have reached a minimum
1278 const T ConvergenceTolerance = 1.e-4f;
1279 const T ConvergedDistance = (1.0f - ConvergenceTolerance) * Distance;
1280 if (VW > ConvergedDistance)
1281 {
1282 // We have reached a solution - use the results from the last iteration
1283 break;
1284 }
1285
1286 SimplexIDs[SimplexIDs.NumVerts] = SimplexIDs.NumVerts;
1287 As[SimplexIDs.NumVerts] = SupportA;
1288 Bs[SimplexIDs.NumVerts] = SupportB;
1289 Simplex[SimplexIDs.NumVerts++] = W;
1290
1291 V = SimplexFindClosestToOrigin(Simplex, SimplexIDs, Barycentric, As, Bs);
1292 T NewDistance = V.Size();
1293
1294 // Are we overlapping or too close for GJK to get a good result?
1295 bIsContact = (NewDistance < Epsilon);
1296
1297 // If we did not get closer in this iteration, we are in a degenerate situation
1299
1300 if (!bIsContact)
1301 {
1302 V /= NewDistance;
1303 Normal = -V;
1304 }
1306 }
1307
1308 if (bIsContact)
1309 {
1310 // We did not converge or we detected an overlap situation, so run EPA to get contact data
1313 VertsA.Reserve(8);
1314 VertsB.Reserve(8);
1315 for (int i = 0; i < SimplexIDs.NumVerts; ++i)
1316 {
1317 VertsA.Add(As[i]);
1318 VertsB.Add(Bs[i]);
1319 }
1320
1321 struct FAHelper
1322 {
1323 const FGeomGJKHelper& Geom;
1324 int32* VertexIndex;
1325
1326 FAHelper(const FGeomGJKHelper& Geom, int32& VertexIndex) : Geom(Geom), VertexIndex(&VertexIndex) {}
1327
1328 TVec3<T> operator()(const TVec3<T>& V) const { return Geom.SupportFunction(V, *VertexIndex); }
1329 };
1330
1331 struct FBHelper
1332 {
1333 const FGeomGJKHelper& Geom;
1334 int32* VertexIndex;
1335 const FRigidTransform3& BToATM;
1336 const FRotation3& AToBRotation;
1337
1338 TVec3<T> operator()(const TVec3<T>& V) const { return Geom.SupportFunction(BToATM, AToBRotation, V, *VertexIndex); }
1339 };
1340
1342 FBHelper BHelper = { B, &VertexIndexB, BToATM, AToBRotation };
1343
1344 T Penetration;
1347
1348 switch (EPAResult)
1349 {
1351 // Possibly a solution but with unknown error. Just return the last EPA state.
1352 // Fall through...
1353 case EEPAResult::Ok:
1354 // EPA has a solution - return it now
1355 OutNormal = MTD;
1356 OutPenetration = Penetration + ThicknessA + ThicknessB;
1361 return true;
1363 // The origin is outside the simplex. Must be a touching contact and EPA setup will have calculated the normal
1364 // and penetration but we keep the position generated by GJK.
1365 Normal = MTD;
1366 Distance = -Penetration;
1367 break;
1369 // We hit a degenerate simplex condition and could not reach a solution so use whatever near touching point GJK came up with.
1370 // The result from EPA under these circumstances is not usable.
1371 //UE_LOG(LogChaos, Warning, TEXT("EPA Degenerate Case"));
1372 break;
1373 }
1374 }
1375
1376 // @todo(chaos): handle the case where EPA hit a degenerate triangle in the simplex.
1377 // Currently we return a touching contact with the last position and normal from GJK. We could run SAT instead.
1378
1379 // GJK converged or we have a touching contact
1380 {
1383 for (int i = 0; i < SimplexIDs.NumVerts; ++i)
1384 {
1385 ClosestA += As[i] * Barycentric[i];
1386 ClosestBInA += Bs[i] * Barycentric[i];
1387 }
1388
1389 OutNormal = Normal;
1390
1391 T Penetration = ThicknessA + ThicknessB - Distance;
1392 OutPenetration = Penetration;
1397
1398 // If we don't care about separation distance/normal, the return value is true if we are overlapping, false otherwise.
1399 // If we do care about seperation distance/normal, the return value is true if we found a solution.
1400 // @todo(chaos): we should pass back failure for the degenerate case so we can decide how to handle it externally.
1401 return (bNegativePenetrationAllowed || (Penetration >= 0.0f));
1402 }
1403 }
1404
1405 // Calculate the penetration depth (or separating distance) of two geometries.
1406 //
1407 // Set bNegativePenetrationAllowed to false (default) if you do not care about the normal and distance when the shapes are separated. The return value
1408 // will be false if the shapes are separated, and the function will be faster because it does not need to determine the closest point.
1409 // If the shapes are overlapping, the function will return true and populate the output parameters with the contact information.
1410 //
1411 // Set bNegativePenetrationAllowed to true if you need to know the closest point on the shapes, even when they are separated. In this case,
1412 // we need to iterate to find the best solution even when objects are separated which is more expensive. The return value will be true as long
1413 // as the algorithm was able to find a solution (i.e., the return value is not related to whether the shapes are overlapping) and the output
1414 // parameters will be populated with the contact information.
1415 //
1416 // In all cases, if the function returns false the output parameters are undefined.
1417 //
1418 // OutClosestA and OutClosestB are the closest or deepest-penetrating points on the two core geometries, both in the space of A and ignoring the margin.
1419 //
1420 // Epsilon is the separation at which GJK considers the objects to be in contact or penetrating and then runs EPA. If this is
1421 // too small, then the renormalization of the separating vector can lead to arbitrarily wrong normals for almost-touching objects.
1422 //
1423 // NOTE: OutPenetration is the penetration including the Thickness (i.e., the actual penetration depth), but the closest points
1424 // returned are on the core shapes (i.e., ignoring the Thickness). If you want the closest positions on the shape surface (including
1425 // the Thickness) use GJKPenetration().
1426 //
1427 template <bool bNegativePenetrationAllowed = false, typename T, typename TGeometryA, typename TGeometryB>
1448 template <typename T, typename TGeometryA, typename TGeometryB>
1449 bool GJKRaycast(const TGeometryA& A, const TGeometryB& B, const TRigidTransform<T, 3>& StartTM, const TVector<T, 3>& RayDir, const T RayLength,
1450 T& OutTime, TVector<T, 3>& OutPosition, TVector<T, 3>& OutNormal, const T ThicknessA = 0, const TVector<T, 3>& InitialDir = TVector<T, 3>(1, 0, 0), const T ThicknessB = 0)
1451 {
1453 ensure(RayLength >= 0);
1454 check(A.IsConvex() && B.IsConvex());
1455 const TVector<T, 3> StartPoint = StartTM.GetLocation();
1458
1462
1463 T Barycentric[4] = { -1,-1,-1,-1 }; //not needed, but compiler warns
1464
1466 const TRotation<T, 3> BToARotation = StartTM.GetRotation();
1467 const TRotation<T, 3> AToBRotation = BToARotation.Inverse();
1468 TVector<T, 3> SupportA = A.Support(InitialDir, ThicknessA, VertexIndexA); //todo: use Thickness on quadratic geometry
1469 As[0] = SupportA;
1470
1471 const TVector<T, 3> InitialDirInB = AToBRotation * (-InitialDir);
1474 Bs[0] = SupportB;
1475
1476 T Lambda = 0;
1477 TVector<T, 3> X = StartPoint;
1479 TVector<T, 3> V = X - (SupportA - SupportB);
1480
1481 bool bTerminate;
1482 bool bNearZero = false;
1483 bool bDegenerate = false;
1484 int NumIterations = 0;
1486 do
1487 {
1488 //if (!ensure(NumIterations++ < 32)) //todo: take this out
1489 if (!(NumIterations++ < 32)) //todo: take this out
1490 {
1491 break; //if taking too long just stop. This should never happen
1492 }
1493
1494 SupportA = A.Support(V, ThicknessA, VertexIndexA); //todo: add thickness to quadratic geometry to avoid quadratic vs quadratic when possible
1495 const TVector<T, 3> VInB = AToBRotation * (-V);
1497 SupportB = BToARotation * SupportBLocal;
1498 const TVector<T, 3> P = SupportA - SupportB;
1499 const TVector<T, 3> W = X - P;
1500 SimplexIDs[SimplexIDs.NumVerts] = SimplexIDs.NumVerts; //is this needed?
1501 As[SimplexIDs.NumVerts] = SupportA;
1502 Bs[SimplexIDs.NumVerts] = SupportB;
1503
1504 const T VDotW = TVector<T, 3>::DotProduct(V, W);
1505 if (VDotW > 0)
1506 {
1508 if (VDotRayDir >= 0)
1509 {
1510 return false;
1511 }
1512
1513 const T PreLambda = Lambda; //use to check for no progress
1514 // @todo(ccaulfield): this can still overflow - the comparisons against zero above should be changed (though not sure to what yet)
1515 Lambda = Lambda - VDotW / VDotRayDir;
1516 if (Lambda > PreLambda)
1517 {
1518 if (Lambda > RayLength)
1519 {
1520 return false;
1521 }
1522
1523 const TVector<T, 3> OldX = X;
1524 X = StartPoint + Lambda * RayDir;
1525 Normal = V;
1526
1527 //Update simplex from (OldX - P) to (X - P)
1528 const TVector<T, 3> XMinusOldX = X - OldX;
1529 Simplex[0] += XMinusOldX;
1530 Simplex[1] += XMinusOldX;
1531 Simplex[2] += XMinusOldX;
1532 Simplex[SimplexIDs.NumVerts++] = X - P;
1533
1534 GJKPreDist2 = TNumericLimits<T>::Max(); //translated origin so restart gjk search
1535 }
1536 }
1537 else
1538 {
1539 Simplex[SimplexIDs.NumVerts++] = W; //this is really X - P which is what we need for simplex computation
1540 }
1541
1542 V = SimplexFindClosestToOrigin(Simplex, SimplexIDs, Barycentric, As, Bs);
1543
1544 T NewDist2 = V.SizeSquared(); //todo: relative error
1545 bNearZero = NewDist2 < 1e-6;
1549
1550 } while (!bTerminate);
1551
1552 OutTime = Lambda;
1553
1554 if (Lambda > 0)
1555 {
1556 OutNormal = Normal.GetUnsafeNormal();
1559
1560 for (int i = 0; i < SimplexIDs.NumVerts; ++i)
1561 {
1562 ClosestB += Bs[i] * Barycentric[i];
1563 }
1565
1566 OutPosition = StartPoint + RayDir * Lambda + ClosestLocal;
1567 }
1568
1569 return true;
1570 }
1586 template<typename T, EGJKTestSpace TestSpace = EGJKTestSpace::ALocalSpace>
1587 bool GJKRaycast2ImplSimd(const FGeomGJKHelperSIMD& RESTRICT A, const FGeomGJKHelperSIMD& RESTRICT B, const T& BToARotation, const T& StartPoint, const T& RayDir,
1588 FRealSingle RayLength, FRealSingle& OutTime, T& OutPosition, T& OutNormal, bool bComputeMTD, const T& InitialDir)
1589 {
1590 ensure(RayLength >= 0);
1591
1592 // Margin selection logic: we only need a small margin for sweeps since we only move the sweeping object
1593 // to the point where it just touches.
1594 // Spheres and Capsules: always use the core shape and full "margin" because it represents the radius
1595 // Sphere/Capsule versus OtherShape: no margin on other
1596 // OtherShape versus OtherShape: use margin of the smaller shape, zero margin on the other
1597 const FRealSingle RadiusA = static_cast<FRealSingle>(A.GetRadius());
1598 const FRealSingle RadiusB = static_cast<FRealSingle>(B.GetRadius());
1599 const bool bHasRadiusA = RadiusA > 0;
1600 const bool bHasRadiusB = RadiusB > 0;
1601
1602 // The sweep margins if required. Only one can be non-zero (we keep the smaller one)
1603 const FRealSingle SweepMarginScale = 0.05f;
1604 const bool bAIsSmallest = A.GetMargin() < B.GetMargin();
1605 const FRealSingle SweepMarginA = (bHasRadiusA || bHasRadiusB) ? 0.0f : (bAIsSmallest ? SweepMarginScale * static_cast<FRealSingle>(A.GetMargin()) : 0.0f);
1606 const FRealSingle SweepMarginB = (bHasRadiusA || bHasRadiusB) ? 0.0f : (bAIsSmallest ? 0.0f : SweepMarginScale * static_cast<FRealSingle>(B.GetMargin()));
1607
1608 // Net margin (note: both SweepMargins are zero if either Radius is non-zero, and only one SweepMargin can be non-zero)
1609 A.Margin = RadiusA + SweepMarginA;
1610 B.Margin = RadiusB + SweepMarginB;
1611
1612 const T MarginASimd = T(VectorLoadFloat1(&A.Margin));
1613 const T MarginBSimd = T(VectorLoadFloat1(&B.Margin));
1614
1618
1619 T Barycentric = TVectorZero<T>();
1620
1622 const T Eps2Simd = TMakeVectorRegister<T>(1e-6f, 1e-6f, 1e-6f, 1e-6f);
1624
1626
1627 int32 NumVerts = 0;
1628
1629 T AToBRotation = VectorQuaternionInverse(BToARotation);
1630
1631 T SupportA, SupportB;
1633 As[0] = SupportA;
1634 Bs[0] = SupportB;
1635
1636 T Lambda = TVectorZero<T>();
1637 T X = StartPoint;
1639 T Normal = MakeVectorRegisterFloat(0.f, 0.f, 1.f, 0.f);
1640
1641 const T InitialPreDist2Simd = VectorDot3(V, V);
1642
1645
1648
1649 constexpr FRealSingle Eps2 = 1e-6f;
1650
1651 //mtd needs to find closest point even in inflation region, so can only skip if we found the closest points
1652 bool bCloseEnough = InitialPreDist2 < Inflation2 && (!bComputeMTD || InitialPreDist2 < Eps2);
1653 bool bDegenerate = false;
1654 bool bTerminate = bCloseEnough;
1656 int NumIterations = 0;
1658 T GJKPreDist2 = LimitMax;
1659
1660 while (!bTerminate)
1661 {
1662 if (!(NumIterations++ < 32)) //todo: take this out
1663 {
1664 break; //if taking too long just stop. This should never happen
1665 }
1666
1668
1669 TGJKSupportHelperSIMD<T, TestSpace>::Support(A, B, V, AToBRotation, BToARotation, SupportA, SupportB);
1671 T W = VectorSubtract(X, P);
1672
1673
1674 As[NumVerts] = SupportA;
1675 Bs[NumVerts] = SupportB;
1676
1677 const T VDotW = VectorDot3(V, W);
1678
1680
1682 {
1683 const T VDotRayDir = VectorDot3(V, RayDir);
1685
1687 {
1688 return false;
1689 }
1690
1691 const T PreLambda = Lambda; //use to check for no progress
1692 // @todo(ccaulfield): this can still overflow - the comparisons against zero above should be changed (though not sure to what yet)
1696 {
1699 {
1700 return false;
1701 }
1702
1703 const T OldX = X;
1704 X = VectorMultiplyAdd(Lambda, RayDir, StartPoint);
1705 Normal = V;
1706
1707 //Update simplex with the new X
1708 // As and Bs should have been correcly updated by enabling the flag CalculatExtraInformation
1709 // inside VectorSimplexFindClosestToOrigin
1710 Simplex[0] = VectorSubtract(X, VectorSubtract(As[0], Bs[0]));
1711 Simplex[1] = VectorSubtract(X, VectorSubtract(As[1], Bs[1]));
1712 Simplex[2] = VectorSubtract(X, VectorSubtract(As[2], Bs[2]));
1714
1715 GJKPreDist2 = LimitMax; //translated origin so restart gjk search
1716 bInflatedCloseEnough = false;
1717 }
1718 }
1719 else
1720 {
1721 Simplex[NumVerts++] = W; //this is really X - P which is what we need for simplex computation
1722 }
1723
1725 {
1726 //Inflated shapes are close enough, but we want MTD so we need to find closest point on core shape
1727 const T VDotW2 = VectorDot3(VDotW, VDotW);
1729 }
1730
1731 if (!bCloseEnough)
1732 {
1733
1734 V = VectorSimplexFindClosestToOrigin<T>(Simplex, NumVerts, Barycentric, As, Bs);
1735
1736 T NewDist2 = VectorDot3(V, V); //todo: relative error
1740
1741 if (bComputeMTD && bCloseEnough)
1742 {
1743 const T LambdaEqZero = VectorCompareEQ(Lambda, TVectorZero<T>());
1746
1747 const bool Is4GTNumVerts = 4 > NumVerts;
1748
1750
1751 // Leaving the original code, to explain the logic there
1752 //if (bComputeMTD && bCloseEnough && Lambda == 0 && GJKPreDist2 > 1e-6 && Inflation2 > 1e-6 && NumVerts < 4)
1753 //{
1754 // bCloseEnough = false;
1755 // bInflatedCloseEnough = true;
1756 //}
1759 }
1760 }
1761 else
1762 {
1763 //It must be that we want MTD and we can terminate. However, we must make one final call to fixup the simplex
1764 V = VectorSimplexFindClosestToOrigin<T>(Simplex, NumVerts, Barycentric, As, Bs);
1765 }
1767 }
1768 VectorStoreFloat1(Lambda, &OutTime);
1769
1770 if (OutTime > 0)
1771 {
1772 OutNormal = Normal;
1774
1775 T Barycentrics[4];
1776 Barycentrics[0] = VectorSwizzle(Barycentric, 0, 0, 0, 0);
1777 Barycentrics[1] = VectorSwizzle(Barycentric, 1, 1, 1, 1);
1778 Barycentrics[2] = VectorSwizzle(Barycentric, 2, 2, 2, 2);
1779 Barycentrics[3] = VectorSwizzle(Barycentric, 3, 3, 3, 3);
1780
1781
1782 const T ClosestB1 = VectorMultiplyAdd(Bs[0], Barycentrics[0], ClosestB);
1783 const T ClosestB2 = VectorMultiplyAdd(Bs[1], Barycentrics[1], ClosestB1);
1784 const T ClosestB3 = VectorMultiplyAdd(Bs[2], Barycentrics[2], ClosestB2);
1785 const T ClosestB4 = VectorMultiplyAdd(Bs[3], Barycentrics[3], ClosestB3);
1786
1791
1793
1794 OutPosition = VectorAdd(VectorMultiplyAdd(RayDir, Lambda, StartPoint), ClosestLocal);
1795
1796 }
1797 else if (bComputeMTD)
1798 {
1799 // If Inflation == 0 we would expect GJKPreDist2 to be 0
1800 // However, due to precision we can still end up with GJK failing.
1801 // When that happens fall back on EPA
1806
1809
1810 if (NumIterations)
1811 {
1812 T Barycentrics[4];
1813 Barycentrics[0] = VectorSwizzle(Barycentric, 0, 0, 0, 0);
1814 Barycentrics[1] = VectorSwizzle(Barycentric, 1, 1, 1, 1);
1815 Barycentrics[2] = VectorSwizzle(Barycentric, 2, 2, 2, 2);
1816 Barycentrics[3] = VectorSwizzle(Barycentric, 3, 3, 3, 3);
1817
1818 for (int i = 0; i < NumVerts; ++i)
1819 {
1822 }
1823 }
1824 else
1825 {
1826 //didn't even go into gjk loop
1827 GJKClosestA = As[0];
1828 GJKClosestB = Bs[0];
1829 }
1830
1831 if (VectorMaskBits(IsDone))
1832 {
1833 const T ClosestBInA = VectorAdd(StartPoint, GJKClosestB);
1835 OutNormal = VectorNormalizeAccurate(V);
1836
1838 Penetration = VectorMin(Penetration, LimitMax);
1839 Penetration = VectorMax(Penetration, TVectorZero<T>());
1840
1842
1843 OutPosition = VectorAdd(VectorMultiplyAdd(OutNormal, Penetration, StartPoint), ClosestLocal);
1844 Penetration = VectorNegate(Penetration);
1845 VectorStoreFloat1(Penetration, &OutTime);
1846 }
1847 else
1848 {
1849 if (NumIterations)
1850 {
1853
1854 VertsA.Reserve(8);
1855 VertsB.Reserve(8);
1856
1857 for (int i = 0; i < NumVerts; ++i)
1858 {
1861 }
1862
1864 VectorRegister4Float Penetration;
1868 {
1869 OutNormal = MTD;
1870 VectorStoreFloat1(Penetration, &OutTime);
1871 OutTime = -OutTime - (A.Margin + B.Margin);
1872 OutPosition = VectorMultiplyAdd(OutNormal, MarginASimd, ClosestA);
1873 }
1875 {
1876 // The origin is outside the simplex. Must be a touching contact and EPA setup will have calculated the normal
1877 // and penetration but we keep the position generated by GJK.
1878 OutNormal = MTD;
1879 VectorStoreFloat1(Penetration, &OutTime);
1880 OutTime = -OutTime - (A.Margin + B.Margin);
1881 OutPosition = VectorMultiplyAdd(OutNormal, MarginASimd, GJKClosestA);
1882 }
1884 {
1885 // Assume touching hit and use the last GJK result because
1886 // EPA does not necessarily return a good normal when GJK provides it with
1887 // a near-degenerate simplex that does not contain the origin (it can reject
1888 // the nearest face and return an arbitrarily bad normal)
1889 OutTime = -(A.Margin + B.Margin);
1890 OutNormal = Normal;
1891 OutPosition = VectorMultiplyAdd(OutNormal, MarginASimd, GJKClosestA);
1892 }
1893 else // EEPAResult::NoValidContact
1894 {
1895 return false;
1896 }
1897 }
1898 else
1899 {
1900 //didn't even go into gjk loop, touching hit
1901 OutTime = -(A.Margin + B.Margin);
1902 OutNormal = TMakeVectorRegister<T>(0.0f, 0.0f, 1.0f, 0.0f);
1903 OutPosition = VectorMultiplyAdd(OutNormal, MarginASimd, As[0]);
1904 }
1905 }
1906 }
1907 else
1908 {
1909 // Initial overlap without MTD. These properties are not valid, but assigning them anyway so they don't contain NaNs and cause issues in invoking code.
1910 OutNormal = TMakeVectorRegister<T>(0.0f, 0.0f, 1.0f, 0.0f);
1911 OutPosition = TMakeVectorRegister<T>(0.0f, 0.0f, 0.0f, 0.0f);
1912 }
1913
1914 return true;
1915 }
1916
1918 FReal& OutTime, TVector<FReal, 3>& OutPosition, TVector<FReal, 3>& OutNormal, const FReal GivenThicknessA, bool bComputeMTD, const TVector<FReal, 3>& InitialDir, const FReal GivenThicknessB)
1919 {
1920 const UE::Math::TQuat<FReal>& RotationDouble = StartTM.GetRotation();
1922
1923 const UE::Math::TVector<FReal>& TranslationDouble = StartTM.GetTranslation();
1925
1926 // Normalize rotation
1928
1931
1933 VectorRegister4Float OutPositionSimd, OutNormalSimd;
1934 const bool Result = GJKRaycast2ImplSimd<VectorRegister4Float>(A, B, Rotation, Translation, RayDirSimd, static_cast<FRealSingle>(RayLength), OutTimeFloat, OutPositionSimd, OutNormalSimd, bComputeMTD, InitialDirSimd);
1935
1936 OutTime = static_cast<double>(OutTimeFloat);
1937
1938 alignas(16) FRealSingle OutFloat[4];
1939 VectorStoreAligned(OutNormalSimd, OutFloat);
1940 OutNormal.X = OutFloat[0];
1941 OutNormal.Y = OutFloat[1];
1942 OutNormal.Z = OutFloat[2];
1943
1944 VectorStoreAligned(OutPositionSimd, OutFloat);
1945 OutPosition.X = OutFloat[0];
1946 OutPosition.Y = OutFloat[1];
1947 OutPosition.Z = OutFloat[2];
1948
1949 return Result;
1950 }
1951
1952 template <typename T = FReal, typename TGeometryA, typename TGeometryB>
1953 bool GJKRaycast2(const TGeometryA& A, const TGeometryB& B, const TRigidTransform<T, 3>& StartTM, const TVector<T, 3>& RayDir, const T RayLength,
1954 T& OutTime, TVector<T, 3>& OutPosition, TVector<T, 3>& OutNormal, const T GivenThicknessA = 0, bool bComputeMTD = false, const TVector<T, 3>& InitialDir = TVector<T, 3>(1, 0, 0), const T GivenThicknessB = 0)
1955 {
1956 return GJKRaycast2Impl(A, B, StartTM, RayDir, RayLength, OutTime, OutPosition, OutNormal, GivenThicknessA, bComputeMTD, InitialDir, GivenThicknessB);
1957 }
1958
1960 FReal& OutTime, TVector<FReal, 3>& OutPosition, TVector<FReal, 3>& OutNormal, const FReal GivenThicknessA, bool bComputeMTD, const TVector<FReal, 3>& InitialDir, const FReal GivenThicknessB)
1961 {
1964
1967
1969 VectorRegister4Float OutPositionSimd, OutNormalSimd;
1970 const bool Result = GJKRaycast2ImplSimd<VectorRegister4Float, EGJKTestSpace::SameSpace>(A, B, UnusedRotation, Translation, RayDirSimd, static_cast<FRealSingle>(RayLength), OutTimeFloat, OutPositionSimd, OutNormalSimd, bComputeMTD, InitialDirSimd);
1971
1972 OutTime = static_cast<double>(OutTimeFloat);
1973
1974 alignas(16) FRealSingle OutFloat[4];
1975 VectorStoreAligned(OutNormalSimd, OutFloat);
1976 OutNormal.X = OutFloat[0];
1977 OutNormal.Y = OutFloat[1];
1978 OutNormal.Z = OutFloat[2];
1979
1980 VectorStoreAligned(OutPositionSimd, OutFloat);
1981 OutPosition.X = OutFloat[0];
1982 OutPosition.Y = OutFloat[1];
1983 OutPosition.Z = OutFloat[2];
1984
1985 return Result;
1986 }
1987
1988 template <typename T = FReal, typename TGeometryA, typename TGeometryB>
1989 bool GJKRaycast2SameSpace(const TGeometryA& A, const TGeometryB& B, const TVector<T, 3>& Start, const TVector<T, 3>& RayDir, const T RayLength,
1990 T& OutTime, TVector<T, 3>& OutPosition, TVector<T, 3>& OutNormal, const T GivenThicknessA = 0, bool bComputeMTD = false, const TVector<T, 3>& InitialDir = TVector<T, 3>(1, 0, 0), const T GivenThicknessB = 0)
1991 {
1992 return GJKRaycast2SameSpaceImpl(A, B, Start, RayDir, RayLength, OutTime, OutPosition, OutNormal, GivenThicknessA, bComputeMTD, InitialDir, GivenThicknessB);
1993 }
1994
1995 template <typename T, typename TGeometryA, typename TGeometryB>
1996 UE_DEPRECATED(5.5, "Use GJKDistanceInitialVFromDirection if possible, or GJKDistanceInitialVFromRelativeTransform if original behaviour was required")
2001
2002 // Avoid this function - use GJKDistanceInitialVFromDirection that takes a vector direction and assumes the geometries are in the
2003 // same space. If your geometries are not in the same space, they can be wrapped in TGJKShapeTransformed or TGJKCoreShapeTransformed
2004 // and you would already have these available because you need them for GJKDistance
2005 template <typename T, typename TGeometryA, typename TGeometryB>
2007 {
2008 FVec3 Direction = BToATM.GetTranslation();
2009 if (Direction.IsZero())
2010 {
2011 Direction = TVec3<T>(1, 0, 0);
2012 }
2013 const TVector<T, 3> DirectionInB = BToATM.GetRotation().Inverse() * Direction;
2014
2016 const TVector<T, 3> SupportA = A.SupportCore(Direction, A.GetMarginf(), (T*)nullptr, UnusedVertexIndex);
2017 const TVector<T, 3> SupportBLocal = B.SupportCore(-DirectionInB, B.GetMarginf(), (T*)nullptr, UnusedVertexIndex);
2018 const TVector<T, 3> SupportB = BToATM.TransformPositionNoScale(SupportBLocal);
2019 return SupportA - SupportB;
2020 }
2021
2022 /*
2023 * Can be used to generate the initial support direction for use with GJKDistance. Returns a point on the Minkowski Sum
2024 * surface opposite to the direction of the supplied Direction. This is usually a good guess for the initial direction
2025 * but it calls SupportCore on both shapes which in O(N) in the number of vertices, and there are often faster alternatives
2026 * if you know the type of shapes you are dealing with (e.g., return the vectors between the centers of the two convex shapes).
2027 *
2028 * If you do roll your own function, make sure that the vector returned is in or on the Minkowski sum (and don't just use a unit
2029 * vector along some direction for example) or GJKDistance may early-exit with an inaccurate result.
2030 */
2031 template <typename T, typename TGeometryA, typename TGeometryB>
2033 {
2034 if (Direction.IsZero())
2035 {
2036 Direction = TVec3<T>(1, 0, 0);
2037 }
2039 const TVec3<T> SupportA = A.SupportCore(Direction, A.GetMargin(), nullptr, UnusedVertexIndex);
2040 const TVec3<T> SupportB = B.SupportCore(-Direction, B.GetMargin(), nullptr, UnusedVertexIndex);
2041 return SupportA - SupportB;
2042 }
2043
2044 // Status of a call to GJKDistance
2046 {
2047 // The shapes are separated by a positive amount and all outputs have valid values
2048 Separated,
2049
2050 // The shapes are overlapping by less than the net margin and all outputs have valid values (with a negative separation)
2051 Contact,
2052
2053 // The shapes are overlapping by more than the net margin and all outputs are invalid (unchanged from their input values)
2055 };
2056
2082 template <typename T, typename GJKShapeTypeA, typename GJKShapeTypeB>
2083 EGJKDistanceResult GJKDistance(const GJKShapeTypeA& A, const GJKShapeTypeB& B, const TVec3<T>& InitialV, T& OutDistance, TVec3<T>& OutNearestA, TVec3<T>& OutNearestB, TVec3<T>& OutNormalA, const T Epsilon = (T)1e-3, const int32 MaxIts = 16)
2084 {
2086 TVec3<T> Simplex[4], SimplexA[4], SimplexB[4];
2087 T Barycentric[4] = { -1, -1, -1, -1 };
2088
2089 const T AMargin = A.GetMargin();
2090 const T BMargin = B.GetMargin();
2091 T Mu = 0;
2092
2093 // Initial vector in Minkowski(A - B)
2094 // NOTE: If InitialV is not in the Minkowski Sum we may incorrectly early-out in the bCloseEnough chaeck below
2095 TVec3<T> V = InitialV;
2096 T VLen = V.Size();
2098
2099 int32 It = 0;
2100 while (VLen > Epsilon)
2101 {
2102 // Find a new point in A-B that is closer to the origin
2103 // NOTE: we do not use support thickness here. Thickness is used when separating objects
2104 // so that GJK can find a solution, but that can be added in a later step.
2105 const TVec3<T> SupportA = A.SupportCore(-V, AMargin, nullptr, VertexIndexA);
2106 const TVec3<T> SupportB = B.SupportCore(V, BMargin, nullptr, VertexIndexB);
2107 const TVec3<T> W = SupportA - SupportB;
2108
2109 T D = TVec3<T>::DotProduct(V, W) / VLen;
2110 Mu = FMath::Max(Mu, D);
2111
2112 // See if we are still making progress toward the origin
2113 bool bCloseEnough = ((VLen - Mu) < Epsilon);
2114 if (bCloseEnough || (++It > MaxIts))
2115 {
2116 // We have reached the minimum to within tolerance. Or we have reached max iterations, in which
2117 // case we (probably) have a solution but with an error larger than Epsilon (technically we could be missing
2118 // the fact that we were going to eventually find the origin, but it'll be a close call so the approximation
2119 // is still good enough).
2120 if (SimplexIDs.NumVerts == 0)
2121 {
2122 // Our initial guess of V was already the minimum separating vector
2125 }
2126 else
2127 {
2128 // The simplex vertices are the nearest point/line/face
2129 OutNearestA = TVec3<T>(0, 0, 0);
2130 OutNearestB = TVec3<T>(0, 0, 0);
2131 for (int32 VertIndex = 0; VertIndex < SimplexIDs.NumVerts; ++VertIndex)
2132 {
2133 int32 WIndex = SimplexIDs[VertIndex];
2134 check(Barycentric[WIndex] >= (T)0);
2135 OutNearestA += Barycentric[WIndex] * SimplexA[WIndex];
2136 OutNearestB += Barycentric[WIndex] * SimplexB[WIndex];
2137 }
2138 }
2139 const TVec3<T> NormalA = -V / VLen;
2140 OutDistance = VLen - (AMargin + BMargin);
2144
2145 // NearestB should be in B-space but is currently in A-space
2146 OutNearestB = B.InverseTransformPositionNoScale(OutNearestB);
2147
2148 return (OutDistance >= 0.0f) ? EGJKDistanceResult::Separated : EGJKDistanceResult::Contact;
2149 }
2150
2151 // Add the new vertex to the simplex
2152 SimplexIDs[SimplexIDs.NumVerts] = SimplexIDs.NumVerts;
2153 Simplex[SimplexIDs.NumVerts] = W;
2154 SimplexA[SimplexIDs.NumVerts] = SupportA;
2155 SimplexB[SimplexIDs.NumVerts] = SupportB;
2156 ++SimplexIDs.NumVerts;
2157
2158 // Find the closest point to the origin on the simplex, and update the simplex to eliminate unnecessary vertices
2160 VLen = V.Size();
2161 }
2162
2163 // Our geometries overlap - we did not set any outputs
2165 }
2166
2167}
#define check(expr)
Definition AssertionMacros.h:314
#define ensure( InExpression)
Definition AssertionMacros.h:464
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define UE_DEPRECATED(Version, Message)
Definition CoreMiscDefines.h:302
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
#define RESTRICT
Definition Platform.h:706
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define CHAOS_COLLISIONERROR_ENSURE(X)
Definition GJK.h:31
#define CHAOS_COLLISIONERROR_CLOG(Condition, Fmt,...)
Definition GJK.h:27
#define FVector
Definition IOSSystemIncludes.h:8
FORCEINLINE VectorRegister4Float VectorSubtract(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:731
FORCEINLINE VectorRegister4Float VectorSqrt(const VectorRegister4Float &Vec)
Definition UnrealMathFPU.h:1263
FORCEINLINE VectorRegister4Float VectorDot3(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:880
FORCEINLINE VectorRegister4Float VectorMin(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:1686
FORCEINLINE VectorRegister4Float MakeVectorRegister(uint32 X, uint32 Y, uint32 Z, uint32 W)
Definition UnrealMathFPU.h:195
FORCEINLINE VectorRegister4Float VectorDivide(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:834
FORCEINLINE VectorRegister4Float VectorMultiply(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:758
FORCEINLINE VectorRegister4Float VectorMax(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:1713
FORCEINLINE VectorRegister4Float VectorBitwiseAnd(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:1165
FORCEINLINE VectorRegister4Float VectorLoadFloat1(const float *Ptr)
Definition UnrealMathFPU.h:468
FORCEINLINE constexpr VectorRegister4Float MakeVectorRegisterFloatConstant(float X, float Y, float Z, float W)
Definition UnrealMathFPU.h:297
FORCEINLINE VectorRegister4Float VectorMultiplyAdd(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2, const VectorRegister4Float &Vec3)
Definition UnrealMathFPU.h:786
FORCEINLINE VectorRegister4Float VectorCompareGT(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:974
#define VectorSet1(F)
Definition UnrealMathFPU.h:2651
FORCEINLINE VectorRegister4Float VectorCompareGE(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:1000
FORCEINLINE VectorRegister4Float VectorCompareLT(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:1025
FORCEINLINE int32 VectorMaskBits(const VectorRegister4Float &Vec1)
Definition UnrealMathFPU.h:1075
FORCEINLINE VectorRegister4Float VectorNegate(const VectorRegister4Float &Vec)
Definition UnrealMathFPU.h:687
FORCEINLINE VectorRegister4Float VectorNegateMultiplyAdd(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2, const VectorRegister4Float &Vec3)
Definition UnrealMathFPU.h:815
FORCEINLINE VectorRegister4Float VectorAdd(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:704
void VectorStoreAligned(const VectorRegister4Float &Vec, float *Ptr)
Definition UnrealMathFPU.h:534
#define VectorSwizzle(Vec, X, Y, Z, W)
Definition UnrealMathFPU.h:639
FORCEINLINE VectorRegister4Float VectorZeroFloat(void)
Definition UnrealMathFPU.h:331
FORCEINLINE VectorRegister4Float VectorCompareEQ(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:923
FORCEINLINE void VectorStoreFloat1(const VectorRegister4Float &Vec, float *Dst)
Definition UnrealMathFPU.h:610
FORCEINLINE VectorRegister4Float MakeVectorRegisterFloat(uint32 X, uint32 Y, uint32 Z, uint32 W)
Definition UnrealMathFPU.h:175
FORCEINLINE VectorRegister4Float MakeVectorRegisterFloatFromDouble(const VectorRegister4Double &Vec4d)
Definition UnrealMathFPU.h:262
#define UE_KINDA_SMALL_NUMBER
Definition UnrealMathUtility.h:131
FORCEINLINE TVectorRegisterType VectorNormalizeAccurate(TVectorRegisterType Vector)
Definition UnrealMathVectorCommon.h.inl:412
FORCEINLINE VectorRegister4Float VectorZero(void)
Definition UnrealMathVectorCommon.h.inl:16
FORCEINLINE VectorRegister4Float VectorNormalizeSafe(VectorRegister4Float Vector, VectorRegister4Float DefaultValue)
Definition UnrealMathVectorCommon.h.inl:425
FORCEINLINE VectorRegister4Float VectorQuaternionInverse(VectorRegister4Float NormalizedQuat)
Definition UnrealMathVectorCommon.h.inl:723
FORCEINLINE VectorRegister4Float VectorQuaternionRotateVector(VectorRegister4Float Quat, VectorRegister4Float VectorW0)
Definition UnrealMathVectorCommon.h.inl:741
Internal simplex data for GJK that can also be stored for warm-starting subsequent calls.
Definition GJK.h:379
void Restore(const TRigidTransform< T, 3 > &BToATM, FSimplex &OutSimplexIDs, TVec3< T > OutSimplex[], TVec3< T > &OutV, T &OutDistance, const T Epsilon)
Definition GJK.h:402
int32 NumVerts
Definition GJK.h:475
TGJKSimplexData()
Definition GJK.h:381
void Save(const FSimplex InSimplexIDs)
Definition GJK.h:392
T Barycentric[MaxSimplexVerts]
Definition GJK.h:472
void Reset()
Definition GJK.h:386
void Restore2(const TRigidTransform< T, 3 > &BToATM, int32 &OutNumVerts, TVec3< T > OutSimplex[], TVec3< T > &OutV, T &OutDistance, const T Epsilon)
Definition GJK.h:433
TVec3< T > Bs[MaxSimplexVerts]
Definition GJK.h:469
TVec3< T > As[MaxSimplexVerts]
Definition GJK.h:466
static const int32 MaxSimplexVerts
Definition GJK.h:463
Definition Transform.h:115
Definition Vector.h:1000
FORCEINLINE T Size() const
Definition Vector.h:1055
FORCEINLINE bool ContainsNaN() const
Definition Vector.h:1101
FORCEINLINE T SizeSquared() const
Definition Vector.h:1067
Definition Vector.h:41
Definition Array.h:670
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition SkeletalMeshComponent.h:307
bool GJKPenetrationSameSpace2(const TGeometryA &A, const TGeometryB &B, T &OutPenetration, TVec3< T > &OutClosestA, TVec3< T > &OutClosestB, TVec3< T > &OutNormal, int32 &OutVertexA, int32 &OutVertexB, T &OutMaxSupportDelta, const TVec3< T > &InitialDir, const T Epsilon=T(1.e-3), const T EPAEpsilon=T(1.e-2))
Definition GJK.h:1072
TVector< T, 3 > GJKDistanceInitialV(const TGeometryA &A, const TGeometryB &B, const TRigidTransform< T, 3 > &BToATM)
Definition GJK.h:1997
bool GJKIntersectionSimd(const TGeometryA &RESTRICT A, const TGeometryB &RESTRICT B, const VectorRegister4Float &Translation, const VectorRegister4Float &Rotation, FRealSingle InThicknessA, const VectorRegister4Float &InitialDir)
Definition GJK.h:199
bool CheckGJKIterationLimit(const int32 NumIterations, const ConvexTypeA &A, const ConvexTypeB &B)
Definition GJK.h:142
bool GJKRaycast(const TGeometryA &A, const TGeometryB &B, const TRigidTransform< T, 3 > &StartTM, const TVector< T, 3 > &RayDir, const T RayLength, T &OutTime, TVector< T, 3 > &OutPosition, TVector< T, 3 > &OutNormal, const T ThicknessA=0, const TVector< T, 3 > &InitialDir=TVector< T, 3 >(1, 0, 0), const T ThicknessB=0)
Definition GJK.h:1449
TVec3< T > GJKDistanceInitialVFromDirection(const TGeometryA &A, const TGeometryB &B, TVec3< T > Direction)
Definition GJK.h:2032
EEPAResult VectorEPA(TArray< VectorRegister4Float > &VertsABuffer, TArray< VectorRegister4Float > &VertsBBuffer, const TSupportA &SupportA, const TSupportB &SupportB, VectorRegister4Float &OutPenetration, VectorRegister4Float &OutDir, VectorRegister4Float &WitnessA, VectorRegister4Float &WitnessB)
Definition EPAVectorized.h:425
@ Y
Definition SimulationModuleBase.h:153
@ X
Definition SimulationModuleBase.h:152
bool GJKIntersectionSameSpaceSimd(const TGeometryA &A, const TGeometryB &B, FRealSingle InThicknessA, const VectorRegister4Float &InitialDir)
Definition GJK.h:302
bool GJKIntersection(const TGeometryA &RESTRICT A, const TGeometryB &RESTRICT B, const TRigidTransform< T, 3 > &BToATM, const T InThicknessA=0, const TVector< T, 3 > &InitialDir=TVector< T, 3 >(1, 0, 0))
Definition GJK.h:190
bool GJKRaycast2SameSpaceImpl(const FGeomGJKHelperSIMD &A, const FGeomGJKHelperSIMD &B, const TVector< FReal, 3 > &Start, const TVector< FReal, 3 > &RayDir, const FReal RayLength, FReal &OutTime, TVector< FReal, 3 > &OutPosition, TVector< FReal, 3 > &OutNormal, const FReal GivenThicknessA, bool bComputeMTD, const TVector< FReal, 3 > &InitialDir, const FReal GivenThicknessB)
Definition GJK.h:1959
FRealDouble FReal
Definition Real.h:22
EGJKDistanceResult
Definition GJK.h:2046
bool GJKRaycast2ImplSimd(const FGeomGJKHelperSIMD &RESTRICT A, const FGeomGJKHelperSIMD &RESTRICT B, const T &BToARotation, const T &StartPoint, const T &RayDir, FRealSingle RayLength, FRealSingle &OutTime, T &OutPosition, T &OutNormal, bool bComputeMTD, const T &InitialDir)
Definition GJK.h:1587
bool GJKRaycast2(const TGeometryA &A, const TGeometryB &B, const TRigidTransform< T, 3 > &StartTM, const TVector< T, 3 > &RayDir, const T RayLength, T &OutTime, TVector< T, 3 > &OutPosition, TVector< T, 3 > &OutNormal, const T GivenThicknessA=0, bool bComputeMTD=false, const TVector< T, 3 > &InitialDir=TVector< T, 3 >(1, 0, 0), const T GivenThicknessB=0)
Definition GJK.h:1953
bool GJKRaycast2Impl(const FGeomGJKHelperSIMD &A, const FGeomGJKHelperSIMD &B, const TRigidTransform< FReal, 3 > &StartTM, const TVector< FReal, 3 > &RayDir, const FReal RayLength, FReal &OutTime, TVector< FReal, 3 > &OutPosition, TVector< FReal, 3 > &OutNormal, const FReal GivenThicknessA, bool bComputeMTD, const TVector< FReal, 3 > &InitialDir, const FReal GivenThicknessB)
Definition GJK.h:1917
bool GJKPenetrationImpl(const FGeomGJKHelper &A, const FGeomGJKHelper &B, const TRigidTransform< T, 3 > &BToATM, T &OutPenetration, TVec3< T > &OutClosestA, TVec3< T > &OutClosestB, TVec3< T > &OutNormal, int32 &OutClosestVertexIndexA, int32 &OutClosestVertexIndexB, const T InThicknessA=0.0f, const T InThicknessB=0.0f, const TVector< T, 3 > &InitialDir=TVector< T, 3 >(1, 0, 0), const T Epsilon=1.e-3f)
Definition GJK.h:1230
bool GJKPenetrationWarmStartable(const TGeometryA &A, const TGeometryB &B, const TRigidTransform< T, 3 > &BToATM, T &OutPenetration, TVec3< T > &OutClosestA, TVec3< T > &OutClosestB, TVec3< T > &OutNormalA, TVec3< T > &OutNormalB, int32 &OutVertexA, int32 &OutVertexB, TGJKSimplexData< T > &InOutSimplexData, T &OutMaxSupportDelta, const T Epsilon=T(1.e-3), const T EPAEpsilon=T(1.e-2))
Calculate the penetration data for two shapes using GJK and a warm-start buffer.
Definition GJK.h:558
float FRealSingle
Definition Real.h:14
bool GJKRaycast2SameSpace(const TGeometryA &A, const TGeometryB &B, const TVector< T, 3 > &Start, const TVector< T, 3 > &RayDir, const T RayLength, T &OutTime, TVector< T, 3 > &OutPosition, TVector< T, 3 > &OutNormal, const T GivenThicknessA=0, bool bComputeMTD=false, const TVector< T, 3 > &InitialDir=TVector< T, 3 >(1, 0, 0), const T GivenThicknessB=0)
Definition GJK.h:1989
EGJKTestSpace
Definition GJK.h:81
@ ALocalSpace
Definition GJK.h:83
@ SameSpace
Definition GJK.h:82
EGJKDistanceResult GJKDistance(const GJKShapeTypeA &A, const GJKShapeTypeB &B, const TVec3< T > &InitialV, T &OutDistance, TVec3< T > &OutNearestA, TVec3< T > &OutNearestB, TVec3< T > &OutNormalA, const T Epsilon=(T) 1e-3, const int32 MaxIts=16)
Definition GJK.h:2083
EEPAResult
Definition EPA.h:421
void CalculateQueryMargins(const TGeometryA &A, const TGeometryB &B, T &outMarginA, T &outMarginB)
Definition GJK.h:156
bool GJKPenetration(const TGeometryA &A, const TGeometryB &B, const TRigidTransform< T, 3 > &BToATM, T &OutPenetration, TVec3< T > &OutClosestA, TVec3< T > &OutClosestB, TVec3< T > &OutNormal, int32 &OutClosestVertexIndexA, int32 &OutClosestVertexIndexB, const T InThicknessA=0.0f, const T InThicknessB=0.0f, const TVector< T, 3 > &InitialDir=TVector< T, 3 >(1, 0, 0), const T Epsilon=1.e-3f)
Definition GJK.h:1428
bool GJKPenetrationSameSpace(const TGeometryA &A, const TGeometryB &B, T &OutPenetration, TVec3< T > &OutClosestA, TVec3< T > &OutClosestB, TVec3< T > &OutNormal, int32 &OutVertexA, int32 &OutVertexB, T &OutMaxSupportDelta, const TVec3< T > &InitialDir, const T Epsilon=T(1.e-3), const T EPAEpsilon=T(1.e-2))
Calculate the penetration data for two shapes using GJK, assuming both shapes are already in the same...
Definition GJK.h:918
TVector< T, 3 > GJKDistanceInitialVFromRelativeTransform(const TGeometryA &A, const TGeometryB &B, const TRigidTransform< T, 3 > &BToATM)
Definition GJK.h:2006
TVec3< T > SimplexFindClosestToOrigin2(TVec3< T > *Simplex, int32 &NumVerts, T *OutBarycentric, TVec3< T > *A, TVec3< T > *B)
Definition Simplex.h:636
TVec3< T > SimplexFindClosestToOrigin(TVec3< T > *Simplex, FSimplex &Idxs, T *OutBarycentric, TVec3< T > *A=nullptr, TVec3< T > *B=nullptr)
Definition Simplex.h:587
bool GJKPenetrationWarmStartable2(const TGeometryA &A, const TGeometryB &B, const TRigidTransform< T, 3 > &BToATM, T &OutPenetration, TVec3< T > &OutClosestA, TVec3< T > &OutClosestB, TVec3< T > &OutNormalA, TVec3< T > &OutNormalB, int32 &OutVertexA, int32 &OutVertexB, TGJKSimplexData< T > &InOutSimplexData, T &OutMaxSupportDelta, const T Epsilon=T(1.e-3), const T EPAEpsilon=T(1.e-2))
Definition GJK.h:729
constexpr VectorRegister4Float BigNumber
Definition UnrealMathVectorConstants.h.inl:55
constexpr VectorRegister4Float Float0001
Definition UnrealMathVectorConstants.h.inl:43
Definition GJK.h:43
FRealSingle Margin
Definition GJK.h:47
const void * Geometry
Definition GJK.h:46
VectorRegister4Float SupportFunction(const VectorRegister4Float AToBRotation, const VectorRegister4Float BToARotation, const VectorRegister4Float V) const
Definition GJK.h:66
VectorRegister4Float SupportFunction(const VectorRegister4Float V, FRealSingle InMargin) const
Definition GJK.h:63
VectorRegister4Float SupportFunction(const VectorRegister4Float V) const
Definition GJK.h:62
VectorRegister4Float operator()(const VectorRegister4Float V) const
Definition GJK.h:61
VectorRegister4Float operator()(const VectorRegister4Float AToBRotation, const VectorRegister4Float BToARotation, const VectorRegister4Float V) const
Definition GJK.h:65
VectorRegister4Float(* SupportFunc)(const void *Geom, FRealSingle Margin, const VectorRegister4Float V)
Definition GJK.h:44
FRealSingle GetMarginf() const
Definition GJK.h:59
FRealSingle GetMargin() const
Definition GJK.h:57
FRealSingle GetRadius() const
Definition GJK.h:56
SupportFunc Func
Definition GJK.h:49
FRealSingle Radius
Definition GJK.h:48
FGeomGJKHelperSIMD(const T &Geom)
Definition GJK.h:52
FRealSingle GetRadiusf() const
Definition GJK.h:58
Definition GJK.h:482
FGeomGJKHelper(const T &Geom)
Definition GJK.h:490
FVector SupportFunction(const FRotation3 &AToBRotation, const FVec3 &V, FReal *OutSupportDelta, int32 &VertexIndex) const
Definition GJK.h:495
FReal GetMargin() const
Definition GJK.h:515
FVector SupportFunction(const FRigidTransform3 &BToATM, const FRotation3 &AToBRotation, const FVec3 &V, int32 &VertexIndex) const
Definition GJK.h:501
FVector SupportFunction(const FVec3 &V, int32 &VertexIndex) const
Definition GJK.h:492
FReal GetMarginf() const
Definition GJK.h:516
FVector(* SupportFunc)(const void *Geom, const FVec3 &Direction, FReal *OutSupportDelta, int32 &VertexIndex)
Definition GJK.h:483
FVector SupportFunction(const FRigidTransform3 &BToATM, const FRotation3 &AToBRotation, const FVec3 &V, FReal *OutSupportDelta, int32 &VertexIndex) const
Definition GJK.h:508
FRealSingle Margin
Definition GJK.h:487
const void * Geometry
Definition GJK.h:485
FVector SupportFunction(const FVec3 &V, FReal *OutSupportDelta, int32 &VertexIndex) const
Definition GJK.h:493
SupportFunc Func
Definition GJK.h:486
Definition Simplex.h:84
int32 NumVerts
Definition Simplex.h:85
static void Support(const FGeomGJKHelperSIMD &RESTRICT A, const FGeomGJKHelperSIMD &RESTRICT B, const T &SearchDir, const T &BToARotation, const T &AToBRotation, T &OutSupportA, T &OutSupportB)
Definition GJK.h:99
Definition GJK.h:88
static void Support(const FGeomGJKHelperSIMD &RESTRICT A, const FGeomGJKHelperSIMD &RESTRICT B, const T &SearchDir, const T &AToBRotation, const T &BToARotation, T &OutSupportA, T &OutSupportB)
Definition GJK.h:89
const FGeomGJKHelperSIMD & B
Definition GJK.h:128
VectorRegister4Float operator()(const VectorRegister4Float &V) const
Definition GJK.h:135
TSupportBAtOriginHelperSIMD(const FGeomGJKHelperSIMD &InB, const VectorRegister4Float &InAToBRotation, const VectorRegister4Float InBToARotation, const VectorRegister4Float &InStartPoint)
Definition GJK.h:130
const VectorRegister4Float StartPoint
Definition GJK.h:129
VectorRegister4Float operator()(const VectorRegister4Float &V) const
Definition GJK.h:122
const FGeomGJKHelperSIMD & B
Definition GJK.h:109
TSupportBAtOriginHelperSIMD(const FGeomGJKHelperSIMD &InB, const VectorRegister4Float &InAToBRotation, const VectorRegister4Float InBToARotation, const VectorRegister4Float &InStartPoint)
Definition GJK.h:114
const VectorRegister4Float AToBRotation
Definition GJK.h:110
const VectorRegister4Float StartPoint
Definition GJK.h:112
const VectorRegister4Float BToARotation
Definition GJK.h:111
static UE_FORCEINLINE_HINT bool IsNearlyEqual(float A, float B, float ErrorTolerance=UE_SMALL_NUMBER)
Definition UnrealMathUtility.h:388
static constexpr UE_FORCEINLINE_HINT T Square(const T A)
Definition UnrealMathUtility.h:578
Definition NumericLimits.h:41
Definition Quat.h:39
Definition Vector.h:51
Definition UnrealMathFPU.h:20