UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SATConvexTriangle.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"
7#include "Chaos/Convex.h"
8#include "Chaos/Triangle.h"
9#include "Chaos/Utilities.h"
10
11namespace Chaos::Private
12{
13 // Find the contact point between a convex and a triangle using SAT.
14 // NOTE: Does not fill in the features of OutContactPoint (see GetConvexFeature())
15 template<typename ConvexType>
17 {
18 // Constants. NOTE: We square and multiple InvalidPhi so not using lowest()
19 const FReal NormalTolerance = FReal(1.e-8);
20 const FReal NormalToleranceSq = NormalTolerance * NormalTolerance;
21 const FReal InvalidPhi = FReal(-1.e10); // 1000 km penetration
22
23 // Triangle (same space as convex)
24 const FVec3& TriN = TriangleNormal;
25 const FVec3 TriC = Triangle.GetCentroid();
26
27 //
28 // SAT: Triangle face vs Convex verts
29 //
30
31 // We store the convex vertex distances to the triangle face for use in the edge-edge culling
34 ConvexVertexDs.SetNum(Convex.NumVertices());
36
37 // Project the convex onto the triangle normal, with distances relative to the triangle plane
41
42 // Distance culling
43 if (Utilities::SignedSquare(TriPlaneDMin) > CullDistanceSq)
44 {
45 // Outside triangle face and separated by more than CullDistance
46 return false;
47 }
48 if (TriPlaneDMax < FReal(0))
49 {
50 // Inside triangle face (single-sided collision)
51 return false;
52 }
53
54 //
55 // SAT: Convex faces vs triangle verts
56 //
57
58 // For each convex plane, project the Triangle onto the convex plane normal
59 // and reject if the separation is more than cull distance.
65 for (int32 PlaneIndex = 0; PlaneIndex < Convex.NumPlanes(); ++PlaneIndex)
66 {
68 Convex.GetPlaneNX(PlaneIndex, ConN, ConX);
69
71 int32 IndexMin, IndexMax;
73
74 // Backface cull - we can't select a convex face that would us a contact normal pointing below the triangle
75 //const FReal ConvexDotTriangleNormal = FVec3::DotProduct(ConN, TriN);
76 //if (ConvexDotTriangleNormal > 0)
77 //{
78 // continue;
79 //}
80
81 // Distance culling
82 // @todo(chaos): Cull against the projected convex hull, not just the outward face
83 // (we can store the most-distance vertex for each face with the convex to avoid actually having to project)
84 if (Utilities::SignedSquare(DMin) > CullDistanceSq)
85 {
86 // Separated by more than CullDistance
87 return false;
88 }
89
91 {
95 ConvexPlaneIndexMin = PlaneIndex;
97 }
98 }
99
100 //
101 // SAT: Convex edges vs triangle edges
102 //
103
104 // Calculate the distance of triangle each edge to the convex separating plane
105 FReal TriVertexConvexDMin0 = FVec3::DotProduct(Triangle.GetVertex(0) - ConvexPlaneX, ConvexPlaneN);
106 FReal TriVertexConvexDMin1 = FVec3::DotProduct(Triangle.GetVertex(1) - ConvexPlaneX, ConvexPlaneN);
107 FReal TriVertexConvexDMin2 = FVec3::DotProduct(Triangle.GetVertex(2) - ConvexPlaneX, ConvexPlaneN);
109 {
113 };
114
115 FReal ConvexWinding = Convex.GetWindingOrder();
116 FVec3 EdgeEdgeN = FVec3(0);
121 {
122 // Handle reverse winding for negative scaled convexes. Loop over edges in reverse order, and reverse edge vertex order
124 const int32 ConvexEdgeVIndex0 = (ConvexWinding >= 0) ? 0 : 1;
125 const int32 ConvexEdgeVIndex1 = (ConvexWinding >= 0) ? 1 : 0;
126
127 // Skip convex edges beyond CullDistance of the triangle face
132 if ((Utilities::SignedSquare(FaceConvexD0) > CullDistanceSq) && (Utilities::SignedSquare(FaceConvexD1) > CullDistanceSq))
133 {
134 continue;
135 }
136
137 // Convex edge vertices
140
141 // Convex planes that form the edge
142 const int32 ConvexEdgePlaneIndexA = Convex.GetEdgePlane(ConvexEdgeIndex, 0);
143 const int32 ConvexEdgePlaneIndexB = Convex.GetEdgePlane(ConvexEdgeIndex, 1);
144 const FVec3 ConvexEdgePlaneNormalA = Convex.GetPlane(ConvexEdgePlaneIndexA).Normal();
145 const FVec3 ConvexEdgePlaneNormalB = Convex.GetPlane(ConvexEdgePlaneIndexB).Normal();
146
147 for (int32 TriEdgeIndex = 0; TriEdgeIndex < 3; ++TriEdgeIndex)
148 {
149 // Skip triangle edges beyond cull distance of the convex separating face
150 if (Utilities::SignedSquare(TriEdgeConvexDMin[TriEdgeIndex]) > CullDistanceSq)
151 {
152 continue;
153 }
154
155 // Triangle edge vertices
156 const FVec3& TriEdgeV0 = Triangle.GetVertex(TriEdgeIndex);
157 const FVec3& TriEdgeV1 = (TriEdgeIndex == 2) ? Triangle.GetVertex(0) : Triangle.GetVertex(TriEdgeIndex + 1);
158
159 // Skip edge pairs that do not contribute to the Minkowski Sum surface
160 // NOTE: This relies on the ordering of the edge planes from above.
161 // I.e., we require Sign(ConvexEdgePlaneNormalA x ConvexEdgePlaneNormalB) == Sign(ConvexEdgeV1 - ConvexEdgeV0)
162 // Also note that we must pass the negated triangle normal in
164 {
165 continue;
166 }
167
168 // Separating axis
169 // NOTE: Not normalized at this stage. We perform the projection against the
170 // non-normalized axis and defer the square root until we know we need it
171 FVec3 Axis = FVec3::CrossProduct(ConvexEdgeV1 - ConvexEdgeV0, TriEdgeV1 - TriEdgeV0);
172 const FReal AxisLenSq = Axis.SizeSquared();
173
174 // Pick consistent axis direction: away from triangle (we want a signed distance)
175 const FReal Sign = FVec3::DotProduct(TriEdgeV0 - TriC, Axis);
176 if (Sign < FReal(0))
177 {
178 Axis = -Axis;
179 }
180
181 // Ignore backface edges
182 //const FReal AxisDotNormal = FVec3::DotProduct(TriN, Axis);
183 //if (AxisDotNormal < -NormalTolerance)
184 //{
185 // continue;
186 //}
187
188 const FReal ScaledSeparation = FVec3::DotProduct(ConvexEdgeV0 - TriEdgeV0, Axis);
189
190 // Check cull distance on projected segments
191 // Comparing square distances scaled by axis length to defer square root (keep the sign)
193 const FReal ScaledCullDistanceSq = CullDistanceSq * AxisLenSq;
195 {
196 return false;
197 }
198
199 // Comparing square distances scaled by axis length to defer square root (keep the sign)
202 {
203 // Now we need to know the actual separation and axis
204 const FReal AxisInvLen = FMath::InvSqrt(AxisLenSq);
208 TriEdgeIndexMin = TriEdgeIndex;
209 }
210 }
211 }
212
213 // Determine which of the features we want to use ad fill in the output
214 // NOTE: we rely on the fact that all valid Phi values are greater than InvalidPhi here
215
216 const FReal TriFaceBias = FReal(1.e-2); // Prevent flip-flop on near-parallel cases
218 {
219 // Triangle face contact. The triangle normal is the separating axis
222
225 OutContactPoint.ShapeContactPoints[0] = ConvexContactPoint;
226 OutContactPoint.ShapeContactPoints[1] = TriangleContactPoint;
227 OutContactPoint.ShapeContactNormal = SeparatingAxis;
229 return true;
230 }
231
233 {
234 // Convex face contact. The convex face is the separating axis, but it must point from the triangle to the convex
237
240 OutContactPoint.ShapeContactPoints[0] = ConvexContactPoint;
241 OutContactPoint.ShapeContactPoints[1] = TriangleContactPoint;
242 OutContactPoint.ShapeContactNormal = SeparatingAxis;
244 return true;
245 }
246
248 {
249 // Edge-edge contact
250 // The separating axis must point from the triangle to the convex
253
254 // Convex edge vertices
255 const int32 ConvexEdgeVIndex0 = (ConvexWinding >= 0) ? 0 : 1;
256 const int32 ConvexEdgeVIndex1 = (ConvexWinding >= 0) ? 1 : 0;
261
262 // Triangle edge vertices
263 const FVec3& TriEdgeV0 = Triangle.GetVertex(TriEdgeIndexMin);
264 const FVec3& TriEdgeV1 = (TriEdgeIndexMin == 2) ? Triangle.GetVertex(0) : Triangle.GetVertex(TriEdgeIndexMin + 1);
265
269
270 OutContactPoint.ShapeContactPoints[0] = ConvexContactPoint;
271 OutContactPoint.ShapeContactPoints[1] = TriangleContactPoint;
272 OutContactPoint.ShapeContactNormal = SeparatingAxis;
274 return true;
275 }
276
277 // No valid features (should not happen - at least TriPlaneDMin should always be valid)
278 ensureMsgf(false, TEXT("SATConvexTriangle failed to select a feature"));
279 return false;
280 }
281}
constexpr auto MakeArrayView(OtherRangeType &&Other)
Definition ArrayView.h:873
#define ensureMsgf( InExpression, InFormat,...)
Definition AssertionMacros.h:465
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#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
Definition ConvexContactPoint.h:13
Definition MemStack.h:506
Definition ArrayView.h:139
Definition Array.h:670
Definition MemStack.h:391
static FORCEINLINE FMemStack & Get()
Definition ThreadSingleton.h:101
Definition BodyInstance.h:90
void ProjectOntoAxis(const ConvexType &Convex, const FVec3 &AxisN, const FVec3 &AxisX, FReal &PMin, FReal &PMax, int32 &MinVertexIndex, int32 &MaxVertexIndex, TArrayView< FReal > *VertexDs)
Definition ConvexContactPointUtilities.h:15
bool SATConvexTriangle(const ConvexType &Convex, const FTriangle &Triangle, const FVec3 &TriangleNormal, const FReal CullDistanceSq, Private::FConvexContactPoint &OutContactPoint)
Definition SATConvexTriangle.h:16
bool IsOnMinkowskiSumConvexTriangle(const FVec3 &A, const FVec3 &B, const FVec3 &BA, const FVec3 &C, const FVec3 &DC)
Definition ConvexContactPointUtilities.h:249
TRealType SignedSquare(TRealType A)
Definition Utilities.h:22
void NearestPointsOnLineSegments(const FVec3 &P1, const FVec3 &Q1, const FVec3 &P2, const FVec3 &Q2, FReal &S, FReal &T, FVec3 &C1, FVec3 &C2, const FReal Epsilon=1.e-4f)
Definition Utilities.h:649
FRealDouble FReal
Definition Real.h:22
TVector< FReal, 3 > FVec3
Definition Core.h:17
constexpr T InvalidPhi()
Definition ContactPoint.h:29