UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SAT.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#include "Chaos/Plane.h"
6#include "Chaos/Utilities.h"
7
8namespace Chaos
9{
10 // The feature type returned by SATPenetration
11 enum class ESATFeatureType
12 {
13 None,
14 Plane,
15 Edge,
16 Vertex,
17 };
18
19 // The results from SATPenetration
21 {
27
28 bool IsValid() const
29 {
30 return FeatureTypes[0] != ESATFeatureType::None; // No need to check feature 1
31 }
32
33 bool IsEdgeContact() const
34 {
35 return (FeatureTypes[0] == ESATFeatureType::Edge); // No need to check feature 1
36 }
37
38 bool IsPlaneContact() const
39 {
40 return (FeatureTypes[0] == ESATFeatureType::Plane) || (FeatureTypes[0] == ESATFeatureType::Vertex); // No need to check feature 1
41 }
42
44 {
47 return *this;
48 }
49
53 };
54
55 // Parameters for SATPenetartion
57 {
59 : PlaneBias(0)
60 , ObjectBias(0)
61 {}
62
63 // Bias to select Plane-Vertex contacts over Edge-Edge contacts with similar separation
65
66 // Bias to select first (+ve) or second (-ve) object as the Plane owner when both report similar Plane-Vertex distances
68 };
69
70 // Check whether the two edges of two convex shapes contribute to the Minkowski sum.
71 // A and B are the face normals for the faces of the edge convex 1
72 // C and D are the negated face normals for the faces of the edge convex 2
73 inline bool IsMinkowskiSumFace(const FVec3& A, const FVec3& B, const FVec3& C, const FVec3& D)
74 {
75 const FVec3 BA = FVec3::CrossProduct(B, A);
76 const FVec3 DC = FVec3::CrossProduct(D, C);
77 const FReal CBA = FVec3::DotProduct(C, BA);
78 const FReal DBA = FVec3::DotProduct(D, BA);
79 const FReal ADC = FVec3::DotProduct(A, DC);
80 const FReal BDC = FVec3::DotProduct(B, DC);
81
82 const FReal Tolerance = 1.e-2f;
83 return ((CBA * DBA) < -Tolerance) && ((ADC * BDC) < -Tolerance) && ((CBA * BDC) > Tolerance);
84 }
85
86 // Find the nearest Plane-Vertex pair by looking at the Vertices of Convex1 and the Planes of Convex2
87 template <typename ConvexImplicitType1, typename ConvexImplicitType2>
93 const FReal CullDistance)
94 {
95 FSATResult Result;
96
98
99 const int32 NumPlanes2 = Convex2.NumPlanes();
101 {
103 const FVec3 PlaneN2In1 = Convex2ToConvex1Transform.TransformVectorNoScale(Plane2.Normal());
104 const FVec3 PlaneX2In1 = Convex2ToConvex1Transform.TransformPositionNoScale(Plane2.X());
105
108
109 // @todo(chaos): Use support method that returns vertex index. Use hill climbing
110 const int32 NumVertices1 = Convex1.NumVertices();
111 for (int32 VertexIndex1 = 0; VertexIndex1 < NumVertices1; ++VertexIndex1)
112 {
113 const FVec3 VertexX1 = Convex1.GetVertex(VertexIndex1);
114 const FReal VertexDistance = FVec3::DotProduct(VertexX1 - PlaneX2In1, PlaneN2In1);
116 {
118 NearestVertexIndex1 = VertexIndex1;
119 }
120 }
121
122 // We can stop if all verts are farther than CullDistance from any plane
123 if (NearestVertexDistance > CullDistance)
124 {
125 return FSATResult();
126 }
127
128 // Is this the new best separating axis?
129 if (NearestVertexDistance > Result.SignedDistance)
130 {
131 Result.FeatureTypes[0] = ESATFeatureType::Vertex;
132 Result.FeatureTypes[1] = ESATFeatureType::Plane;
133 Result.FeatureIndices[0] = NearestVertexIndex1;
134 Result.FeatureIndices[1] = PlaneIndex2;
135 Result.SignedDistance = NearestVertexDistance;
136 }
137 }
138
139 return Result;
140 }
141
142 // Find the nearest Edge-Edge pair that contributes to the minkowski surface
143 template <typename ConvexImplicitType1, typename ConvexImplicitType2>
149 const FReal CullDistance)
150 {
151 FSATResult Result;
152
154
155 // Center of the convex shape, used to enforce correct normal direction
156 const FVec3 Centroid2 = Convex2.GetCenterOfMass();
157 const FVec3 Centroid2In1 = Convex2ToConvex1Transform.TransformPositionNoScale(Centroid2);
158
159 const int32 NumEdges1 = Convex1.NumEdges();
160 const int32 NumEdges2 = Convex2.NumEdges();
161
162 // Loop over the edges in Convex2
164 {
165 // Edge Vertices in other object space
166 const int32 EdgeVertexIndex2A = Convex2.GetEdgeVertex(EdgeIndex2, 0);
167 const int32 EdgeVertexIndex2B = Convex2.GetEdgeVertex(EdgeIndex2, 1);
168 const FVec3 EdgeVertex2AIn1 = Convex2ToConvex1Transform.TransformPositionNoScale(FVector(Convex2.GetVertex(EdgeVertexIndex2A)));
169 const FVec3 EdgeVertex2BIn1 = Convex2ToConvex1Transform.TransformPositionNoScale(FVector(Convex2.GetVertex(EdgeVertexIndex2B)));
170
171 // Planes that use the edge
172 const int32 EdgePlaneIndex2A = Convex2.GetEdgePlane(EdgeIndex2, 0);
173 const int32 EdgePlaneIndex2B = Convex2.GetEdgePlane(EdgeIndex2, 1);
174 const FVec3 EdgePlaneNormal2AIn1 = Convex2ToConvex1Transform.TransformVectorNoScale(Convex2.GetPlane(EdgePlaneIndex2A).Normal());
175 const FVec3 EdgePlaneNormal2BIn1 = Convex2ToConvex1Transform.TransformVectorNoScale(Convex2.GetPlane(EdgePlaneIndex2B).Normal());
176
177 // Loop over the edges in Convex1
179 {
180 // Edge Vertices
181 const int32 EdgeVertexIndex1A = Convex1.GetEdgeVertex(EdgeIndex1, 0);
182 const int32 EdgeVertexIndex1B = Convex1.GetEdgeVertex(EdgeIndex1, 1);
183 const FVec3 EdgeVertex1A = Convex1.GetVertex(EdgeVertexIndex1A);
184 const FVec3 EdgeVertex1B = Convex1.GetVertex(EdgeVertexIndex1B);
185
186 // Planes that use the edge
187 const int32 EdgePlaneIndex1A = Convex1.GetEdgePlane(EdgeIndex1, 0);
188 const int32 EdgePlaneIndex1B = Convex1.GetEdgePlane(EdgeIndex1, 1);
189 const FVec3 EdgePlaneNormal1A = Convex1.GetPlane(EdgePlaneIndex1A).Normal();
190 const FVec3 EdgePlaneNormal1B = Convex1.GetPlane(EdgePlaneIndex1B).Normal();
191
192 // Does this edge pair contribute to the Minkowski sum?
194 {
195 continue;
196 }
197
198 // Separating normal (always points away from convex 2)
199 // @todo(chaos): we can perform the distance culling with a non-normalized axis and defer the sqrt
202 {
203 continue;
204 }
205 if (FVec3::DotProduct(EdgeNormal, EdgeVertex2AIn1 - Centroid2In1) < 0)
206 {
208 }
209
210 // Signed separating distance
211 const FReal EdgeDistance = FVec3::DotProduct(EdgeVertex1A - EdgeVertex2AIn1, EdgeNormal);
212
213 // We can stop if any edge pair on the Minkowski surface is a separating axis
214 if (EdgeDistance > CullDistance)
215 {
216 return FSATResult();
217 }
218
219 // Should we use this edge pair?
220 if (EdgeDistance > Result.SignedDistance)
221 {
222 Result.FeatureTypes[0] = ESATFeatureType::Edge;
223 Result.FeatureTypes[1] = ESATFeatureType::Edge;
224 Result.FeatureIndices[0] = EdgeIndex1;
225 Result.FeatureIndices[1] = EdgeIndex2;
226 Result.SignedDistance = EdgeDistance;
227 }
228 }
229 }
230
231 return Result;
232 }
233
234 // Separating Axis Test
235 // Find the pair of features with the minimum separation distance or the minimum depenetration distance
236 template <typename ConvexImplicitType1, typename ConvexImplicitType2>
242 const FReal CullDistance,
243 const FSATSettings& Settings)
244 {
245 // Find the closest vertex of Convex1 to a plane of Convex2
247 if (PlaneResult1.SignedDistance > CullDistance)
248 {
249 return FSATResult();
250 }
251
252 // Find the closest vertex of Convex2 to a plane of Convex1
254 if (PlaneResult2.SignedDistance > CullDistance)
255 {
256 return FSATResult();
257 }
258
259 // Find the closest edge pair
261 if (EdgeResult.SignedDistance > CullDistance)
262 {
263 return FSATResult();
264 }
265
266 // Select the best contact. Prefer face contacts to edge contacts (for +ve bias)
267 const FReal MaxPlaneDistance = FMath::Max(PlaneResult1.SignedDistance, PlaneResult2.SignedDistance);
268 const bool bUseEdgeResult = (EdgeResult.SignedDistance > MaxPlaneDistance + Settings.PlaneBias);
269 if (bUseEdgeResult)
270 {
271 return EdgeResult;
272 }
273
274 // Prefer planes on Convex2 over Convex1 (for +ve bias, and vice-versa for -ve bias) to prevent flip-flopping
275 const bool bUsePlaneResult1 = (PlaneResult1.SignedDistance > PlaneResult2.SignedDistance - Settings.ObjectBias);
277 {
278 return PlaneResult1;
279 }
280
281 return PlaneResult2;
282 }
283
284}
@ 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
#define FVector
Definition IOSSystemIncludes.h:8
Definition CorePlane.h:12
bool NormalizeSafe(FVec3 &V, FReal EpsilonSq=UE_SMALL_NUMBER)
Definition Utilities.h:492
Definition SkeletalMeshComponent.h:307
ESATFeatureType
Definition SAT.h:12
FSATResult SATPenetration(const ConvexImplicitType1 &Convex1, const FRigidTransform3 &Convex1Transform, const ConvexImplicitType2 &Convex2, const FRigidTransform3 &Convex2Transform, const FReal CullDistance, const FSATSettings &Settings)
Definition SAT.h:237
FSATResult SATPlaneVertex(const ConvexImplicitType1 &Convex1, const FRigidTransform3 &Convex1Transform, const ConvexImplicitType2 &Convex2, const FRigidTransform3 &Convex2Transform, const FReal CullDistance)
Definition SAT.h:88
FRealDouble FReal
Definition Real.h:22
bool IsMinkowskiSumFace(const FVec3 &A, const FVec3 &B, const FVec3 &C, const FVec3 &D)
Definition SAT.h:73
FSATResult SATEdgeEdge(const ConvexImplicitType1 &Convex1, const FRigidTransform3 &Convex1Transform, const ConvexImplicitType2 &Convex2, const FRigidTransform3 &Convex2Transform, const FReal CullDistance)
Definition SAT.h:144
Definition SAT.h:21
FSATResult & SwapShapes()
Definition SAT.h:43
bool IsValid() const
Definition SAT.h:28
bool IsEdgeContact() const
Definition SAT.h:33
FReal SignedDistance
Definition SAT.h:52
FSATResult()
Definition SAT.h:22
int32 FeatureIndices[2]
Definition SAT.h:51
ESATFeatureType FeatureTypes[2]
Definition SAT.h:50
bool IsPlaneContact() const
Definition SAT.h:38
Definition SAT.h:57
FReal PlaneBias
Definition SAT.h:64
FSATSettings()
Definition SAT.h:58
FReal ObjectBias
Definition SAT.h:67
Definition NumericLimits.h:41