Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Geometry/EPAConvexHullBuilder.h
9913 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#pragma once
6
7
// Define to validate the integrity of the hull structure
8
//#define JPH_EPA_CONVEX_BUILDER_VALIDATE
9
10
// Define to draw the building of the hull for debugging purposes
11
//#define JPH_EPA_CONVEX_BUILDER_DRAW
12
13
#include <Jolt/Core/NonCopyable.h>
14
#include <Jolt/Core/BinaryHeap.h>
15
16
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
17
#include <Jolt/Renderer/DebugRenderer.h>
18
#include <Jolt/Core/StringTools.h>
19
#endif
20
21
JPH_NAMESPACE_BEGIN
22
23
/// A convex hull builder specifically made for the EPA penetration depth calculation. It trades accuracy for speed and will simply abort of the hull forms defects due to numerical precision problems.
24
class EPAConvexHullBuilder : public NonCopyable
25
{
26
private:
27
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
28
/// Factor to scale convex hull when debug drawing the construction process
29
static constexpr Real cDrawScale = 10;
30
#endif
31
32
public:
33
// Due to the Euler characteristic (https://en.wikipedia.org/wiki/Euler_characteristic) we know that Vertices - Edges + Faces = 2
34
// In our case we only have triangles and they are always fully connected, so each edge is shared exactly between 2 faces: Edges = Faces * 3 / 2
35
// Substituting: Vertices = Faces / 2 + 2 which is approximately Faces / 2.
36
static constexpr int cMaxTriangles = 256; ///< Max triangles in hull
37
static constexpr int cMaxPoints = cMaxTriangles / 2; ///< Max number of points in hull
38
39
// Constants
40
static constexpr int cMaxEdgeLength = 128; ///< Max number of edges in FindEdge
41
static constexpr float cMinTriangleArea = 1.0e-10f; ///< Minimum area of a triangle before, if smaller than this it will not be added to the priority queue
42
static constexpr float cBarycentricEpsilon = 1.0e-3f; ///< Epsilon value used to determine if a point is in the interior of a triangle
43
44
// Forward declare
45
class Triangle;
46
47
/// Class that holds the information of an edge
48
class Edge
49
{
50
public:
51
/// Information about neighbouring triangle
52
Triangle * mNeighbourTriangle; ///< Triangle that neighbours this triangle
53
int mNeighbourEdge; ///< Index in mEdge that specifies edge that this Edge is connected to
54
55
int mStartIdx; ///< Vertex index in mPositions that indicates the start vertex of this edge
56
};
57
58
using Edges = StaticArray<Edge, cMaxEdgeLength>;
59
using NewTriangles = StaticArray<Triangle *, cMaxEdgeLength>;
60
61
/// Class that holds the information of one triangle
62
class Triangle : public NonCopyable
63
{
64
public:
65
/// Constructor
66
inline Triangle(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions);
67
68
/// Check if triangle is facing inPosition
69
inline bool IsFacing(Vec3Arg inPosition) const
70
{
71
JPH_ASSERT(!mRemoved);
72
return mNormal.Dot(inPosition - mCentroid) > 0.0f;
73
}
74
75
/// Check if triangle is facing the origin
76
inline bool IsFacingOrigin() const
77
{
78
JPH_ASSERT(!mRemoved);
79
return mNormal.Dot(mCentroid) < 0.0f;
80
}
81
82
/// Get the next edge of edge inIndex
83
inline const Edge & GetNextEdge(int inIndex) const
84
{
85
return mEdge[(inIndex + 1) % 3];
86
}
87
88
Edge mEdge[3]; ///< 3 edges of this triangle
89
Vec3 mNormal; ///< Normal of this triangle, length is 2 times area of triangle
90
Vec3 mCentroid; ///< Center of the triangle
91
float mClosestLenSq = FLT_MAX; ///< Closest distance^2 from origin to triangle
92
float mLambda[2]; ///< Barycentric coordinates of closest point to origin on triangle
93
bool mLambdaRelativeTo0; ///< How to calculate the closest point, true: y0 + l0 * (y1 - y0) + l1 * (y2 - y0), false: y1 + l0 * (y0 - y1) + l1 * (y2 - y1)
94
bool mClosestPointInterior = false; ///< Flag that indicates that the closest point from this triangle to the origin is an interior point
95
bool mRemoved = false; ///< Flag that indicates that triangle has been removed
96
bool mInQueue = false; ///< Flag that indicates that this triangle was placed in the sorted heap (stays true after it is popped because the triangle is freed by the main EPA algorithm loop)
97
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
98
int mIteration; ///< Iteration that this triangle was created
99
#endif
100
};
101
102
/// Factory that creates triangles in a fixed size buffer
103
class TriangleFactory : public NonCopyable
104
{
105
private:
106
/// Struct that stores both a triangle or a next pointer in case the triangle is unused
107
union alignas(Triangle) Block
108
{
109
uint8 mTriangle[sizeof(Triangle)];
110
Block * mNextFree;
111
};
112
113
/// Storage for triangle data
114
Block mTriangles[cMaxTriangles]; ///< Storage for triangles
115
Block * mNextFree = nullptr; ///< List of free triangles
116
int mHighWatermark = 0; ///< High water mark for used triangles (if mNextFree == nullptr we can take one from here)
117
118
public:
119
/// Return all triangles to the free pool
120
void Clear()
121
{
122
mNextFree = nullptr;
123
mHighWatermark = 0;
124
}
125
126
/// Allocate a new triangle with 3 indexes
127
Triangle * CreateTriangle(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions)
128
{
129
Triangle *t;
130
if (mNextFree != nullptr)
131
{
132
// Entry available from the free list
133
t = reinterpret_cast<Triangle *>(&mNextFree->mTriangle);
134
mNextFree = mNextFree->mNextFree;
135
}
136
else
137
{
138
// Allocate from never used before triangle store
139
if (mHighWatermark >= cMaxTriangles)
140
return nullptr; // Buffer full
141
t = reinterpret_cast<Triangle *>(&mTriangles[mHighWatermark].mTriangle);
142
++mHighWatermark;
143
}
144
145
// Call constructor
146
new (t) Triangle(inIdx0, inIdx1, inIdx2, inPositions);
147
148
return t;
149
}
150
151
/// Free a triangle
152
void FreeTriangle(Triangle *inT)
153
{
154
// Destruct triangle
155
inT->~Triangle();
156
#ifdef JPH_DEBUG
157
memset(inT, 0xcd, sizeof(Triangle));
158
#endif
159
160
// Add triangle to the free list
161
Block *tu = reinterpret_cast<Block *>(inT);
162
tu->mNextFree = mNextFree;
163
mNextFree = tu;
164
}
165
};
166
167
// Typedefs
168
using PointsBase = StaticArray<Vec3, cMaxPoints>;
169
using Triangles = StaticArray<Triangle *, cMaxTriangles>;
170
171
/// Specialized points list that allows direct access to the size
172
class Points : public PointsBase
173
{
174
public:
175
size_type & GetSizeRef()
176
{
177
return mSize;
178
}
179
};
180
181
/// Specialized triangles list that keeps them sorted on closest distance to origin
182
class TriangleQueue : public Triangles
183
{
184
public:
185
/// Function to sort triangles on closest distance to origin
186
static bool sTriangleSorter(const Triangle *inT1, const Triangle *inT2)
187
{
188
return inT1->mClosestLenSq > inT2->mClosestLenSq;
189
}
190
191
/// Add triangle to the list
192
void push_back(Triangle *inT)
193
{
194
// Add to base
195
Triangles::push_back(inT);
196
197
// Mark in queue
198
inT->mInQueue = true;
199
200
// Resort heap
201
BinaryHeapPush(begin(), end(), sTriangleSorter);
202
}
203
204
/// Peek the next closest triangle without removing it
205
Triangle * PeekClosest()
206
{
207
return front();
208
}
209
210
/// Get next closest triangle
211
Triangle * PopClosest()
212
{
213
// Move closest to end
214
BinaryHeapPop(begin(), end(), sTriangleSorter);
215
216
// Remove last triangle
217
Triangle *t = back();
218
pop_back();
219
return t;
220
}
221
};
222
223
/// Constructor
224
explicit EPAConvexHullBuilder(const Points &inPositions) :
225
mPositions(inPositions)
226
{
227
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
228
mIteration = 0;
229
mOffset = RVec3::sZero();
230
#endif
231
}
232
233
/// Initialize the hull with 3 points
234
void Initialize(int inIdx1, int inIdx2, int inIdx3)
235
{
236
// Release triangles
237
mFactory.Clear();
238
239
// Create triangles (back to back)
240
Triangle *t1 = CreateTriangle(inIdx1, inIdx2, inIdx3);
241
Triangle *t2 = CreateTriangle(inIdx1, inIdx3, inIdx2);
242
243
// Link triangles edges
244
sLinkTriangle(t1, 0, t2, 2);
245
sLinkTriangle(t1, 1, t2, 1);
246
sLinkTriangle(t1, 2, t2, 0);
247
248
// Always add both triangles to the priority queue
249
mTriangleQueue.push_back(t1);
250
mTriangleQueue.push_back(t2);
251
252
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
253
// Draw current state
254
DrawState();
255
256
// Increment iteration counter
257
++mIteration;
258
#endif
259
}
260
261
/// Check if there's another triangle to process from the queue
262
bool HasNextTriangle() const
263
{
264
return !mTriangleQueue.empty();
265
}
266
267
/// Access to the next closest triangle to the origin (won't remove it from the queue).
268
Triangle * PeekClosestTriangleInQueue()
269
{
270
return mTriangleQueue.PeekClosest();
271
}
272
273
/// Access to the next closest triangle to the origin and remove it from the queue.
274
Triangle * PopClosestTriangleFromQueue()
275
{
276
return mTriangleQueue.PopClosest();
277
}
278
279
/// Find the triangle on which inPosition is the furthest to the front
280
/// Note this function works as long as all points added have been added with AddPoint(..., FLT_MAX).
281
Triangle * FindFacingTriangle(Vec3Arg inPosition, float &outBestDistSq)
282
{
283
Triangle *best = nullptr;
284
float best_dist_sq = 0.0f;
285
286
for (Triangle *t : mTriangleQueue)
287
if (!t->mRemoved)
288
{
289
float dot = t->mNormal.Dot(inPosition - t->mCentroid);
290
if (dot > 0.0f)
291
{
292
float dist_sq = dot * dot / t->mNormal.LengthSq();
293
if (dist_sq > best_dist_sq)
294
{
295
best = t;
296
best_dist_sq = dist_sq;
297
}
298
}
299
}
300
301
outBestDistSq = best_dist_sq;
302
return best;
303
}
304
305
/// Add a new point to the convex hull
306
bool AddPoint(Triangle *inFacingTriangle, int inIdx, float inClosestDistSq, NewTriangles &outTriangles)
307
{
308
// Get position
309
Vec3 pos = mPositions[inIdx];
310
311
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
312
// Draw new support point
313
DrawMarker(pos, Color::sYellow, 1.0f);
314
#endif
315
316
#ifdef JPH_EPA_CONVEX_BUILDER_VALIDATE
317
// Check if structure is intact
318
ValidateTriangles();
319
#endif
320
321
// Find edge of convex hull of triangles that are not facing the new vertex w
322
Edges edges;
323
if (!FindEdge(inFacingTriangle, pos, edges))
324
return false;
325
326
// Create new triangles
327
int num_edges = edges.size();
328
for (int i = 0; i < num_edges; ++i)
329
{
330
// Create new triangle
331
Triangle *nt = CreateTriangle(edges[i].mStartIdx, edges[(i + 1) % num_edges].mStartIdx, inIdx);
332
if (nt == nullptr)
333
return false;
334
outTriangles.push_back(nt);
335
336
// Check if we need to put this triangle in the priority queue
337
if ((nt->mClosestPointInterior && nt->mClosestLenSq < inClosestDistSq) // For the main algorithm
338
|| nt->mClosestLenSq < 0.0f) // For when the origin is not inside the hull yet
339
mTriangleQueue.push_back(nt);
340
}
341
342
// Link edges
343
for (int i = 0; i < num_edges; ++i)
344
{
345
sLinkTriangle(outTriangles[i], 0, edges[i].mNeighbourTriangle, edges[i].mNeighbourEdge);
346
sLinkTriangle(outTriangles[i], 1, outTriangles[(i + 1) % num_edges], 2);
347
}
348
349
#ifdef JPH_EPA_CONVEX_BUILDER_VALIDATE
350
// Check if structure is intact
351
ValidateTriangles();
352
#endif
353
354
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
355
// Draw state of the hull
356
DrawState();
357
358
// Increment iteration counter
359
++mIteration;
360
#endif
361
362
return true;
363
}
364
365
/// Free a triangle
366
void FreeTriangle(Triangle *inT)
367
{
368
#ifdef JPH_ENABLE_ASSERTS
369
// Make sure that this triangle is not connected
370
JPH_ASSERT(inT->mRemoved);
371
for (const Edge &e : inT->mEdge)
372
JPH_ASSERT(e.mNeighbourTriangle == nullptr);
373
#endif
374
375
#if defined(JPH_EPA_CONVEX_BUILDER_VALIDATE) || defined(JPH_EPA_CONVEX_BUILDER_DRAW)
376
// Remove from list of all triangles
377
Triangles::iterator i = std::find(mTriangles.begin(), mTriangles.end(), inT);
378
JPH_ASSERT(i != mTriangles.end());
379
mTriangles.erase(i);
380
#endif
381
382
mFactory.FreeTriangle(inT);
383
}
384
385
private:
386
/// Create a new triangle
387
Triangle * CreateTriangle(int inIdx1, int inIdx2, int inIdx3)
388
{
389
// Call provider to create triangle
390
Triangle *t = mFactory.CreateTriangle(inIdx1, inIdx2, inIdx3, mPositions.data());
391
if (t == nullptr)
392
return nullptr;
393
394
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
395
// Remember iteration counter
396
t->mIteration = mIteration;
397
#endif
398
399
#if defined(JPH_EPA_CONVEX_BUILDER_VALIDATE) || defined(JPH_EPA_CONVEX_BUILDER_DRAW)
400
// Add to list of triangles for debugging purposes
401
mTriangles.push_back(t);
402
#endif
403
404
return t;
405
}
406
407
/// Link triangle edge to other triangle edge
408
static void sLinkTriangle(Triangle *inT1, int inEdge1, Triangle *inT2, int inEdge2)
409
{
410
JPH_ASSERT(inEdge1 >= 0 && inEdge1 < 3);
411
JPH_ASSERT(inEdge2 >= 0 && inEdge2 < 3);
412
Edge &e1 = inT1->mEdge[inEdge1];
413
Edge &e2 = inT2->mEdge[inEdge2];
414
415
// Check not connected yet
416
JPH_ASSERT(e1.mNeighbourTriangle == nullptr);
417
JPH_ASSERT(e2.mNeighbourTriangle == nullptr);
418
419
// Check vertices match
420
JPH_ASSERT(e1.mStartIdx == inT2->GetNextEdge(inEdge2).mStartIdx);
421
JPH_ASSERT(e2.mStartIdx == inT1->GetNextEdge(inEdge1).mStartIdx);
422
423
// Link up
424
e1.mNeighbourTriangle = inT2;
425
e1.mNeighbourEdge = inEdge2;
426
e2.mNeighbourTriangle = inT1;
427
e2.mNeighbourEdge = inEdge1;
428
}
429
430
/// Unlink this triangle
431
void UnlinkTriangle(Triangle *inT)
432
{
433
// Unlink from neighbours
434
for (int i = 0; i < 3; ++i)
435
{
436
Edge &edge = inT->mEdge[i];
437
if (edge.mNeighbourTriangle != nullptr)
438
{
439
Edge &neighbour_edge = edge.mNeighbourTriangle->mEdge[edge.mNeighbourEdge];
440
441
// Validate that neighbour points to us
442
JPH_ASSERT(neighbour_edge.mNeighbourTriangle == inT);
443
JPH_ASSERT(neighbour_edge.mNeighbourEdge == i);
444
445
// Unlink
446
neighbour_edge.mNeighbourTriangle = nullptr;
447
edge.mNeighbourTriangle = nullptr;
448
}
449
}
450
451
// If this triangle is not in the priority queue, we can delete it now
452
if (!inT->mInQueue)
453
FreeTriangle(inT);
454
}
455
456
/// Given one triangle that faces inVertex, find the edges of the triangles that are not facing inVertex.
457
/// Will flag all those triangles for removal.
458
bool FindEdge(Triangle *inFacingTriangle, Vec3Arg inVertex, Edges &outEdges)
459
{
460
// Assert that we were given an empty array
461
JPH_ASSERT(outEdges.empty());
462
463
// Should start with a facing triangle
464
JPH_ASSERT(inFacingTriangle->IsFacing(inVertex));
465
466
// Flag as removed
467
inFacingTriangle->mRemoved = true;
468
469
// Instead of recursing, we build our own stack with the information we need
470
struct StackEntry
471
{
472
Triangle * mTriangle;
473
int mEdge;
474
int mIter;
475
};
476
StackEntry stack[cMaxEdgeLength];
477
int cur_stack_pos = 0;
478
479
// Start with the triangle / edge provided
480
stack[0].mTriangle = inFacingTriangle;
481
stack[0].mEdge = 0;
482
stack[0].mIter = -1; // Start with edge 0 (is incremented below before use)
483
484
// Next index that we expect to find, if we don't then there are 'islands'
485
int next_expected_start_idx = -1;
486
487
for (;;)
488
{
489
StackEntry &cur_entry = stack[cur_stack_pos];
490
491
// Next iteration
492
if (++cur_entry.mIter >= 3)
493
{
494
// This triangle needs to be removed, unlink it now
495
UnlinkTriangle(cur_entry.mTriangle);
496
497
// Pop from stack
498
if (--cur_stack_pos < 0)
499
break;
500
}
501
else
502
{
503
// Visit neighbour
504
Edge &e = cur_entry.mTriangle->mEdge[(cur_entry.mEdge + cur_entry.mIter) % 3];
505
Triangle *n = e.mNeighbourTriangle;
506
if (n != nullptr && !n->mRemoved)
507
{
508
// Check if vertex is on the front side of this triangle
509
if (n->IsFacing(inVertex))
510
{
511
// Vertex on front, this triangle needs to be removed
512
n->mRemoved = true;
513
514
// Add element to the stack of elements to visit
515
cur_stack_pos++;
516
JPH_ASSERT(cur_stack_pos < cMaxEdgeLength);
517
StackEntry &new_entry = stack[cur_stack_pos];
518
new_entry.mTriangle = n;
519
new_entry.mEdge = e.mNeighbourEdge;
520
new_entry.mIter = 0; // Is incremented before use, we don't need to test this edge again since we came from it
521
}
522
else
523
{
524
// Detect if edge doesn't connect to previous edge, if this happens we have found and 'island' which means
525
// the newly added point is so close to the triangles of the hull that we classified some (nearly) coplanar
526
// triangles as before and some behind the point. At this point we just abort adding the point because
527
// we've reached numerical precision.
528
// Note that we do not need to test if the first and last edge connect, since when there are islands
529
// there should be at least 2 disconnects.
530
if (e.mStartIdx != next_expected_start_idx && next_expected_start_idx != -1)
531
return false;
532
533
// Next expected index is the start index of our neighbour's edge
534
next_expected_start_idx = n->mEdge[e.mNeighbourEdge].mStartIdx;
535
536
// Vertex behind, keep edge
537
outEdges.push_back(e);
538
}
539
}
540
}
541
}
542
543
// Assert that we have a fully connected loop
544
JPH_ASSERT(outEdges.empty() || outEdges[0].mStartIdx == next_expected_start_idx);
545
546
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
547
// Draw edge of facing triangles
548
for (int i = 0; i < (int)outEdges.size(); ++i)
549
{
550
RVec3 edge_start = cDrawScale * (mOffset + mPositions[outEdges[i].mStartIdx]);
551
DebugRenderer::sInstance->DrawArrow(edge_start, cDrawScale * (mOffset + mPositions[outEdges[(i + 1) % outEdges.size()].mStartIdx]), Color::sYellow, 0.01f);
552
DebugRenderer::sInstance->DrawText3D(edge_start, ConvertToString(outEdges[i].mStartIdx), Color::sWhite);
553
}
554
555
// Draw the state with the facing triangles removed
556
DrawState();
557
#endif
558
559
// When we start with two triangles facing away from each other and adding a point that is on the plane,
560
// sometimes we consider the point in front of both causing both triangles to be removed resulting in an empty edge list.
561
// In this case we fail to add the point which will result in no collision reported (the shapes are contacting in 1 point so there's 0 penetration)
562
return outEdges.size() >= 3;
563
}
564
565
#ifdef JPH_EPA_CONVEX_BUILDER_VALIDATE
566
/// Check consistency of 1 triangle
567
void ValidateTriangle(const Triangle *inT) const
568
{
569
if (inT->mRemoved)
570
{
571
// Validate that removed triangles are not connected to anything
572
for (const Edge &my_edge : inT->mEdge)
573
JPH_ASSERT(my_edge.mNeighbourTriangle == nullptr);
574
}
575
else
576
{
577
for (int i = 0; i < 3; ++i)
578
{
579
const Edge &my_edge = inT->mEdge[i];
580
581
// Assert that we have a neighbour
582
const Triangle *nb = my_edge.mNeighbourTriangle;
583
JPH_ASSERT(nb != nullptr);
584
585
if (nb != nullptr)
586
{
587
// Assert that our neighbours edge points to us
588
const Edge &nb_edge = nb->mEdge[my_edge.mNeighbourEdge];
589
JPH_ASSERT(nb_edge.mNeighbourTriangle == inT);
590
JPH_ASSERT(nb_edge.mNeighbourEdge == i);
591
592
// Assert that the next edge of the neighbour points to the same vertex as this edge's vertex
593
const Edge &nb_next_edge = nb->GetNextEdge(my_edge.mNeighbourEdge);
594
JPH_ASSERT(nb_next_edge.mStartIdx == my_edge.mStartIdx);
595
596
// Assert that my next edge points to the same vertex as my neighbours vertex
597
const Edge &my_next_edge = inT->GetNextEdge(i);
598
JPH_ASSERT(my_next_edge.mStartIdx == nb_edge.mStartIdx);
599
}
600
}
601
}
602
}
603
604
/// Check consistency of all triangles
605
void ValidateTriangles() const
606
{
607
for (const Triangle *t : mTriangles)
608
ValidateTriangle(t);
609
}
610
#endif
611
612
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
613
public:
614
/// Draw state of algorithm
615
void DrawState()
616
{
617
// Draw origin
618
DebugRenderer::sInstance->DrawCoordinateSystem(RMat44::sTranslation(cDrawScale * mOffset), 1.0f);
619
620
// Draw triangles
621
for (const Triangle *t : mTriangles)
622
if (!t->mRemoved)
623
{
624
// Calculate the triangle vertices
625
RVec3 p1 = cDrawScale * (mOffset + mPositions[t->mEdge[0].mStartIdx]);
626
RVec3 p2 = cDrawScale * (mOffset + mPositions[t->mEdge[1].mStartIdx]);
627
RVec3 p3 = cDrawScale * (mOffset + mPositions[t->mEdge[2].mStartIdx]);
628
629
// Draw triangle
630
DebugRenderer::sInstance->DrawTriangle(p1, p2, p3, Color::sGetDistinctColor(t->mIteration));
631
DebugRenderer::sInstance->DrawWireTriangle(p1, p2, p3, Color::sGrey);
632
633
// Draw normal
634
RVec3 centroid = cDrawScale * (mOffset + t->mCentroid);
635
float len = t->mNormal.Length();
636
if (len > 0.0f)
637
DebugRenderer::sInstance->DrawArrow(centroid, centroid + t->mNormal / len, Color::sDarkGreen, 0.01f);
638
}
639
640
// Determine max position
641
float min_x = FLT_MAX;
642
float max_x = -FLT_MAX;
643
for (Vec3 p : mPositions)
644
{
645
min_x = min(min_x, p.GetX());
646
max_x = max(max_x, p.GetX());
647
}
648
649
// Offset to the right
650
mOffset += Vec3(max_x - min_x + 0.5f, 0.0f, 0.0f);
651
}
652
653
/// Draw a label to indicate the next stage in the algorithm
654
void DrawLabel(const string_view &inText)
655
{
656
DebugRenderer::sInstance->DrawText3D(cDrawScale * mOffset, inText, Color::sWhite, 0.1f * cDrawScale);
657
658
mOffset += Vec3(5.0f, 0.0f, 0.0f);
659
}
660
661
/// Draw geometry for debugging purposes
662
void DrawGeometry(const DebugRenderer::GeometryRef &inGeometry, ColorArg inColor)
663
{
664
RMat44 origin = RMat44::sScale(Vec3::sReplicate(cDrawScale)) * RMat44::sTranslation(mOffset);
665
DebugRenderer::sInstance->DrawGeometry(origin, inGeometry->mBounds.Transformed(origin), inGeometry->mBounds.GetExtent().LengthSq(), inColor, inGeometry);
666
667
mOffset += Vec3(inGeometry->mBounds.GetSize().GetX(), 0, 0);
668
}
669
670
/// Draw a triangle for debugging purposes
671
void DrawWireTriangle(const Triangle &inTriangle, ColorArg inColor)
672
{
673
RVec3 prev = cDrawScale * (mOffset + mPositions[inTriangle.mEdge[2].mStartIdx]);
674
for (const Edge &edge : inTriangle.mEdge)
675
{
676
RVec3 cur = cDrawScale * (mOffset + mPositions[edge.mStartIdx]);
677
DebugRenderer::sInstance->DrawArrow(prev, cur, inColor, 0.01f);
678
prev = cur;
679
}
680
}
681
682
/// Draw a marker for debugging purposes
683
void DrawMarker(Vec3Arg inPosition, ColorArg inColor, float inSize)
684
{
685
DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + inPosition), inColor, inSize);
686
}
687
688
/// Draw an arrow for debugging purposes
689
void DrawArrow(Vec3Arg inFrom, Vec3Arg inTo, ColorArg inColor, float inArrowSize)
690
{
691
DebugRenderer::sInstance->DrawArrow(cDrawScale * (mOffset + inFrom), cDrawScale * (mOffset + inTo), inColor, inArrowSize);
692
}
693
#endif
694
695
private:
696
TriangleFactory mFactory; ///< Factory to create new triangles and remove old ones
697
const Points & mPositions; ///< List of positions (some of them are part of the hull)
698
TriangleQueue mTriangleQueue; ///< List of triangles that are part of the hull that still need to be checked (if !mRemoved)
699
700
#if defined(JPH_EPA_CONVEX_BUILDER_VALIDATE) || defined(JPH_EPA_CONVEX_BUILDER_DRAW)
701
Triangles mTriangles; ///< The list of all triangles in this hull (for debug purposes)
702
#endif
703
704
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
705
int mIteration; ///< Number of iterations we've had so far (for debug purposes)
706
RVec3 mOffset; ///< Offset to use for state drawing
707
#endif
708
};
709
710
// The determinant that is calculated in the Triangle constructor is really sensitive
711
// to numerical round off, disable the fmadd instructions to maintain precision.
712
JPH_PRECISE_MATH_ON
713
714
EPAConvexHullBuilder::Triangle::Triangle(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions)
715
{
716
// Fill in indexes
717
JPH_ASSERT(inIdx0 != inIdx1 && inIdx0 != inIdx2 && inIdx1 != inIdx2);
718
mEdge[0].mStartIdx = inIdx0;
719
mEdge[1].mStartIdx = inIdx1;
720
mEdge[2].mStartIdx = inIdx2;
721
722
// Clear links
723
mEdge[0].mNeighbourTriangle = nullptr;
724
mEdge[1].mNeighbourTriangle = nullptr;
725
mEdge[2].mNeighbourTriangle = nullptr;
726
727
// Get vertex positions
728
Vec3 y0 = inPositions[inIdx0];
729
Vec3 y1 = inPositions[inIdx1];
730
Vec3 y2 = inPositions[inIdx2];
731
732
// Calculate centroid
733
mCentroid = (y0 + y1 + y2) / 3.0f;
734
735
// Calculate edges
736
Vec3 y10 = y1 - y0;
737
Vec3 y20 = y2 - y0;
738
Vec3 y21 = y2 - y1;
739
740
// The most accurate normal is calculated by using the two shortest edges
741
// See: https://box2d.org/posts/2014/01/troublesome-triangle/
742
// The difference in normals is most pronounced when one edge is much smaller than the others (in which case the other 2 must have roughly the same length).
743
// Therefore we can suffice by just picking the shortest from 2 edges and use that with the 3rd edge to calculate the normal.
744
// We first check which of the edges is shorter.
745
float y20_dot_y20 = y20.Dot(y20);
746
float y21_dot_y21 = y21.Dot(y21);
747
if (y20_dot_y20 < y21_dot_y21)
748
{
749
// We select the edges y10 and y20
750
mNormal = y10.Cross(y20);
751
752
// Check if triangle is degenerate
753
float normal_len_sq = mNormal.LengthSq();
754
if (normal_len_sq > cMinTriangleArea)
755
{
756
// Determine distance between triangle and origin: distance = (centroid - origin) . normal / |normal|
757
// Note that this way of calculating the closest point is much more accurate than first calculating barycentric coordinates and then calculating the closest
758
// point based on those coordinates. Note that we preserve the sign of the distance to check on which side the origin is.
759
float c_dot_n = mCentroid.Dot(mNormal);
760
mClosestLenSq = abs(c_dot_n) * c_dot_n / normal_len_sq;
761
762
// Calculate closest point to origin using barycentric coordinates:
763
//
764
// v = y0 + l0 * (y1 - y0) + l1 * (y2 - y0)
765
// v . (y1 - y0) = 0
766
// v . (y2 - y0) = 0
767
//
768
// Written in matrix form:
769
//
770
// | y10.y10 y20.y10 | | l0 | = | -y0.y10 |
771
// | y10.y20 y20.y20 | | l1 | | -y0.y20 |
772
//
773
// (y10 = y1 - y0 etc.)
774
//
775
// Cramers rule to invert matrix:
776
float y10_dot_y10 = y10.LengthSq();
777
float y10_dot_y20 = y10.Dot(y20);
778
float determinant = y10_dot_y10 * y20_dot_y20 - y10_dot_y20 * y10_dot_y20;
779
if (determinant > 0.0f) // If determinant == 0 then the system is linearly dependent and the triangle is degenerate, since y10.10 * y20.y20 > y10.y20^2 it should also be > 0
780
{
781
float y0_dot_y10 = y0.Dot(y10);
782
float y0_dot_y20 = y0.Dot(y20);
783
float l0 = (y10_dot_y20 * y0_dot_y20 - y20_dot_y20 * y0_dot_y10) / determinant;
784
float l1 = (y10_dot_y20 * y0_dot_y10 - y10_dot_y10 * y0_dot_y20) / determinant;
785
mLambda[0] = l0;
786
mLambda[1] = l1;
787
mLambdaRelativeTo0 = true;
788
789
// Check if closest point is interior to the triangle. For a convex hull which contains the origin each face must contain the origin, but because
790
// our faces are triangles, we can have multiple coplanar triangles and only 1 will have the origin as an interior point. We want to use this triangle
791
// to calculate the contact points because it gives the most accurate results, so we will only add these triangles to the priority queue.
792
if (l0 > -cBarycentricEpsilon && l1 > -cBarycentricEpsilon && l0 + l1 < 1.0f + cBarycentricEpsilon)
793
mClosestPointInterior = true;
794
}
795
}
796
}
797
else
798
{
799
// We select the edges y10 and y21
800
mNormal = y10.Cross(y21);
801
802
// Check if triangle is degenerate
803
float normal_len_sq = mNormal.LengthSq();
804
if (normal_len_sq > cMinTriangleArea)
805
{
806
// Again calculate distance between triangle and origin
807
float c_dot_n = mCentroid.Dot(mNormal);
808
mClosestLenSq = abs(c_dot_n) * c_dot_n / normal_len_sq;
809
810
// Calculate closest point to origin using barycentric coordinates but this time using y1 as the reference vertex
811
//
812
// v = y1 + l0 * (y0 - y1) + l1 * (y2 - y1)
813
// v . (y0 - y1) = 0
814
// v . (y2 - y1) = 0
815
//
816
// Written in matrix form:
817
//
818
// | y10.y10 -y21.y10 | | l0 | = | y1.y10 |
819
// | -y10.y21 y21.y21 | | l1 | | -y1.y21 |
820
//
821
// Cramers rule to invert matrix:
822
float y10_dot_y10 = y10.LengthSq();
823
float y10_dot_y21 = y10.Dot(y21);
824
float determinant = y10_dot_y10 * y21_dot_y21 - y10_dot_y21 * y10_dot_y21;
825
if (determinant > 0.0f)
826
{
827
float y1_dot_y10 = y1.Dot(y10);
828
float y1_dot_y21 = y1.Dot(y21);
829
float l0 = (y21_dot_y21 * y1_dot_y10 - y10_dot_y21 * y1_dot_y21) / determinant;
830
float l1 = (y10_dot_y21 * y1_dot_y10 - y10_dot_y10 * y1_dot_y21) / determinant;
831
mLambda[0] = l0;
832
mLambda[1] = l1;
833
mLambdaRelativeTo0 = false;
834
835
// Again check if the closest point is inside the triangle
836
if (l0 > -cBarycentricEpsilon && l1 > -cBarycentricEpsilon && l0 + l1 < 1.0f + cBarycentricEpsilon)
837
mClosestPointInterior = true;
838
}
839
}
840
}
841
}
842
843
JPH_PRECISE_MATH_OFF
844
845
JPH_NAMESPACE_END
846
847