UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
Simplex.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4#include "Chaos/Core.h"
5
6namespace Chaos
7{
8 template <typename T, int d>
10 {
11 const TVector<T, d>& X0 = Simplex[Idxs[0]];
12 const TVector<T, d>& X1 = Simplex[Idxs[1]];
13 const TVector<T, d> X0ToX1 = X1 - X0;
14
15 //Closest Point = (-X0 dot X1-X0) / ||(X1-X0)||^2 * (X1-X0)
16
19
20 if (Dot <= 0)
21 {
22 NumVerts = 1;
23 OutBarycentric[Idxs[0]] = 1;
24 return X0;
25 }
26
27 const T X0ToX1Squared = X0ToX1.SizeSquared();
28
29 if (X0ToX1Squared <= Dot || X0ToX1Squared <= std::numeric_limits<T>::min()) //if dividing gives 1+ or the line is degenerate
30 {
31 NumVerts = 1;
32 Idxs[0] = Idxs[1];
33 OutBarycentric[Idxs[1]] = 1;
34 return X1;
35 }
36
37 const T Ratio = FMath::Clamp(Dot / X0ToX1Squared, T(0), T(1));
38 const TVector<T, d> Closest = Ratio * (X0ToX1)+X0; //note: this could pass X1 by machine epsilon, but doesn't seem worth it for now
39 OutBarycentric[Idxs[0]] = 1 - Ratio;
40 OutBarycentric[Idxs[1]] = Ratio;
41 return Closest;
42 }
43
44 template <typename T>
46 {
47 const TVec3<T>& X0 = Simplex[0];
48 const TVec3<T>& X1 = Simplex[1];
49 const TVec3<T> X0ToX1 = X1 - X0;
50
51 //Closest Point = (-X0 dot X1-X0) / ||(X1-X0)||^2 * (X1-X0)
52
53 const TVec3<T> X0ToOrigin = -X0;
55
56 if (Dot <= 0)
57 {
58 NumVerts = 1;
59 OutBarycentric[0] = 1;
60 return X0;
61 }
62
63 const T X0ToX1Squared = X0ToX1.SizeSquared();
64
65 if (X0ToX1Squared <= Dot || X0ToX1Squared <= std::numeric_limits<T>::min()) //if dividing gives 1+ or the line is degenerate
66 {
67 NumVerts = 1;
68 OutBarycentric[0] = 1;
69 Simplex[0] = Simplex[1];
70 A[0] = A[1];
71 B[0] = B[1];
72 return X1;
73 }
74
75 const T Ratio = FMath::Clamp(Dot / X0ToX1Squared, T(0), T(1));
76 const TVec3<T> Closest = Ratio * (X0ToX1)+X0; //note: this could pass X1 by machine epsilon, but doesn't seem worth it for now
77 OutBarycentric[0] = 1 - Ratio;
78 OutBarycentric[1] = Ratio;
79 return Closest;
80 }
81
82
83 struct FSimplex
84 {
87 int32 operator[](int32 Idx) const { return Idxs[Idx]; }
88 int32& operator[](int32 Idx) { return Idxs[Idx]; }
89
90 FSimplex(std::initializer_list<int32> InIdxs = {})
91 : NumVerts(InIdxs.size())
92 {
93 check(NumVerts <= 4);
94 int32 i = 0;
95 for (int32 Idx : InIdxs)
96 {
97 Idxs[i++] = Idx;
98 }
99 while (i < 4)
100 {
101 Idxs[i++] = 0; //some code uses these for lookup regardless of NumVerts. Makes for faster code so just use 0 to make lookup safe
102 }
103 }
104 };
105
106 template <typename T>
107 bool SignMatch(T A, T B)
108 {
109 return (A > 0 && B > 0) || (A < 0 && B < 0);
110 }
111
112 template <typename T>
114 {
115 /* Project the origin onto the triangle plane:
116 Let n = (b-a) cross (c-a)
117 Let the distance from the origin dist = ((-a) dot n / ||n||^2)
118 Then the projection p = 0 - dist * n = (a dot n) / ||n||^2
119 */
120
121 const int32 Idx0 = Idxs[0];
122 const int32 Idx1 = Idxs[1];
123 const int32 Idx2 = Idxs[2];
124
125 const TVec3<T>& X0 = Simplex[Idx0];
126 const TVec3<T>& X1 = Simplex[Idx1];
127 const TVec3<T>& X2 = Simplex[Idx2];
128
129 const TVec3<T> X0ToX1 = X1 - X0;
130 const TVec3<T> X0ToX2 = X2 - X0;
132
133 /*
134 We want |(a dot n) / ||n||^2| < 1 / eps to avoid inf. But note that |a dot n| <= ||a||||n|| and so
135 |(a dot n) / ||n||^2| <= ||a|| / ||n| < 1 / eps requires that ||eps*a||^2 < ||n||^2
136 */
137 const T TriNormal2 = TriNormal.SizeSquared();
138 const T Eps2 = (X0*std::numeric_limits<T>::min()).SizeSquared();
139 if (Eps2 >= TriNormal2) //equality fixes case where both X0 and TriNormal2 are 0
140 {
141 //degenerate triangle so return previous line results
142 Idxs.NumVerts = 2;
144 }
145
147 const T SignedDistance = TVec3<T>::DotProduct(X0, TriNormalOverSize2);
148 const TVec3<T> ProjectedOrigin = TriNormal * SignedDistance;
149
150 /*
151 Let p be the origin projected onto the triangle plane. We can represent the point p in a 2d subspace spanned by the triangle
152 |a_u, b_u, c_u| |lambda_1| = |p_u|
153 |a_v, b_v, c_v| |lambda_2| = |p_v|
154 |1, 1, 1 | |lambda_3| = |1 |
155
156 Cramer's rule gives: lambda_i = det(M_i) / det(M)
157 To choose u and v we simply test x,y,z to see if any of them are linearly independent
158 */
159
160 T DetM = 0; //not needed but fixes compiler warning
162 int32 BestAxisV = 0; //once best axis is chosen use the axes that go with it in the right order
163
164 {
165 T MaxAbsDetM = 0; //not needed but fixes compiler warning
166 int32 AxisU = 1;
167 int32 AxisV = 2;
168 for (int32 CurAxis = 0; CurAxis < 3; ++CurAxis)
169 {
170 T TmpDetM = X1[AxisU] * X2[AxisV] - X2[AxisU] * X1[AxisV]
171 + X2[AxisU] * X0[AxisV] - X0[AxisU] * X2[AxisV]
172 + X0[AxisU] * X1[AxisV] - X1[AxisU] * X0[AxisV];
173 const T AbsDetM = FMath::Abs(TmpDetM);
175 {
177 DetM = TmpDetM;
180 }
181 AxisU = AxisV;
182 AxisV = CurAxis;
183 }
184 }
185
186 /*
187 Now solve for the cofactors (i.e. the projected origin replaces the column of each cofactor).
188 Notice that this is really the area of each sub triangle with the projected origin.
189 If the sign of the determinants is different than the sign of the entire triangle determinant then we are outside of the triangle.
190 The conflicting signs indicate which voronoi regions to search
191
192 Cofactor_a = |p_u b_u c_u| Cofactor_b = |a_u p_u c_u| Cofactor_c = |a_u b_u p_u|
193 det|p_v b_v c_v| det|a_v p_v c_v| det|a_v c_v p_v|
194 |1 1 1 | |1 1 1 | |1 1 1 |
195 */
196 const TVec3<T>& P0 = ProjectedOrigin;
197 const TVec3<T> P0ToX0 = X0 - P0;
198 const TVec3<T> P0ToX1 = X1 - P0;
199 const TVec3<T> P0ToX2 = X2 - P0;
200
201 const T Cofactors[3] = {
205 };
206
207 bool bSignMatch[3];
208 FSimplex SubSimplices[3] = { {Idx1,Idx2}, {Idx0,Idx2}, {Idx0,Idx1} };
210 T SubBarycentric[3][4];
212 T MinSubDist2 = 0; //not needed
213 bool bInside = true;
214 for (int32 Idx = 0; Idx < 3; ++Idx)
215 {
216 bSignMatch[Idx] = SignMatch(DetM, Cofactors[Idx]);
217 if (!bSignMatch[Idx])
218 {
219 bInside = false;
221
222 const T Dist2 = ClosestPointSub[Idx].SizeSquared();
224 {
226 ClosestSubIdx = Idx;
227 }
228 }
229 }
230
231 if (bInside)
232 {
233 //SignMatch ensures that DetM is not 0. The Det_i / Det_m ratio is always between 0-1 because it represents the ratio of areas and Det_m is the total area
234 const T InvDetM = 1 / DetM;
235 T Lambda0 = Cofactors[0] * InvDetM;
236 T Lambda1 = Cofactors[1] * InvDetM;
237 //T Lambda2 = 1 - Lambda1 - Lambda0;
238 T Lambda2 = Cofactors[2] * InvDetM;
242
243 // We know that we are inside the triangle so we can use the projected point we calculated above.
244 // The closest point can also be derived from the barycentric coordinates, but it will contain
245 // numerical error from the determinant calculation and can cause GJK to terminate with a poor solution.
246 // (E.g., this caused jittering when walking on box with dimensions of 100000cm or more).
247 // return X0 * Lambda0 + X1 * Lambda1 + X2 * Lambda2;
248 return ProjectedOrigin;
249 }
250 else
251 {
252 check(Idx0 >= 0 && Idx0 < 4);
253 check(Idx1 >= 0 && Idx1 < 4);
254 check(Idx2 >= 0 && Idx2 < 4);
260 }
261 }
262
263 template <typename T>
265 {
266 const TVec3<T>& A = Simplex[0];
267 const TVec3<T>& B = Simplex[1];
268 const TVec3<T>& C = Simplex[2];
269
270 // Vertex region A
271 const TVec3<T> AB = B - A;
272 const TVec3<T> AC = C - A;
273 const TVec3<T> AO = -A;
274
275 const T d1 = TVec3<T>::DotProduct(AB, AO);
276 const T d2 = TVec3<T>::DotProduct(AC, AO);
277
278 const bool bIsD1LEZero = (d1 <= T(0));
279 const bool bIsD2LEZero = (d2 <= T(0));
280 const bool bIsA = bIsD1LEZero && bIsD2LEZero;
281 if (bIsA)
282 {
283 NumVerts = 1;
284 OutBarycentric[0] = T(1);
285 return A;
286 }
287
288 //Vertex region B
289 const TVec3<T> BO = -B;
290 const T d3 = TVec3<T>::DotProduct(AB, BO);
291 const T d4 = TVec3<T>::DotProduct(AC, BO);
292
293 const bool bIsD3GEZero = (d3 >= T(0));
294 const bool bIsD3GED4 = (d3 >= d4);
295 const bool bIsB = bIsD3GEZero && bIsD3GED4;
296 if (bIsB)
297 {
298 NumVerts = 1;
299 OutBarycentric[0] = T(1);
300 Simplex[0] = B;
301 As[0] = As[1];
302 Bs[0] = Bs[1];
303 return B;
304 }
305
306 // Edge AB
307 const T d1d4 = d1 * d4;
308 const T vc = d1d4 - d2 * d3;
309 const T NormalizationDenominatorAB = d1 - d3;
310
311 const bool bIsZeroGEvc = (vc <= T(0));
312 const bool bIsD1GEZero = (d1 >= T(0));
313 const bool bIsZeroGED3 = (d3 <= T(0));
314 const bool bIsNDABGTZero = (NormalizationDenominatorAB > T(0));
316
317 if (bIsAB)
318 {
319 NumVerts = 2;
320 const T V = d1 / NormalizationDenominatorAB;
321 const T OneMinusV = T(1) - V;
323 OutBarycentric[1] = V;
324 return A + V * AB;
325 }
326
327 // Vertex C
328 const TVec3<T> CO = -C;
329 const T d5 = TVec3<T>::DotProduct(AB, CO);
330 const T d6 = TVec3<T>::DotProduct(AC, CO);
331 const bool bIsD6GEZero = (d6 >= T(0));
332 const bool bIsD6GED5 = (d6 >= d5);
333 const bool bIsC = bIsD6GEZero && bIsD6GED5;
334 if (bIsC)
335 {
336 NumVerts = 1;
337 OutBarycentric[0] = T(1);
338 Simplex[0] = C;
339 As[0] = As[2];
340 Bs[0] = Bs[2];
341 return C;
342 }
343
344 // Edge AC
345 const T d5d2 = d5 * d2;
346 const T vb = d5d2 - d1 * d6;
347 const T NormalizationDenominatorAC = d2 - d6;
348
349 const bool bIsZeroGEvb = (vb <= T(0));
350 const bool bIsD2GEZero = (d2 >= T(0));
351 const bool bIsZeroGED6 = (d6 <= T(0));
352 const bool bIsNDACGTZero = (NormalizationDenominatorAC > T(0));
354 if (bIsAC)
355 {
356 const T W = d2 / NormalizationDenominatorAC;
357 const T OneMinusW = T(1) - W;
358 NumVerts = 2;
360 OutBarycentric[1] = W;
361 Simplex[1] = C;
362 As[1] = As[2];
363 Bs[1] = Bs[2];
364 return A + W * AC;
365 }
366
367 // Edge BC
368 const T d3d6 = d3 * d6;
369 const T va = d3d6 - d5 * d4;
370 const T d4MinusD3 = d4 - d3;
371 const T d5MinusD6 = d5 - d6;
373
374 const bool bIsZeroGEva = (va <= T(0));
375 const bool bIsD4MinusD3GEZero = (d4MinusD3 >= T(0));
376 const bool bIsD5MinusD6GEZero = (d5MinusD6 >= T(0));
377 const bool bIsNDBCGTZero = (NormalizationDenominatorBC > T(0));
379 if (bIsBC)
380 {
382 const T OneMinusW = T(1) - W;
383 NumVerts = 2;
385 OutBarycentric[1] = W;
386 const TVec3<T> CMinusB = C - B;
387 const TVec3<T> Result = B + W * CMinusB;
388 Simplex[0] = B;
389 Simplex[1] = C;
390 As[0] = As[1];
391 Bs[0] = Bs[1];
392 As[1] = As[2];
393 Bs[1] = Bs[2];
394 return Result;
395 }
396
397 // Inside triangle
398 const T denom = T(1) / (va + vb + vc);
399 const T U = va * denom;
400 const T V = vb * denom;
401 const T W = vc * denom;
402 NumVerts = 3;
403 OutBarycentric[0] = U;
404 OutBarycentric[1] = V;
405 OutBarycentric[2] = W;
406
407 // We know that we are inside the triangle so we project the origin onto the plane
408 // The closest point can also be derived from the barycentric coordinates, but it will contain
409 // numerical error from the determinant calculation and can cause GJK to terminate with a poor solution.
410 // (E.g., this caused jittering when walking on box with dimensions of 100000cm or more).
411 // This fix the unit test TestSmallCapsuleLargeBoxGJKRaycast_Vertical
412 // Previously was return VectorMultiplyAdd(AC, w, VectorMultiplyAdd(AB, v, A));
415 if (TriNormal2 > std::numeric_limits<T>::min())
416 {
418 const T SignedDistance = TVec3<T>::DotProduct(A, TriNormalOverSize2);
419 return TriNormal * SignedDistance;
420 }
421
422 // If we get here we hit a degenerate Simplex (all 3 verts in a line/point)
423 // Let's just exit GJK with whatever normal and distance it had last iteration...
424 return FVec3(0);
425 }
426
427 template <typename T>
429 {
430 const int32 Idx0 = Idxs[0];
431 const int32 Idx1 = Idxs[1];
432 const int32 Idx2 = Idxs[2];
433 const int32 Idx3 = Idxs[3];
434
435 const TVec3<T>& X0 = Simplex[Idx0];
436 const TVec3<T>& X1 = Simplex[Idx1];
437 const TVec3<T>& X2 = Simplex[Idx2];
438 const TVec3<T>& X3 = Simplex[Idx3];
439
440 //Use signed volumes to determine if origin is inside or outside
441 /*
442 M = [X0x X1x X2x X3x;
443 X0y X1y X2y X3y;
444 X0z X1z X2z X3z;
445 1 1 1 1]
446 */
447
448 T Cofactors[4];
453 T DetM = (Cofactors[0] + Cofactors[1]) + (Cofactors[2] + Cofactors[3]);
454
455 bool bSignMatch[4];
456 FSimplex SubIdxs[4] = { {1,2,3}, {0,2,3}, {0,1,3}, {0,1,2} };
458 T SubBarycentric[4][4];
460 T MinTriangleDist2 = 0;
461
462 bool bInside = true;
463 for (int Idx = 0; Idx < 4; ++Idx)
464 {
465 bSignMatch[Idx] = SignMatch(DetM, Cofactors[Idx]);
466 if (!bSignMatch[Idx])
467 {
468 bInside = false;
470
471 const T Dist2 = ClosestPointSub[Idx].SizeSquared();
473 {
475 ClosestTriangleIdx = Idx;
476 }
477 }
478 }
479
480 if (bInside)
481 {
486
487 return TVec3<T>(0);
488 }
489
491
496
498 }
499
500 template <typename T>
502 {
503 const TVec3<T>& X0 = Simplex[0];
504 const TVec3<T>& X1 = Simplex[1];
505 const TVec3<T>& X2 = Simplex[2];
506 const TVec3<T>& X3 = Simplex[3];
507
508 //Use signed volumes to determine if origin is inside or outside
509 /*
510 M = [X0x X1x X2x X3x;
511 X0y X1y X2y X3y;
512 X0z X1z X2z X3z;
513 1 1 1 1]
514 */
515
516 T Cofactors[4];
521 T DetM = (Cofactors[0] + Cofactors[1]) + (Cofactors[2] + Cofactors[3]);
522
523 bool bSignMatch[4];
524 int32 SubNumVerts[4] = { 3, 3, 3, 3 };
525 TVec3<T> SubSimplices[4][3] = { {Simplex[1], Simplex[2], Simplex[3]}, {Simplex[0], Simplex[2], Simplex[3]}, {Simplex[0], Simplex[1], Simplex[3]}, {Simplex[0], Simplex[1], Simplex[2]} };
526 TVec3<T> SubAs[4][3] = { {A[1], A[2], A[3]}, {A[0], A[2], A[3]}, {A[0], A[1], A[3]}, {A[0], A[1], A[2]} };
527 TVec3<T> SubBs[4][3] = { {B[1], B[2], B[3]}, {B[0], B[2], B[3]}, {B[0], B[1], B[3]}, {B[0], B[1], B[2]} };
529 T SubBarycentric[4][4];
531 T MinTriangleDist2 = 0;
532
533 bool bInside = true;
534 for (int Idx = 0; Idx < 4; ++Idx)
535 {
536 bSignMatch[Idx] = SignMatch(DetM, Cofactors[Idx]);
537 if (!bSignMatch[Idx])
538 {
539 bInside = false;
541
542 const T Dist2 = ClosestPointSub[Idx].SizeSquared();
544 {
546 ClosestTriangleIdx = Idx;
547 }
548 }
549 }
550
551 if (bInside)
552 {
553 OutBarycentric[0] = Cofactors[0] / DetM;
554 OutBarycentric[1] = Cofactors[1] / DetM;
555 OutBarycentric[2] = Cofactors[2] / DetM;
556 OutBarycentric[3] = Cofactors[3] / DetM;
557
558 return TVec3<T>(0);
559 }
560
562 for (int i = 0; i < 3; i++)
563 {
566 A[i] = SubAs[ClosestTriangleIdx][i];
567 B[i] = SubBs[ClosestTriangleIdx][i];
568 }
569
571 }
572
573 template <typename T>
574 void ReorderGJKArray(T* Data, FSimplex& Idxs)
575 {
576 const T D0 = Data[Idxs[0]];
577 const T D1 = Data[Idxs[1]];
578 const T D2 = Data[Idxs[2]];
579 const T D3 = Data[Idxs[3]];
580 Data[0] = D0;
581 Data[1] = D1;
582 Data[2] = D2;
583 Data[3] = D3;
584 }
585
586 template <typename T>
588 {
589 TVec3<T> ClosestPoint;
590 switch (Idxs.NumVerts)
591 {
592 case 1:
593 OutBarycentric[Idxs[0]] = 1;
594 ClosestPoint = Simplex[Idxs[0]]; break;
595 case 2:
596 {
597 ClosestPoint = LineSimplexFindOrigin(Simplex, Idxs.Idxs, Idxs.NumVerts, OutBarycentric);
598 break;
599 }
600 case 3:
601 {
602 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, OutBarycentric);
603 break;
604 }
605 case 4:
606 {
608 break;
609 }
610 default:
611 ensure(false);
612 ClosestPoint = TVec3<T>(0);
613 }
614
617 if (A)
618 {
619 ReorderGJKArray(A, Idxs);
620 }
621
622 if (B)
623 {
624 ReorderGJKArray(B, Idxs);
625 }
626
627 Idxs[0] = 0;
628 Idxs[1] = 1;
629 Idxs[2] = 2;
630 Idxs[3] = 3;
631
632 return ClosestPoint;
633 }
634
635 template <typename T>
637 {
638 switch (NumVerts)
639 {
640 case 1:
641 OutBarycentric[0] = T(1);
642 return Simplex[0];
643 case 2:
644 {
646 }
647 case 3:
648 {
650 }
651 case 4:
652 {
654 }
655 default:
656 ensure(false);
657 return TVec3<T>(0);
658 }
659 }
660
661}
#define check(expr)
Definition AssertionMacros.h:314
#define ensure( InExpression)
Definition AssertionMacros.h:464
@ INDEX_NONE
Definition CoreMiscDefines.h:150
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
Definition Vector.h:1000
Definition Vector.h:41
Definition SkeletalMeshComponent.h:307
TVec3< T > TriangleSimplexFindOrigin(const TVec3< T > *Simplex, FSimplex &Idxs, T *OutBarycentric)
Definition Simplex.h:113
TVector< T, d > LineSimplexFindOrigin(const TVector< T, d > *Simplex, int32 *Idxs, int32 &NumVerts, T *OutBarycentric)
Definition Simplex.h:9
TVec3< T > TetrahedronSimplexFindOrigin2(TVec3< T > *Simplex, int32 &NumVerts, T *OutBarycentric, TVec3< T > *A, TVec3< T > *B)
Definition Simplex.h:501
TVector< FReal, 3 > FVec3
Definition Core.h:17
TVec3< T > TriangleSimplexFindOrigin2(TVec3< T > *Simplex, int32 &NumVerts, T *OutBarycentric, TVec3< T > *As, TVec3< T > *Bs)
Definition Simplex.h:264
void ReorderGJKArray(T *Data, FSimplex &Idxs)
Definition Simplex.h:574
TVec3< T > TetrahedronSimplexFindOrigin(const TVec3< T > *Simplex, FSimplex &Idxs, T *OutBarycentric)
Definition Simplex.h:428
TVec3< T > LineSimplexFindOrigin2(TVec3< T > *Simplex, int32 &NumVerts, T *OutBarycentric, TVec3< T > *A, TVec3< T > *B)
Definition Simplex.h:45
TVec3< T > SimplexFindClosestToOrigin2(TVec3< T > *Simplex, int32 &NumVerts, T *OutBarycentric, TVec3< T > *A, TVec3< T > *B)
Definition Simplex.h:636
bool SignMatch(T A, T B)
Definition Simplex.h:107
TVec3< T > SimplexFindClosestToOrigin(TVec3< T > *Simplex, FSimplex &Idxs, T *OutBarycentric, TVec3< T > *A=nullptr, TVec3< T > *B=nullptr)
Definition Simplex.h:587
Definition Simplex.h:84
int32 & operator[](int32 Idx)
Definition Simplex.h:88
int32 NumVerts
Definition Simplex.h:85
int32 operator[](int32 Idx) const
Definition Simplex.h:87
FSimplex(std::initializer_list< int32 > InIdxs={})
Definition Simplex.h:90
int32 Idxs[4]
Definition Simplex.h:86
static constexpr UE_FORCEINLINE_HINT T Clamp(const T X, const T MinValue, const T MaxValue)
Definition UnrealMathUtility.h:592