UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
MeshContactGenerator.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"
10#include "Chaos/Triangle.h"
11
12namespace Chaos::Private
13{
15 {
16 public:
18
19 // Contacts with a dot product against the face normal above this value will not be processed in FixContactNormal
21
22 // Triangle edge/vertex contacts that are more than this far from a valid normal (dot product) will be rejected rather than corrected
24
25 // When backface culling is enabled, the tolerance for the dot product of the contact normal against the face normal
27
28 // Used to determine whether a contact is on an edge or vertex
30
31 // We don't allow more (pre-filtered) contacts than this. Any extras will be lost.
33
34 // Size of the hash table used to store/lookup triangle data
36
37 // Whether to ignore inside normals
38 // @todo(chaos): the non-culled option is not well tested and probably broken
40
41 // Whether to auto-correct normals
43
44 // Whether to sort the contacts by depth
46
47 // Whether to sort the contacts to improve solver convergence (distance from the center of mass)
49
50 // Whether to use the optimized two-pass loop over triangles in GenerateMeshContacts which skips triangles
51 // that have contacts on all vertices in the second pass. This is only useful when this case occurs a lot
52 // which is does for large convexes against many triangles, but rarely for capsules and spheres.
54 };
55
56 // A triangle plus some extended data and state
57 // Should be a member of FMeshContactGenerator but that causes natvis issues.
59 {
60 static constexpr FReal InvalidNormalMarker = std::numeric_limits<FReal>::max();
61
62 public:
64 : Triangle(InTriangle)
65 , Normal(InvalidNormalMarker)
66 , TriangleIndex(InTriangleIndex)
68 , NumFaceEdgeCollisions(0)
69 , VisitIndex(INDEX_NONE)
70 , bEnabled(true)
71 {
72 }
73
74 // Does this triangle contains the specified vertex? (VertexIndex is an index into the owning mesh's vertices)
75 inline bool HasVertexID(const int32 VertexIndex) const
76 {
77 return (VertexIndices[0] == VertexIndex) || (VertexIndices[1] == VertexIndex) || (VertexIndices[2] == VertexIndex);
78 }
79
80 // Get the vertex position from the vertex ID (not the triangle-local vertex index)
81 inline bool GetVertexWithID(const int32 VertexID, FVec3& OutVertex) const
82 {
83 if (VertexID == VertexIndices[0])
84 {
85 OutVertex = Triangle.GetVertex(0);
86 return true;
87 }
88 else if (VertexID == VertexIndices[1])
89 {
90 OutVertex = Triangle.GetVertex(1);
91 return true;
92 }
93 else if (VertexID == VertexIndices[2])
94 {
95 OutVertex = Triangle.GetVertex(2);
96 return true;
97 }
98 return false;
99 }
100
101 inline bool GetOtherVertexIDs(const int32 VertexID, int32& OutVertexID0, int32& OutVertexID1) const
102 {
103 if (VertexID == VertexIndices[0])
104 {
105 OutVertexID0 = VertexIndices[1];
106 OutVertexID1 = VertexIndices[2];
107 return true;
108 }
109 else if (VertexID == VertexIndices[1])
110 {
111 OutVertexID0 = VertexIndices[2];
112 OutVertexID1 = VertexIndices[0];
113 return true;
114 }
115 else if (VertexID == VertexIndices[2])
116 {
117 OutVertexID0 = VertexIndices[0];
118 OutVertexID1 = VertexIndices[1];
119 return true;
120 }
121 return false;
122 }
123
124 // Get the positions of the other two vertices in the triangle. (VertexIndex is an index into the owning mesh's vertices)
125 inline bool GetOtherVerticesFromID(const int32 VertexID, FVec3& OutVertex0, FVec3& OutVertex1) const
126 {
127 if (VertexID == VertexIndices[0])
128 {
129 OutVertex0 = Triangle.GetVertex(1);
130 OutVertex1 = Triangle.GetVertex(2);
131 return true;
132 }
133 else if (VertexID == VertexIndices[1])
134 {
135 OutVertex0 = Triangle.GetVertex(2);
136 OutVertex1 = Triangle.GetVertex(0);
137 return true;
138 }
139 else if (VertexID == VertexIndices[2])
140 {
141 OutVertex0 = Triangle.GetVertex(0);
142 OutVertex1 = Triangle.GetVertex(1);
143 return true;
144 }
145 return false;
146 }
147
149 {
151 {
152 if (FVec3::IsNearlyEqual(GetVertex(LocalVertexIndex), InPos, InTolerance))
153 {
154 return LocalVertexIndex;
155 }
156 }
157 return INDEX_NONE;
158 };
159
161 {
164 {
165 return VertexIndices[LocalVertexIndex];
166 }
167 return INDEX_NONE;
168 };
169
170 const FTriangle& GetTriangle() const
171 {
172 return Triangle;
173 }
174
175 // Get the vertex for the triangle-local vertex index [0,2]
177 {
178 return Triangle.GetVertex(LocalVertexIndex);
179 }
180
182 {
183 return TriangleIndex;
184 }
185
186 int32 GetVertexIndex(const int32 LocalIndex) const
187 {
188 return VertexIndices[LocalIndex];
189 }
190
191 const FVec3& GetNormal() const
192 {
193 if (Normal.X == InvalidNormalMarker)
194 {
195 Normal = Triangle.GetNormal();
196 }
197 return Normal;
198 }
199
201 {
202 return Triangle.GetCentroid();
203 }
204
206 {
207 VisitIndex = InVisitIndex;
208 }
209
211 {
212 return VisitIndex;
213 }
214
215 void SetEnabled(const bool bInEnabled)
216 {
217 bEnabled = bInEnabled;
218 }
219
220 bool GetIsEnabled() const
221 {
222 return bEnabled;
223 }
224
226 {
227 ++NumFaceEdgeCollisions;
228 }
229
231 {
232 return NumFaceEdgeCollisions;
233 }
234
235 private:
236 FTriangle Triangle;
237 mutable FVec3 Normal;
238 int32 TriangleIndex;
239 int32 VertexIndices[3];
240 int8 NumFaceEdgeCollisions;
241 int8 VisitIndex;
242 bool bEnabled;
243 };
244
249 {
250 public:
252
253 // Enable or disable the normal fixup
255 {
256 Settings.bFixNormals = bInFixNormals;
257 }
258
259 // Clear and initialize buffers
265
266 // Add a triangle that we might be overlapping
267 void AddTriangle(const FTriangle& MeshTriangle, const int32 MeshTriangleIndex, const int32 VertexIndex0, const int32 VertexIndex1, const int32 VertexIndex2)
268 {
269 Triangles.Emplace(MeshTriangle, MeshTriangleIndex, VertexIndex0, VertexIndex1, VertexIndex2);
270 }
271
272 // Process all the added triangles to generate connectivity metadata etc
274 {
276 {
277 const FTriangleExt& Triangle = Triangles[LocalTriangleIndex];
278 AddTriangleEdge(LocalTriangleIndex, Triangle.GetVertexIndex(0), Triangle.GetVertexIndex(1));
279 AddTriangleEdge(LocalTriangleIndex, Triangle.GetVertexIndex(1), Triangle.GetVertexIndex(2));
280 AddTriangleEdge(LocalTriangleIndex, Triangle.GetVertexIndex(2), Triangle.GetVertexIndex(0));
281 }
282 }
283
284 // Loop over (the required subset of) all triangles and call the TriangleContactGenerator to create a manifold for each.
285 // TriangleContactGeneratorType: void(const FTriangle& Triangle, FContactPointManifold& OutContactPoints)
286 template<typename TriangleContactGeneratorType>
298
299 // Process all the contact points generated by GenerateMeshContacts. This prunes duplicates, fixes normals, and
300 // transforms the contact data back into shape-local space.
302
303 // The results of contact generation (must call ProcessGeneratedContacts prior to GetContactPoints)
305 {
306 return MakeArrayView(Contacts);
307 }
308
309 private:
310 using FTriangleExt = FMeshContactGeneratorTriangle;
311
312 // A contact index combined with a flag to indicate if the normal is roughly along the triangle face
313 struct FVertexContactIndex
314 {
315 FVertexContactIndex()
316 {
317 }
318
319 FVertexContactIndex(const FContactVertexID InID, const int32 InContactIndex, const bool bInIsFaceContact)
320 : ID(InID)
321 , ContactIndex(InContactIndex)
322 , bIsFaceContact(bInIsFaceContact)
323 {
324 }
325
327 int32 ContactIndex;
328 bool bIsFaceContact;
329 };
330
331 // The triangle indices that share an edge (assumes only 2)
332 struct FEdgeTriangleIndices
333 {
334 FEdgeTriangleIndices(const FContactEdgeID& InEdgeID, const int32 InIndex0, const int32 InIndex1)
335 : ID(InEdgeID)
336 , LocalTriangleIndices{ InIndex0, InIndex1 }
337 {
338 }
339
340 FContactEdgeID ID;
341 int32 LocalTriangleIndices[2];
342 };
343
344 template<typename TriangleContactGeneratorType>
345 void GenerateMeshContactsOnePass(const TriangleContactGeneratorType& TriangleContactGenerator)
346 {
348 {
350
351 SetTriangleVisited(LocalTriangleIndex, 0);
352 }
353 }
354
355 template<typename TriangleContactGeneratorType>
356 void GenerateMeshContactsTwoPass(const TriangleContactGeneratorType& TriangleContactGenerator)
357 {
358 // First loop: Visit triangles that do not have any collisions on any of their vertices or edges.
359 // This will skip all triangles whose neighbours have already been processed and generated a contact
360 // on a shared edge/vertex.
362 {
363 if (GetNumTriangleFaceCollisions(LocalTriangleIndex) == 0)
364 {
366
367 SetTriangleVisited(LocalTriangleIndex, 0);
368 }
369 }
370
371 // Second loop: Visit remaining triangles that have less than 3 contacts on them. This will skip all triangles
372 // that have a full manifold as a result of collisions on shared edges/vertices from adjacent triangles.
374 {
375 if (!IsTriangleVisited(LocalTriangleIndex) && (GetNumTriangleFaceCollisions(LocalTriangleIndex) < 3))
376 {
378
379 SetTriangleVisited(LocalTriangleIndex, 1);
380 }
381 }
382 }
383
384 void Reset(const int32 InMaxTriangles, const int32 InMaxContacts);
385
386 void AddTriangleEdge(const int32 LocalTriangleIndex, const int32 VertexIndex0, const int32 VertexIndex1)
387 {
388 const FContactEdgeID EdgeID = FContactEdgeID(VertexIndex0, VertexIndex1);
389 FEdgeTriangleIndices* EdgeTriangleIndices = EdgeTriangleIndicesMap.Find(EdgeID);
390 if (EdgeTriangleIndices == nullptr)
391 {
392 EdgeTriangleIndicesMap.Emplace(EdgeID, EdgeID, LocalTriangleIndex, INDEX_NONE);
393 }
394 else
395 {
396 EdgeTriangleIndices->LocalTriangleIndices[1] = LocalTriangleIndex;
397 }
398 }
399
400 bool IsSharedEdge(const FContactEdgeID& EdgeID) const
401 {
402 const FEdgeTriangleIndices* EdgeTriangleIndices = EdgeTriangleIndicesMap.Find(EdgeID);
403 if (EdgeTriangleIndices != nullptr)
404 {
405 return (EdgeTriangleIndices->LocalTriangleIndices[0] != INDEX_NONE) && (EdgeTriangleIndices->LocalTriangleIndices[1] != INDEX_NONE);
406 }
407 return false;
408 }
409
410 int32 GetOtherTriangleIndexForEdge(const int32 LocalTriangleIndex, const FContactEdgeID& EdgeID)
411 {
412 FEdgeTriangleIndices* EdgeTriangleIndices = EdgeTriangleIndicesMap.Find(EdgeID);
413 if (EdgeTriangleIndices != nullptr)
414 {
415 return (EdgeTriangleIndices->LocalTriangleIndices[0] == LocalTriangleIndex) ? EdgeTriangleIndices->LocalTriangleIndices[1] : EdgeTriangleIndices->LocalTriangleIndices[0];
416 }
417 return INDEX_NONE;
418 }
419
420 bool HasFaceVertexCollision(const FContactVertexID VertexID) const
421 {
422 if (const FVertexContactIndex* ContactIndex = VertexContactIndicesMap.Find(VertexID))
423 {
424 return ContactIndex->bIsFaceContact;
425 }
426 return false;
427 }
428
429 int32 GetNumTriangleFaceCollisions(const int32 LocalTriangleIndex) const
430 {
431 const int32 MeshVertexIndex0 = Triangles[LocalTriangleIndex].GetVertexIndex(0);
432 const int32 MeshVertexIndex1 = Triangles[LocalTriangleIndex].GetVertexIndex(1);
433 const int32 MeshVertexIndex2 = Triangles[LocalTriangleIndex].GetVertexIndex(2);
434 int32 NumCollisions = Triangles[LocalTriangleIndex].GetNumFaceEdgeCollisions();
435 NumCollisions += HasFaceVertexCollision(MeshVertexIndex0) ? 1 : 0;
436 NumCollisions += HasFaceVertexCollision(MeshVertexIndex1) ? 1 : 0;
437 NumCollisions += HasFaceVertexCollision(MeshVertexIndex2) ? 1 : 0;
438 return NumCollisions;
439 }
440
441 bool IsTriangleVisited(const int32 LocalTriangleIndex) const
442 {
443 return Triangles[LocalTriangleIndex].GetVisitIndex() != INDEX_NONE;
444 }
445
446 void SetTriangleVisited(const int32 LocalTriangleIndex, const int8 VisitIndex)
447 {
448 Triangles[LocalTriangleIndex].SetVisitIndex(VisitIndex);
449 }
450
451 public:
453 {
454 return Triangles.Num();
455 }
456
458 {
459 return Triangles[LocalTriangleIndex].GetTriangle();
460 }
461
463 {
464 return Triangles[LocalTriangleIndex].GetNormal();
465 }
466
468
469 void AddTriangleContacts(const int32 LocalTriangleIndex, const TArrayView<FContactPoint>& TriangleContactPoints);
470
471 private:
472 void PruneAndCorrectContacts();
473 void FixContactNormal(const int32 ContactIndex);
474 void RemoveDisabledContacts();
475 void SortContactByPhi();
476 void SortContactsForSolverConvergence();
477 void FinalizeContacts(const FRigidTransform3& MeshToConvexTransform);
478
479 void DebugDrawContacts(const FRigidTransform3& ConvexTransform, const FColor& Color, const FReal LineScale);
480 void DebugDrawTriangles(const FRigidTransform3& ConvexTransform, const FColor& VisitedColor, const FColor& IgnoredColor);
481 void DebugDrawTriangle(const FRigidTransform3& ConvexTransform, const FTriangleExt& TriangleData, const FColor& Color);
482
483 private:
485
486 // All the triangles we might collide with
487 TArray<FTriangleExt> Triangles;
488
489 // The contact data. This is split into the output data (FContactPoint) and the extra
490 // per-contact metadata required during processing (FTriangleContactPointData).
491 TArray<FContactPoint> Contacts;
493
494 // A map of EdgeID to the two triangles (indices) that use the Edge
495 struct FEdgeTriangleIndicesMapTraits
496 {
497 static uint32 GetIDHash(const FContactEdgeID& EdgeID) { return uint32(MurmurFinalize64(EdgeID.EdgeID)); }
498 static FContactEdgeID GetElementID(const FEdgeTriangleIndices& TriangleIndices) { return TriangleIndices.ID; }
499 };
501
502 // A map of VertexID to contact index on that vertex - we only ever keep one contact per vertex
503 struct VertexContactIndicesMapTraits
504 {
505 static uint32 GetIDHash(const FContactVertexID& VertexID) { return MurmurFinalize32(VertexID); }
506 static FContactVertexID GetElementID(const FVertexContactIndex& VertexIndex) { return VertexIndex.ID; }
507 };
508 THashMappedArray<FContactVertexID, FVertexContactIndex, VertexContactIndicesMapTraits> VertexContactIndicesMap;
509 };
510
511}
constexpr auto MakeArrayView(OtherRangeType &&Other)
Definition ArrayView.h:873
@ INDEX_NONE
Definition CoreMiscDefines.h:150
uint64 MurmurFinalize64(uint64 Hash)
Definition HashTable.h:33
uint32 MurmurFinalize32(uint32 Hash)
Definition HashTable.h:23
FPlatformTypes::int8 int8
An 8-bit signed integer.
Definition Platform.h:1121
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
return true
Definition ExternalRpcRegistry.cpp:601
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition MeshContactGenerator.h:15
uint32 bSortForSolverConvergence
Definition MeshContactGenerator.h:48
uint32 bSortByPhi
Definition MeshContactGenerator.h:45
FMeshContactGeneratorSettings()
Definition MeshContactGenerator.cpp:23
uint32 bFixNormals
Definition MeshContactGenerator.h:42
FReal BackFaceCullTolerance
Definition MeshContactGenerator.h:26
uint32 bCullBackFaces
Definition MeshContactGenerator.h:39
FReal FaceNormalDotThreshold
Definition MeshContactGenerator.h:20
FReal EdgeNormalDotRejectTolerance
Definition MeshContactGenerator.h:23
int32 HashSize
Definition MeshContactGenerator.h:35
int32 MaxContactsBufferSize
Definition MeshContactGenerator.h:32
FReal BarycentricTolerance
Definition MeshContactGenerator.h:29
uint32 bUseTwoPassLoop
Definition MeshContactGenerator.h:53
Definition MeshContactGenerator.h:59
const FVec3 & GetVertex(const int32 LocalVertexIndex) const
Definition MeshContactGenerator.h:176
int32 GetVertexIndex(const int32 LocalIndex) const
Definition MeshContactGenerator.h:186
void SetEnabled(const bool bInEnabled)
Definition MeshContactGenerator.h:215
bool GetOtherVertexIDs(const int32 VertexID, int32 &OutVertexID0, int32 &OutVertexID1) const
Definition MeshContactGenerator.h:101
bool GetOtherVerticesFromID(const int32 VertexID, FVec3 &OutVertex0, FVec3 &OutVertex1) const
Definition MeshContactGenerator.h:125
const FTriangle & GetTriangle() const
Definition MeshContactGenerator.h:170
int32 GetNumFaceEdgeCollisions() const
Definition MeshContactGenerator.h:230
bool GetVertexWithID(const int32 VertexID, FVec3 &OutVertex) const
Definition MeshContactGenerator.h:81
void SetVisitIndex(const int8 InVisitIndex)
Definition MeshContactGenerator.h:205
int32 GetLocalVertexIndexAt(const FVec3 &InPos, const FReal InTolerance) const
Definition MeshContactGenerator.h:148
int32 GetTriangleIndex() const
Definition MeshContactGenerator.h:181
const FVec3 & GetNormal() const
Definition MeshContactGenerator.h:191
FMeshContactGeneratorTriangle(const FTriangle &InTriangle, const int32 InTriangleIndex, const int32 InVertexIndex0, const int32 InVertexIndex1, const int32 InVertexIndex2)
Definition MeshContactGenerator.h:63
FVec3 GetCentroid() const
Definition MeshContactGenerator.h:200
bool HasVertexID(const int32 VertexIndex) const
Definition MeshContactGenerator.h:75
void AddFaceEdgeCollision()
Definition MeshContactGenerator.h:225
bool GetIsEnabled() const
Definition MeshContactGenerator.h:220
int32 GetVertexIDAt(const FVec3 &InPos, const FReal InTolerance) const
Definition MeshContactGenerator.h:160
int8 GetVisitIndex() const
Definition MeshContactGenerator.h:210
Definition MeshContactGenerator.h:249
void EndCollect()
Definition MeshContactGenerator.h:273
bool FixFeature(const int32 LocalTriangleIndex, Private::EConvexFeatureType &InOutFeatureType, int32 &InOutFeatureIndex, FVec3 &InOutPlaneNormal)
Definition MeshContactGenerator.cpp:378
void AddTriangleContacts(const int32 LocalTriangleIndex, const TArrayView< FContactPoint > &TriangleContactPoints)
Definition MeshContactGenerator.cpp:60
int32 GetNumTriangles() const
Definition MeshContactGenerator.h:452
void ProcessGeneratedContacts(const FRigidTransform3 &ConvexTransform, const FRigidTransform3 &MeshToConvexTransform)
Definition MeshContactGenerator.cpp:154
void BeginCollect(const int32 InNumTriangles)
Definition MeshContactGenerator.h:260
TArrayView< const FContactPoint > GetContactPoints() const
Definition MeshContactGenerator.h:304
const FVec3 & GetTriangleNormal(const int32 LocalTriangleIndex) const
Definition MeshContactGenerator.h:462
void SetFixNormalsEnabled(const bool bInFixNormals)
Definition MeshContactGenerator.h:254
void AddTriangle(const FTriangle &MeshTriangle, const int32 MeshTriangleIndex, const int32 VertexIndex0, const int32 VertexIndex1, const int32 VertexIndex2)
Definition MeshContactGenerator.h:267
void GenerateMeshContacts(const TriangleContactGeneratorType &TriangleContactGenerator)
Definition MeshContactGenerator.h:287
const FTriangle & GetTriangle(const int32 LocalTriangleIndex) const
Definition MeshContactGenerator.h:457
const FElementType * Find(const FIDType ID) const
Definition HashMappedArray.h:164
FORCEINLINE const TVec3< T > & GetVertex(const int32 InIndex) const
Definition Triangle.h:114
FORCEINLINE TVec3< T > GetCentroid() const
Definition Triangle.h:143
FORCEINLINE TVec3< T > GetNormal() const
Definition Triangle.h:120
Definition ArrayView.h:139
Definition Array.h:670
Definition BodyInstance.h:90
EConvexFeatureType
Definition ConvexFeature.h:11
FRealDouble FReal
Definition Real.h:22
int32 FContactVertexID
Definition ContactTriangles.h:159
const FName VertexIndex("VertexIndex")
Definition MeshAttributes.h:28
Definition ContactTriangles.h:163
uint64 EdgeID
Definition ContactTriangles.h:213
Definition Color.h:486