UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
EPA.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4// HEADER_UNIT_SKIP - Internal
5
6#include "Chaos/Simplex.h"
7#include <queue>
8#include "ChaosCheck.h"
9#include "ChaosLog.h"
10#include "Templates/Function.h"
11
12namespace Chaos
13{
14
15inline constexpr int32 ArraySizeEPA = 16;
16
17// Array type used in EPA to avoid heap allocation for small convex shapes
18// @todo(chaos): The inline size was picked to avoid allocations in box-box collision - it might need adjusting after more general purpose testing
19// @todo(chaos): We might also consider different inline sizes based on array use-case (e.g., Entries array versus Border array)
20template<typename T>
22
23template <typename T>
25{
26 return VertsABuffer[Idx] - VertsBBuffer[Idx];
27}
28
29template <typename T>
31{
33
34 TVec3<T> PlaneNormal; //Triangle normal
35 T Distance; //Triangle distance from origin
36 TVector<int32,3> AdjFaces; //Adjacent triangles
37 TVector<int32,3> AdjEdges; //Adjacent edges (idx in adjacent face)
38 bool bObsolete; //indicates that an entry can be skipped (became part of bigger polytope)
39
41 {
42 return Distance > Other.Distance;
43 }
44
45 static constexpr T SelectEpsilon(float FloatEpsilon, double DoubleEpsilon)
46 {
47 if (sizeof(T) <= sizeof(float))
48 {
49 return FloatEpsilon;
50 }
51 else
52 {
53 return DoubleEpsilon;
54 }
55 }
56
58 {
62
63 const TVec3<T> V0V1 = V1 - V0;
64 const TVec3<T> V0V2 = V2 - V0;
66 const T NormLenSq = Norm.SizeSquared();
67 // We have the square of the size of a cross product, so we need the distance margin to be a power of 4
68 // Verbosity emphasizes that
69 const T Eps = TEPAEntry::SelectEpsilon(1.e-4f * 1.e-4f * 1.e-4f * 1.e-4f, 1.e-8 * 1.e-8 * 1.e-8 * 1.e-8);
70 if (NormLenSq < Eps)
71 {
72 return false;
73 }
74 PlaneNormal = Norm * FMath::InvSqrt(NormLenSq);
75
76 IdxBuffer[0] = InIdx0;
77 IdxBuffer[1] = InIdx1;
78 IdxBuffer[2] = InIdx2;
79
82
84 bObsolete = false;
85
86 return true;
87 }
88
89 void SwapWinding(TEPAEntry* Entries)
90 {
91 //change vertex order
92 std::swap(IdxBuffer[0], IdxBuffer[1]);
93
94 //edges went from 0,1,2 to 1,0,2
95 //0th edge/face is the same (0,1 becomes 1,0)
96 //1th edge/face is now (0,2 instead of 1,2)
97 //2nd edge/face is now (2,1 instead of 2,0)
98
99 //update the adjacent face's adjacent edge first
100 auto UpdateAdjEdge = [Entries, this](int32 Old, int32 New)
101 {
102 TEPAEntry& AdjFace = Entries[AdjFaces[Old]];
104 check(StaleAdjIdx == Old);
106 };
107
108 UpdateAdjEdge(1, 2);
109 UpdateAdjEdge(2, 1);
110
111 //now swap the actual edges and faces
112 std::swap(AdjFaces[1], AdjFaces[2]);
113 std::swap(AdjEdges[1], AdjEdges[2]);
114
117 }
118
123
124 bool IsOriginProjectedInside(const TVec3<T>* VertsABuffer, const TVec3<T>* VertsBBuffer, const T Epsilon) const
125 {
126 //Compare the projected point (PlaneNormal) to the triangle in the plane
131
136
137 if((PACSign < -Epsilon && PCBSign > Epsilon) || (PACSign > Epsilon && PCBSign < -Epsilon))
138 {
139 return false;
140 }
141
144
145 if((PACSign < -Epsilon && PBASign > Epsilon) || (PACSign > Epsilon && PBASign < -Epsilon))
146 {
147 return false;
148 }
149
150 return true;
151 }
152};
153
154template <typename T>
156{
157 const int32 NumVerts = VertsA.Num();
158 check(VertsB.Num() == NumVerts);
159
160 auto AddFartherPoint = [&](const TVec3<T>& Dir)
161 {
162 const TVec3<T> NegDir = -Dir;
163 const TVec3<T> A0 = SupportA(Dir); //should we have a function that does both directions at once?
164 const TVec3<T> A1 = SupportA(NegDir);
165 const TVec3<T> B0 = SupportB(NegDir);
166 const TVec3<T> B1 = SupportB(Dir);
167
168 const TVec3<T> W0 = A0 - B0;
169 const TVec3<T> W1 = A1 - B1;
170
171 const T Dist0 = TVec3<T>::DotProduct(W0, Dir);
173
174 if (Dist1 >= Dist0)
175 {
176 VertsA.Add(A1);
177 VertsB.Add(B1);
178 }
179 else
180 {
181 VertsA.Add(A0);
182 VertsB.Add(B0);
183 }
184 };
185
186 OutEntries.AddUninitialized(4);
187 OutTouchNormal = TVec3<T>(0,0,1);
188
189 bool bValid = false;
190
191 switch(NumVerts)
192 {
193 case 1:
194 {
195 //assuming it's a touching hit at origin, but we still need to calculate a separating normal
196 AddFartherPoint(OutTouchNormal); // Use an arbitrary direction
197
198 // Now we might have a line! So fall trough to the next case
199 }
200 case 2:
201 {
202 //line, add farthest point along most orthogonal axes
203 TVec3<T> Dir = MinkowskiVert(VertsA.GetData(), VertsB.GetData(), 1) - MinkowskiVert(VertsA.GetData(), VertsB.GetData(), 0);
204
205 bValid = Dir.SizeSquared() > 1e-4;
206 if (bValid) //two verts given are distinct
207 {
208 //find most opposing axis
209 int32 BestAxis = 0;
211 for (int32 Axis = 0; Axis < 3; ++Axis)
212 {
213 const T AbsVal = FMath::Abs(Dir[Axis]);
214 if (MinVal > AbsVal)
215 {
216 BestAxis = Axis;
217 MinVal = AbsVal;
218 }
219 }
223
226
227 bValid = OutEntries[0].Initialize(VertsA.GetData(), VertsB.GetData(), 1, 2, 3, { 3,1,2 }, { 1,1,1 });
228 bValid &= OutEntries[1].Initialize(VertsA.GetData(), VertsB.GetData(), 0, 3, 2, { 2,0,3 }, { 2,1,0 });
229 bValid &= OutEntries[2].Initialize(VertsA.GetData(), VertsB.GetData(), 0, 1, 3, { 3,0,1 }, { 2,2,0 });
230 bValid &= OutEntries[3].Initialize(VertsA.GetData(), VertsB.GetData(), 0, 2, 1, { 1,0,2 }, { 2,0,0 });
231
232 if(!bValid)
233 {
234 OutTouchNormal = Orthog.GetUnsafeNormal();
235 return false;
236 }
237 }
238 else
239 {
240 // The two vertices are not distinct that may happen when the single vertex case above was hit and our CSO is very thin in that direction
241 CHAOS_ENSURE(NumVerts == 1); // If this ensure fires we were given 2 vertices that are not distinct to start with
242 return false;
243 }
244 break;
245 }
246 case 3:
247 {
248 //triangle, add farthest point along normal
249 bValid = OutEntries[3].Initialize(VertsA.GetData(), VertsB.GetData(), 0, 2, 1, { 1,0,2 }, { 2,0,0 });
250 if (CHAOS_ENSURE(bValid)) //input verts must form a valid triangle
251 {
252 const TEPAEntry<T>& Base = OutEntries[3];
253
254 AddFartherPoint(Base.PlaneNormal);
255
256 bValid = OutEntries[0].Initialize(VertsA.GetData(), VertsB.GetData(), 1, 2, 3, { 3,1,2 }, { 1,1,1 });
257 bValid &= OutEntries[1].Initialize(VertsA.GetData(), VertsB.GetData(), 0, 3, 2, { 2,0,3 }, { 2,1,0 });
258 bValid &= OutEntries[2].Initialize(VertsA.GetData(), VertsB.GetData(), 0, 1, 3, { 3,0,1 }, { 2,2,0 });
259
260 if(!bValid)
261 {
262 OutTouchNormal = Base.PlaneNormal;
263 return false;
264 }
265 }
266 break;
267 }
268 case 4:
269 {
270 bValid = OutEntries[0].Initialize(VertsA.GetData(), VertsB.GetData(), 1, 2, 3, { 3,1,2 }, { 1,1,1 });
271 bValid &= OutEntries[1].Initialize(VertsA.GetData(), VertsB.GetData(), 0, 3, 2, { 2,0,3 }, { 2,1,0 });
272 bValid &= OutEntries[2].Initialize(VertsA.GetData(), VertsB.GetData(), 0, 1, 3, { 3,0,1 }, { 2,2,0 });
273 bValid &= OutEntries[3].Initialize(VertsA.GetData(), VertsB.GetData(), 0, 2, 1, { 1,0,2 }, { 2,0,0 });
274
275 if(!bValid)
276 {
277 CHAOS_ENSURE(false); //expect user to give us valid tetrahedron
278 UE_LOG(LogChaos, Log, TEXT("Invalid tetrahedron encountered in InitializeEPA"));
279 }
280
281 break;
282 }
283
284 default:
285 {
286 CHAOS_ENSURE(false);
287 break;
288 }
289 }
290
291 if (bValid)
292 {
293 //make sure normals are pointing out of tetrahedron
294 // In the usual case the distances will either all be positive or negative,
295 // but the tetrahedron can be very close to (or touching) the origin
296 // Look for farthest plane to decide
297 T MaxSignedDistance = 0;
298 for (TEPAEntry<T>& Entry : OutEntries)
299 {
300 if (FMath::Abs(Entry.Distance) > FMath::Abs(MaxSignedDistance))
301 {
302 MaxSignedDistance = Entry.Distance;
303 }
304 }
305
306 if (MaxSignedDistance < 0.0f)
307 {
308 for (TEPAEntry<T>& Entry : OutEntries)
309 {
310 Entry.SwapWinding(OutEntries.GetData());
311 }
312 }
313 }
314
315 return bValid;
316}
317
318template <typename T, typename SupportALambda, typename SupportBLambda >
326
332
333template <typename T>
335{
336 {
337 TEPAEntry<T>& Entry = Entries[EntryIdx];
338 for (int i = 0; i < 3; ++i)
339 {
340 ToVisitStack.Add({ Entry.AdjFaces[i], Entry.AdjEdges[i] });
341 }
342 }
343
344 int32 Iteration = 0;
345 const int32 MaxIteration = 10000;
346
347 while (ToVisitStack.Num() && Iteration++ < MaxIteration)
348 {
350 TEPAEntry<T>& Entry = Entries[FloodEntry.EntryIdx];
351 if (!Entry.bObsolete)
352 {
353 if (Entry.DistanceToPlane(W) <= TEPAEntry<T>::SelectEpsilon(1.e-4f, 1.e-8))
354 {
355 //W can't see this triangle so mark the edge as a border
357 }
358 else
359 {
360 //W can see this triangle so continue flood fill
361 Entry.bObsolete = true; //no longer needed
362 const int32 Idx0 = FloodEntry.EdgeIdx;
363 const int32 Idx1 = (Idx0 + 1) % 3;
364 const int32 Idx2 = (Idx0 + 2) % 3;
365 ToVisitStack.Add({ Entry.AdjFaces[Idx1], Entry.AdjEdges[Idx1] });
366 ToVisitStack.Add({ Entry.AdjFaces[Idx2], Entry.AdjEdges[Idx2] });
367 }
368 }
369 }
370
371 if(Iteration >= MaxIteration)
372 {
373 UE_LOG(LogChaos,Warning,TEXT("EPAComputeVisibilityBorder reached max iteration - something is wrong"));
374 }
375}
376
377template <typename T>
379{
380 //NOTE: We use this function as fallback when robustness breaks. So - do not assume adjacency is valid as these may be new uninitialized traingles that failed
381 FSimplex SimplexIDs({ 0,1,2 });
382 TVec3<T> As[4] = { VertsA[Entry.IdxBuffer[0]], VertsA[Entry.IdxBuffer[1]], VertsA[Entry.IdxBuffer[2]] };
383 TVec3<T> Bs[4] = { VertsB[Entry.IdxBuffer[0]], VertsB[Entry.IdxBuffer[1]], VertsB[Entry.IdxBuffer[2]] };
384 TVec3<T> Simplex[4] = { As[0] - Bs[0], As[1] - Bs[1], As[2] - Bs[2] };
385 T Barycentric[4];
386
387 OutDir = SimplexFindClosestToOrigin(Simplex, SimplexIDs, Barycentric, As, Bs);
388 OutPenetration = OutDir.Size();
389
390 // @todo(chaos): pass in epsilon? Does it need to match anything in GJK?
391 if (OutPenetration < 1e-4) //if closest point is on the origin (edge case when surface is right on the origin)
392 {
393 OutDir = Entry.PlaneNormal; //just fall back on plane normal
394 if (Entry.Distance < 0)
395 {
396 OutPenetration = -OutPenetration; // We are a bit outside of the shape so penetration is negative
397 }
398 }
399 else
400 {
402 if (Entry.Distance < 0)
403 {
404 //The origin is on the outside, so the direction is reversed
405 OutDir = -OutDir;
406 OutPenetration = -OutPenetration; // We are a bit outside of the shape so penetration is negative
407 }
408 }
409
410 OutA = TVec3<T>(0);
411 OutB = TVec3<T>(0);
412
413 for (int i = 0; i < SimplexIDs.NumVerts; ++i)
414 {
415 OutA += As[i] * Barycentric[i];
416 OutB += Bs[i] * Barycentric[i];
417 }
418}
419
420enum class EEPAResult
421{
422 Ok, // Successfully found the contact point to within the tolerance
423 MaxIterations, // We have a contact point, but did not reach the target tolerance before we hit the iteration limit - result accuracy is unknown
424 Degenerate, // We hit a degenerate condition in EPA which prevents a solution from being generated (result is invalid but objects may be penetrating)
425 BadInitialSimplex, // The initial setup did not provide a polytope containing the origin (objects are separated)
426 NoValidContact, // No valid contact have been found, no contacts will be returned
427};
428
429UE_DEPRECATED(5.3, "Not used")
434
435#ifndef DEBUG_EPA
436#define DEBUG_EPA 0
437#endif
438
439// Expanding Polytope Algorithm for finding the contact point for overlapping convex polyhedra.
440// See e.g., "Collision Detection in Interactive 3D Environments" (Gino van den Bergen, 2004)
441// or "Real-time Collision Detection with Implicit Objects" (Leif Olvang, 2010)
442template <typename T, typename TSupportA, typename TSupportB>
449
450template <typename T>
453{
454 struct FEPAEntryWrapper
455 {
456 const TArray<TEPAEntry<T>>* Entries;
457 int32 Idx;
458
459 bool operator>(const FEPAEntryWrapper& Other) const
460 {
461 return (*Entries)[Idx] > (*Entries)[Other.Idx];
462 }
463 };
464
465 constexpr T OriginInsideEps = 0.0f;
466
468
470 {
471 //either degenerate or a touching hit. Either way return penetration 0
472 OutPenetration = 0;
473 WitnessA = TVec3<T>(0);
474 WitnessB = TVec3<T>(0);
476 }
477
478#if DEBUG_EPA
480 for (int32 Idx = 0; Idx < 4; ++Idx)
481 {
482 VertsWBuffer.Add(MinkowskiVert(VertsABuffer.GetData(), VertsBBuffer.GetData(), Idx));
483 }
484#endif
485
487 for(int32 Idx = 0; Idx < Entries.Num(); ++Idx)
488 {
489 //ensure(Entries[Idx].Distance > -Eps);
490 // Entries[Idx].Distance <= 0.0f is true if the origin is a bit out of the polytope (we need to support this case for robustness)
491 if(Entries[Idx].Distance <= 0.0f || Entries[Idx].IsOriginProjectedInside(VertsABuffer.GetData(), VertsBBuffer.GetData(), OriginInsideEps))
492 {
493 Queue.Add(Idx);
494 }
495 }
496
499
500 //TEPAEntry<T> BestEntry;
501 //BestEntry.Distance = 0;
502 TEPAEntry<T> LastEntry = Queue.Num() > 0 ? Entries[Queue.Last()] : Entries[0];
503 T UpperBound = TNumericLimits<T>::Max();
504 T LowerBound = TNumericLimits<T>::Lowest();
505 bool bQueueDirty = true;
506 int32 Iteration = 0;
507 int32 constexpr MaxIterations = 128;
509 while (Queue.Num() && (Iteration++ < MaxIterations))
510 {
511 if (bQueueDirty)
512 {
513 // Avoiding UE's Sort here because it has a call to FMath::Loge. The std version calls insertion sort when possible
514 std::sort(Queue.GetData(), Queue.GetData() + Queue.Num(), [&Entries](const int32 L, const int32 R) { return Entries[L] > Entries[R]; });
515 bQueueDirty = false;
516 }
517
518 int32 EntryIdx = Queue.Pop(EAllowShrinking::No);
519 TEPAEntry<T>& Entry = Entries[EntryIdx];
520 //bool bBadFace = Entry.IsOriginProjectedInside(VertsABuffer.GetData(), VertsBBuffer.GetData());
521 {
522 //UE_LOG(LogChaos, Warning, TEXT("%d BestW:%f, Distance:%f, bObsolete:%d, InTriangle:%d"),
523 // Iteration, BestW.Size(), Entry.Distance, Entry.bObsolete, bBadFace);
524 }
525 if (Entry.bObsolete)
526 {
527 // @todo(chaos): should this count as an iteration? Currently it does...
528 continue;
529 }
530
531 if (Entry.Distance > UpperBound)
532 {
534 break;
535 }
536
537 const TVec3<T> ASupport = SupportA(Entry.PlaneNormal);
538 const TVec3<T> BSupport = SupportB(-Entry.PlaneNormal);
539 const TVec3<T> W = ASupport - BSupport;
541 if(DistanceToSupportPlane < UpperBound)
542 {
543 UpperBound = DistanceToSupportPlane;
544 //Remember the entry that gave us the lowest upper bound and use it in case we have to terminate early
545 //This can result in very deep planes. Ideally we'd just use the plane formed at W, but it's not clear how you get back points in A, B for that
546 //BestEntry = Entry;
547 }
548
549 LowerBound = Entry.Distance;
550
551 // It's possible the origin is not contained by the CSO, probably because of numerical error.
552 // In this case the upper bound will be negative, at which point we should just exit.
553 const T UpperBoundTolerance = (T(1) + EpsRel) * FMath::Abs(LowerBound);
554 if (UpperBound <= UpperBoundTolerance)
555 {
557 LastEntry = Entry;
558 break;
559 }
560
561 if (UpperBound < LowerBound)
562 {
563 //we cannot get any better than what we saw, so just return previous face
565 break;
566 }
567
568 LastEntry = Entry;
569
570
573
574#if DEBUG_EPA
576#endif
577
578 Entry.bObsolete = true;
579 VisibilityBorder.Reset();
583 const int32 FirstIdxInBatch = Entries.Num();
586 if (NumBorderEdges >= 3)
587 {
588 bool bTerminate = false;
590 {
591 //create new entries and update adjacencies
593 TEPAEntry<T>& NewEntry = Entries[NewIdx];
594 const int32 BorderEntryIdx = BorderInfo.EntryIdx;
596 const int32 BorderEdgeIdx0 = BorderInfo.EdgeIdx;
597 const int32 BorderEdgeIdx1 = (BorderEdgeIdx0 + 1) % 3;
600 const bool bValidTri = NewEntry.Initialize(VertsABuffer.GetData(), VertsBBuffer.GetData(), BorderEntry.IdxBuffer[BorderEdgeIdx1], BorderEntry.IdxBuffer[BorderEdgeIdx0], NewVertIdx,
602 { BorderEdgeIdx0, 2, 1 });
604 BorderEntry.AdjEdges[BorderEdgeIdx0] = 0;
605
606 if (!bValidTri)
607 {
608 //couldn't properly expand polytope, so just stop
610 bTerminate = true;
611 break;
612 }
613
614 // Due to numerical inaccuracies NewEntry.Distance >= LowerBound may be false!
615 // However these Entries still have good normals, and needs to be included to prevent
616 // this exiting with very deep penetration results
617
618 if (bValidTri && NewEntry.Distance <= UpperBound)
619 {
620 // NewEntry.Distance <= 0.0f is if the origin is a bit out of the polytope
621 if (NewEntry.Distance <= 0.0f || NewEntry.IsOriginProjectedInside(VertsABuffer.GetData(), VertsBBuffer.GetData(), OriginInsideEps))
622 {
623 Queue.Add(NewIdx);
624 bQueueDirty = true;
625 }
626 }
627
628 ++NewIdx;
629 }
630
631 if (bTerminate)
632 {
633 break;
634 }
635 }
636 else
637 {
638 //couldn't properly expand polytope, just stop now
640 break;
641 }
642 }
643
645
646 return ResultStatus;
647}
648
649}
#define FORCEINLINE
Definition AndroidPlatform.h:140
#define check(expr)
Definition AssertionMacros.h:314
#define CHAOS_ENSURE(Condition)
Definition ChaosCheck.h:22
#define FORCEINLINE_DEBUGGABLE
Definition CoreMiscDefines.h:74
#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
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define UE_LOG(CategoryName, Verbosity, Format,...)
Definition LogMacros.h:270
#define W1
T * New(FMemStackBase &Mem, int32 Count=1, int32 Align=DEFAULT_ALIGNMENT)
Definition MemStack.h:259
UE_REWRITE constexpr bool operator>(const LhsType &Lhs, const RhsType &Rhs)
Definition UEOps.h:90
Definition Vector.h:1000
static FORCEINLINE TVector< T, 3 > AxisVector(const int32 Axis)
Definition Vector.h:1061
FORCEINLINE T SizeSquared() const
Definition Vector.h:1067
Definition Vector.h:41
Definition Array.h:670
UE_FORCEINLINE_HINT SizeType AddUninitialized()
Definition Array.h:1664
UE_REWRITE SizeType Num() const
Definition Array.h:1144
UE_NODEBUG UE_FORCEINLINE_HINT ElementType & Last(SizeType IndexFromTheEnd=0) UE_LIFETIMEBOUND
Definition Array.h:1263
UE_NODEBUG UE_FORCEINLINE_HINT ElementType * GetData() UE_LIFETIMEBOUND
Definition Array.h:1027
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
ElementType Pop(EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:1196
Definition AssetRegistryState.h:50
Definition SkeletalMeshComponent.h:307
void ComputeEPAResults(const TVec3< T > *VertsA, const TVec3< T > *VertsB, const TEPAEntry< T > &Entry, T &OutPenetration, TVec3< T > &OutDir, TVec3< T > &OutA, TVec3< T > &OutB)
Definition EPA.h:378
constexpr int32 ArraySizeEPA
Definition EPA.h:15
@ X
Definition SimulationModuleBase.h:152
FRealDouble FReal
Definition Real.h:22
const bool IsEPASuccess(EEPAResult EPAResult)
Definition EPA.h:430
void EPAComputeVisibilityBorder(TEPAWorkingArray< TEPAEntry< T > > &Entries, int32 EntryIdx, const TVec3< T > &W, TEPAWorkingArray< FEPAFloodEntry > &OutBorderEdges, TEPAWorkingArray< FEPAFloodEntry > &ToVisitStack)
Definition EPA.h:334
bool InitializeEPA(TArray< TVec3< T > > &VertsA, TArray< TVec3< T > > &VertsB, TFunctionRef< TVector< T, 3 >(const TVec3< T > &V)> SupportA, TFunctionRef< TVector< T, 3 >(const TVec3< T > &V)> SupportB, TEPAWorkingArray< TEPAEntry< T > > &OutEntries, TVec3< T > &OutTouchNormal)
Definition EPA.h:155
EEPAResult
Definition EPA.h:421
FORCEINLINE const TVec3< T > MinkowskiVert(const TVec3< T > *VertsABuffer, const TVec3< T > *VertsBBuffer, const int32 Idx)
Definition EPA.h:24
EEPAResult EPA(TArray< TVec3< T > > &VertsABuffer, TArray< TVec3< T > > &VertsBBuffer, const TSupportA &SupportA, const TSupportB &SupportB, T &OutPenetration, TVec3< T > &OutDir, TVec3< T > &WitnessA, TVec3< T > &WitnessB, const FReal Eps=1.e-2f)
Definition EPA.h:443
TVec3< T > SimplexFindClosestToOrigin(TVec3< T > *Simplex, FSimplex &Idxs, T *OutBarycentric, TVec3< T > *A=nullptr, TVec3< T > *B=nullptr)
Definition Simplex.h:587
Definition EPA.h:328
int32 EdgeIdx
Definition EPA.h:330
int32 EntryIdx
Definition EPA.h:329
Definition Simplex.h:84
Definition EPA.h:31
void SwapWinding(TEPAEntry *Entries)
Definition EPA.h:89
bool IsOriginProjectedInside(const TVec3< T > *VertsABuffer, const TVec3< T > *VertsBBuffer, const T Epsilon) const
Definition EPA.h:124
int32 IdxBuffer[3]
Definition EPA.h:32
bool bObsolete
Definition EPA.h:38
FORCEINLINE_DEBUGGABLE T DistanceToPlane(const TVec3< T > &X) const
Definition EPA.h:119
TVector< int32, 3 > AdjFaces
Definition EPA.h:36
TVec3< T > PlaneNormal
Definition EPA.h:34
FORCEINLINE_DEBUGGABLE bool Initialize(const TVec3< T > *VerticesA, const TVec3< T > *VerticesB, int32 InIdx0, int32 InIdx1, int32 InIdx2, const TVector< int32, 3 > &InAdjFaces, const TVector< int32, 3 > &InAdjEdges)
Definition EPA.h:57
T Distance
Definition EPA.h:35
FORCEINLINE bool operator>(const TEPAEntry< T > &Other) const
Definition EPA.h:40
TVector< int32, 3 > AdjEdges
Definition EPA.h:37
static constexpr T SelectEpsilon(float FloatEpsilon, double DoubleEpsilon)
Definition EPA.h:45
Definition NumericLimits.h:41