Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Geometry/EPAPenetrationDepth.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
#include <Jolt/Core/StaticArray.h>
8
#include <Jolt/Core/Profiler.h>
9
#include <Jolt/Geometry/GJKClosestPoint.h>
10
#include <Jolt/Geometry/EPAConvexHullBuilder.h>
11
12
//#define JPH_EPA_PENETRATION_DEPTH_DEBUG
13
14
JPH_NAMESPACE_BEGIN
15
16
/// Implementation of Expanding Polytope Algorithm as described in:
17
///
18
/// Proximity Queries and Penetration Depth Computation on 3D Game Objects - Gino van den Bergen
19
///
20
/// The implementation of this algorithm does not completely follow the article, instead of splitting
21
/// triangles at each edge as in fig. 7 in the article, we build a convex hull (removing any triangles that
22
/// are facing the new point, thereby avoiding the problem of getting really oblong triangles as mentioned in
23
/// the article).
24
///
25
/// The algorithm roughly works like:
26
///
27
/// - Start with a simplex of the Minkowski sum (difference) of two objects that was calculated by GJK
28
/// - This simplex should contain the origin (or else GJK would have reported: no collision)
29
/// - In cases where the simplex consists of 1 - 3 points, find some extra support points (of the Minkowski sum) to get to at least 4 points
30
/// - Convert this into a convex hull with non-zero volume (which includes the origin)
31
/// - A: Calculate the closest point to the origin for all triangles of the hull and take the closest one
32
/// - Calculate a new support point (of the Minkowski sum) in this direction and add this point to the convex hull
33
/// - This will remove all faces that are facing the new point and will create new triangles to fill up the hole
34
/// - Loop to A until no closer point found
35
/// - The closest point indicates the position / direction of least penetration
36
class EPAPenetrationDepth
37
{
38
private:
39
// Typedefs
40
static constexpr int cMaxPoints = EPAConvexHullBuilder::cMaxPoints;
41
static constexpr int cMaxPointsToIncludeOriginInHull = 32;
42
static_assert(cMaxPointsToIncludeOriginInHull < cMaxPoints);
43
44
using Triangle = EPAConvexHullBuilder::Triangle;
45
using Points = EPAConvexHullBuilder::Points;
46
47
/// The GJK algorithm, used to start the EPA algorithm
48
GJKClosestPoint mGJK;
49
50
#ifdef JPH_ENABLE_ASSERTS
51
/// Tolerance as passed to the GJK algorithm, used for asserting.
52
float mGJKTolerance = 0.0f;
53
#endif // JPH_ENABLE_ASSERTS
54
55
/// A list of support points for the EPA algorithm
56
class SupportPoints
57
{
58
public:
59
/// List of support points
60
Points mY;
61
Vec3 mP[cMaxPoints];
62
Vec3 mQ[cMaxPoints];
63
64
/// Calculate and add new support point to the list of points
65
template <typename A, typename B>
66
Vec3 Add(const A &inA, const B &inB, Vec3Arg inDirection, int &outIndex)
67
{
68
// Get support point of the minkowski sum A - B
69
Vec3 p = inA.GetSupport(inDirection);
70
Vec3 q = inB.GetSupport(-inDirection);
71
Vec3 w = p - q;
72
73
// Store new point
74
outIndex = mY.size();
75
mY.push_back(w);
76
mP[outIndex] = p;
77
mQ[outIndex] = q;
78
79
return w;
80
}
81
};
82
83
public:
84
/// Return code for GetPenetrationDepthStepGJK
85
enum class EStatus
86
{
87
NotColliding, ///< Returned if the objects don't collide, in this case outPointA/outPointB are invalid
88
Colliding, ///< Returned if the objects penetrate
89
Indeterminate ///< Returned if the objects penetrate further than the convex radius. In this case you need to call GetPenetrationDepthStepEPA to get the actual penetration depth.
90
};
91
92
/// Calculates penetration depth between two objects, first step of two (the GJK step)
93
///
94
/// @param inAExcludingConvexRadius Object A without convex radius.
95
/// @param inBExcludingConvexRadius Object B without convex radius.
96
/// @param inConvexRadiusA Convex radius for A.
97
/// @param inConvexRadiusB Convex radius for B.
98
/// @param ioV Pass in previously returned value or (1, 0, 0). On return this value is changed to direction to move B out of collision along the shortest path (magnitude is meaningless).
99
/// @param inTolerance Minimal distance before A and B are considered colliding.
100
/// @param outPointA Position on A that has the least amount of penetration.
101
/// @param outPointB Position on B that has the least amount of penetration.
102
/// Use |outPointB - outPointA| to get the distance of penetration.
103
template <typename AE, typename BE>
104
EStatus GetPenetrationDepthStepGJK(const AE &inAExcludingConvexRadius, float inConvexRadiusA, const BE &inBExcludingConvexRadius, float inConvexRadiusB, float inTolerance, Vec3 &ioV, Vec3 &outPointA, Vec3 &outPointB)
105
{
106
JPH_PROFILE_FUNCTION();
107
108
JPH_IF_ENABLE_ASSERTS(mGJKTolerance = inTolerance;)
109
110
// Don't supply a zero ioV, we only want to get points on the hull of the Minkowsky sum and not internal points.
111
//
112
// Note that if the assert below triggers, it is very likely that you have a MeshShape that contains a degenerate triangle (e.g. a sliver).
113
// Go up a couple of levels in the call stack to see if we're indeed testing a triangle and if it is degenerate.
114
// If this is the case then fix the triangles you supply to the MeshShape.
115
JPH_ASSERT(!ioV.IsNearZero());
116
117
// Get closest points
118
float combined_radius = inConvexRadiusA + inConvexRadiusB;
119
float combined_radius_sq = combined_radius * combined_radius;
120
float closest_points_dist_sq = mGJK.GetClosestPoints(inAExcludingConvexRadius, inBExcludingConvexRadius, inTolerance, combined_radius_sq, ioV, outPointA, outPointB);
121
if (closest_points_dist_sq > combined_radius_sq)
122
{
123
// No collision
124
return EStatus::NotColliding;
125
}
126
if (closest_points_dist_sq > 0.0f)
127
{
128
// Collision within convex radius, adjust points for convex radius
129
float v_len = sqrt(closest_points_dist_sq); // GetClosestPoints function returns |ioV|^2 when return value < FLT_MAX
130
outPointA += ioV * (inConvexRadiusA / v_len);
131
outPointB -= ioV * (inConvexRadiusB / v_len);
132
return EStatus::Colliding;
133
}
134
135
return EStatus::Indeterminate;
136
}
137
138
/// Calculates penetration depth between two objects, second step (the EPA step)
139
///
140
/// @param inAIncludingConvexRadius Object A with convex radius
141
/// @param inBIncludingConvexRadius Object B with convex radius
142
/// @param inTolerance A factor that determines the accuracy of the result. If the change of the squared distance is less than inTolerance * current_penetration_depth^2 the algorithm will terminate. Should be bigger or equal to FLT_EPSILON.
143
/// @param outV Direction to move B out of collision along the shortest path (magnitude is meaningless)
144
/// @param outPointA Position on A that has the least amount of penetration
145
/// @param outPointB Position on B that has the least amount of penetration
146
/// Use |outPointB - outPointA| to get the distance of penetration
147
///
148
/// @return False if the objects don't collide, in this case outPointA/outPointB are invalid.
149
/// True if the objects penetrate
150
template <typename AI, typename BI>
151
bool GetPenetrationDepthStepEPA(const AI &inAIncludingConvexRadius, const BI &inBIncludingConvexRadius, float inTolerance, Vec3 &outV, Vec3 &outPointA, Vec3 &outPointB)
152
{
153
JPH_PROFILE_FUNCTION();
154
155
// Check that the tolerance makes sense (smaller value than this will just result in needless iterations)
156
JPH_ASSERT(inTolerance >= FLT_EPSILON);
157
158
// Fetch the simplex from GJK algorithm
159
SupportPoints support_points;
160
mGJK.GetClosestPointsSimplex(support_points.mY.data(), support_points.mP, support_points.mQ, support_points.mY.GetSizeRef());
161
162
// Fill up the amount of support points to 4
163
switch (support_points.mY.size())
164
{
165
case 1:
166
{
167
// 1 vertex, which must be at the origin, which is useless for our purpose
168
JPH_ASSERT(support_points.mY[0].IsNearZero(Square(mGJKTolerance)));
169
support_points.mY.pop_back();
170
171
// Add support points in 4 directions to form a tetrahedron around the origin
172
int p1, p2, p3, p4;
173
(void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(0, 1, 0), p1);
174
(void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(-1, -1, -1), p2);
175
(void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(1, -1, -1), p3);
176
(void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(0, -1, 1), p4);
177
JPH_ASSERT(p1 == 0);
178
JPH_ASSERT(p2 == 1);
179
JPH_ASSERT(p3 == 2);
180
JPH_ASSERT(p4 == 3);
181
break;
182
}
183
184
case 2:
185
{
186
// Two vertices, create 3 extra by taking perpendicular axis and rotating it around in 120 degree increments
187
Vec3 axis = (support_points.mY[1] - support_points.mY[0]).Normalized();
188
Mat44 rotation = Mat44::sRotation(axis, DegreesToRadians(120.0f));
189
Vec3 dir1 = axis.GetNormalizedPerpendicular();
190
Vec3 dir2 = rotation * dir1;
191
Vec3 dir3 = rotation * dir2;
192
int p1, p2, p3;
193
(void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, dir1, p1);
194
(void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, dir2, p2);
195
(void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, dir3, p3);
196
JPH_ASSERT(p1 == 2);
197
JPH_ASSERT(p2 == 3);
198
JPH_ASSERT(p3 == 4);
199
break;
200
}
201
202
case 3:
203
case 4:
204
// We already have enough points
205
break;
206
}
207
208
// Create hull out of the initial points
209
JPH_ASSERT(support_points.mY.size() >= 3);
210
EPAConvexHullBuilder hull(support_points.mY);
211
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
212
hull.DrawLabel("Build initial hull");
213
#endif
214
#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
215
Trace("Init: num_points = %u", (uint)support_points.mY.size());
216
#endif
217
hull.Initialize(0, 1, 2);
218
for (typename Points::size_type i = 3; i < support_points.mY.size(); ++i)
219
{
220
float dist_sq;
221
Triangle *t = hull.FindFacingTriangle(support_points.mY[i], dist_sq);
222
if (t != nullptr)
223
{
224
EPAConvexHullBuilder::NewTriangles new_triangles;
225
if (!hull.AddPoint(t, i, FLT_MAX, new_triangles))
226
{
227
// We can't recover from a failure to add a point to the hull because the old triangles have been unlinked already.
228
// Assume no collision. This can happen if the shapes touch in 1 point (or plane) in which case the hull is degenerate.
229
return false;
230
}
231
}
232
}
233
234
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
235
hull.DrawLabel("Complete hull");
236
237
// Generate the hull of the Minkowski difference for visualization
238
MinkowskiDifference diff(inAIncludingConvexRadius, inBIncludingConvexRadius);
239
DebugRenderer::GeometryRef geometry = DebugRenderer::sInstance->CreateTriangleGeometryForConvex([&diff](Vec3Arg inDirection) { return diff.GetSupport(inDirection); });
240
hull.DrawGeometry(geometry, Color::sYellow);
241
242
hull.DrawLabel("Ensure origin in hull");
243
#endif
244
245
// Loop until we are sure that the origin is inside the hull
246
for (;;)
247
{
248
// Get the next closest triangle
249
Triangle *t = hull.PeekClosestTriangleInQueue();
250
251
// Don't process removed triangles, just free them (because they're in a heap we don't remove them earlier since we would have to rebuild the sorted heap)
252
if (t->mRemoved)
253
{
254
hull.PopClosestTriangleFromQueue();
255
256
// If we run out of triangles, we couldn't include the origin in the hull so there must be very little penetration and we report no collision.
257
if (!hull.HasNextTriangle())
258
return false;
259
260
hull.FreeTriangle(t);
261
continue;
262
}
263
264
// If the closest to the triangle is zero or positive, the origin is in the hull and we can proceed to the main algorithm
265
if (t->mClosestLenSq >= 0.0f)
266
break;
267
268
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
269
hull.DrawLabel("Next iteration");
270
#endif
271
#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
272
Trace("EncapsulateOrigin: verts = (%d, %d, %d), closest_dist_sq = %g, centroid = (%g, %g, %g), normal = (%g, %g, %g)",
273
t->mEdge[0].mStartIdx, t->mEdge[1].mStartIdx, t->mEdge[2].mStartIdx,
274
t->mClosestLenSq,
275
t->mCentroid.GetX(), t->mCentroid.GetY(), t->mCentroid.GetZ(),
276
t->mNormal.GetX(), t->mNormal.GetY(), t->mNormal.GetZ());
277
#endif
278
279
// Remove the triangle from the queue before we start adding new ones (which may result in a new closest triangle at the front of the queue)
280
hull.PopClosestTriangleFromQueue();
281
282
// Add a support point to get the origin inside the hull
283
int new_index;
284
Vec3 w = support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, t->mNormal, new_index);
285
286
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
287
// Draw the point that we're adding
288
hull.DrawMarker(w, Color::sRed, 1.0f);
289
hull.DrawWireTriangle(*t, Color::sRed);
290
hull.DrawState();
291
#endif
292
293
// Add the point to the hull, if we fail we terminate and report no collision
294
EPAConvexHullBuilder::NewTriangles new_triangles;
295
if (!t->IsFacing(w) || !hull.AddPoint(t, new_index, FLT_MAX, new_triangles))
296
return false;
297
298
// The triangle is facing the support point "w" and can now be safely removed
299
JPH_ASSERT(t->mRemoved);
300
hull.FreeTriangle(t);
301
302
// If we run out of triangles or points, we couldn't include the origin in the hull so there must be very little penetration and we report no collision.
303
if (!hull.HasNextTriangle() || support_points.mY.size() >= cMaxPointsToIncludeOriginInHull)
304
return false;
305
}
306
307
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
308
hull.DrawLabel("Main algorithm");
309
#endif
310
311
// Current closest distance to origin
312
float closest_dist_sq = FLT_MAX;
313
314
// Remember last good triangle
315
Triangle *last = nullptr;
316
317
// If we want to flip the penetration depth
318
bool flip_v_sign = false;
319
320
// Loop until closest point found
321
do
322
{
323
// Get closest triangle to the origin
324
Triangle *t = hull.PopClosestTriangleFromQueue();
325
326
// Don't process removed triangles, just free them (because they're in a heap we don't remove them earlier since we would have to rebuild the sorted heap)
327
if (t->mRemoved)
328
{
329
hull.FreeTriangle(t);
330
continue;
331
}
332
333
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
334
hull.DrawLabel("Next iteration");
335
#endif
336
#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
337
Trace("FindClosest: verts = (%d, %d, %d), closest_len_sq = %g, centroid = (%g, %g, %g), normal = (%g, %g, %g)",
338
t->mEdge[0].mStartIdx, t->mEdge[1].mStartIdx, t->mEdge[2].mStartIdx,
339
t->mClosestLenSq,
340
t->mCentroid.GetX(), t->mCentroid.GetY(), t->mCentroid.GetZ(),
341
t->mNormal.GetX(), t->mNormal.GetY(), t->mNormal.GetZ());
342
#endif
343
// Check if next triangle is further away than closest point, we've found the closest point
344
if (t->mClosestLenSq >= closest_dist_sq)
345
break;
346
347
// Replace last good with this triangle
348
if (last != nullptr)
349
hull.FreeTriangle(last);
350
last = t;
351
352
// Add support point in direction of normal of the plane
353
// Note that the article uses the closest point between the origin and plane, but this always has the exact same direction as the normal (if the origin is behind the plane)
354
// and this way we do less calculations and lose less precision
355
int new_index;
356
Vec3 w = support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, t->mNormal, new_index);
357
358
// Project w onto the triangle normal
359
float dot = t->mNormal.Dot(w);
360
361
// Check if we just found a separating axis. This can happen if the shape shrunk by convex radius and then expanded by
362
// convex radius is bigger then the original shape due to inaccuracies in the shrinking process.
363
if (dot < 0.0f)
364
return false;
365
366
// Get the distance squared (along normal) to the support point
367
float dist_sq = Square(dot) / t->mNormal.LengthSq();
368
369
#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
370
Trace("FindClosest: w = (%g, %g, %g), dot = %g, dist_sq = %g",
371
w.GetX(), w.GetY(), w.GetZ(),
372
dot, dist_sq);
373
#endif
374
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
375
// Draw the point that we're adding
376
hull.DrawMarker(w, Color::sPurple, 1.0f);
377
hull.DrawWireTriangle(*t, Color::sPurple);
378
hull.DrawState();
379
#endif
380
381
// If the error became small enough, we've converged
382
if (dist_sq - t->mClosestLenSq < t->mClosestLenSq * inTolerance)
383
{
384
#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
385
Trace("Converged");
386
#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG
387
break;
388
}
389
390
// Keep track of the minimum distance
391
closest_dist_sq = min(closest_dist_sq, dist_sq);
392
393
// If the triangle thinks this point is not front facing, we've reached numerical precision and we're done
394
if (!t->IsFacing(w))
395
{
396
#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
397
Trace("Not facing triangle");
398
#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG
399
break;
400
}
401
402
// Add point to hull
403
EPAConvexHullBuilder::NewTriangles new_triangles;
404
if (!hull.AddPoint(t, new_index, closest_dist_sq, new_triangles))
405
{
406
#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
407
Trace("Could not add point");
408
#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG
409
break;
410
}
411
412
// If the hull is starting to form defects then we're reaching numerical precision and we have to stop
413
bool has_defect = false;
414
for (const Triangle *nt : new_triangles)
415
if (nt->IsFacingOrigin())
416
{
417
has_defect = true;
418
break;
419
}
420
if (has_defect)
421
{
422
#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG
423
Trace("Has defect");
424
#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG
425
// When the hull has defects it is possible that the origin has been classified on the wrong side of the triangle
426
// so we do an additional check to see if the penetration in the -triangle normal direction is smaller than
427
// the penetration in the triangle normal direction. If so we must flip the sign of the penetration depth.
428
Vec3 w2 = inAIncludingConvexRadius.GetSupport(-t->mNormal) - inBIncludingConvexRadius.GetSupport(t->mNormal);
429
float dot2 = -t->mNormal.Dot(w2);
430
if (dot2 < dot)
431
flip_v_sign = true;
432
break;
433
}
434
}
435
while (hull.HasNextTriangle() && support_points.mY.size() < cMaxPoints);
436
437
// Determine closest points, if last == null it means the hull was a plane so there's no penetration
438
if (last == nullptr)
439
return false;
440
441
#ifdef JPH_EPA_CONVEX_BUILDER_DRAW
442
hull.DrawLabel("Closest found");
443
hull.DrawWireTriangle(*last, Color::sWhite);
444
hull.DrawArrow(last->mCentroid, last->mCentroid + last->mNormal.NormalizedOr(Vec3::sZero()), Color::sWhite, 0.1f);
445
hull.DrawState();
446
#endif
447
448
// Calculate penetration by getting the vector from the origin to the closest point on the triangle:
449
// distance = (centroid - origin) . normal / |normal|, closest = origin + distance * normal / |normal|
450
outV = (last->mCentroid.Dot(last->mNormal) / last->mNormal.LengthSq()) * last->mNormal;
451
452
// If penetration is near zero, treat this as a non collision since we cannot find a good normal
453
if (outV.IsNearZero())
454
return false;
455
456
// Check if we have to flip the sign of the penetration depth
457
if (flip_v_sign)
458
outV = -outV;
459
460
// Use the barycentric coordinates for the closest point to the origin to find the contact points on A and B
461
Vec3 p0 = support_points.mP[last->mEdge[0].mStartIdx];
462
Vec3 p1 = support_points.mP[last->mEdge[1].mStartIdx];
463
Vec3 p2 = support_points.mP[last->mEdge[2].mStartIdx];
464
465
Vec3 q0 = support_points.mQ[last->mEdge[0].mStartIdx];
466
Vec3 q1 = support_points.mQ[last->mEdge[1].mStartIdx];
467
Vec3 q2 = support_points.mQ[last->mEdge[2].mStartIdx];
468
469
if (last->mLambdaRelativeTo0)
470
{
471
// y0 was the reference vertex
472
outPointA = p0 + last->mLambda[0] * (p1 - p0) + last->mLambda[1] * (p2 - p0);
473
outPointB = q0 + last->mLambda[0] * (q1 - q0) + last->mLambda[1] * (q2 - q0);
474
}
475
else
476
{
477
// y1 was the reference vertex
478
outPointA = p1 + last->mLambda[0] * (p0 - p1) + last->mLambda[1] * (p2 - p1);
479
outPointB = q1 + last->mLambda[0] * (q0 - q1) + last->mLambda[1] * (q2 - q1);
480
}
481
482
return true;
483
}
484
485
/// This function combines the GJK and EPA steps and is provided as a convenience function.
486
/// Note: less performant since you're providing all support functions in one go
487
/// Note 2: You need to initialize ioV, see documentation at GetPenetrationDepthStepGJK!
488
template <typename AE, typename AI, typename BE, typename BI>
489
bool GetPenetrationDepth(const AE &inAExcludingConvexRadius, const AI &inAIncludingConvexRadius, float inConvexRadiusA, const BE &inBExcludingConvexRadius, const BI &inBIncludingConvexRadius, float inConvexRadiusB, float inCollisionToleranceSq, float inPenetrationTolerance, Vec3 &ioV, Vec3 &outPointA, Vec3 &outPointB)
490
{
491
// Check result of collision detection
492
switch (GetPenetrationDepthStepGJK(inAExcludingConvexRadius, inConvexRadiusA, inBExcludingConvexRadius, inConvexRadiusB, inCollisionToleranceSq, ioV, outPointA, outPointB))
493
{
494
case EPAPenetrationDepth::EStatus::Colliding:
495
return true;
496
497
case EPAPenetrationDepth::EStatus::NotColliding:
498
return false;
499
500
case EPAPenetrationDepth::EStatus::Indeterminate:
501
return GetPenetrationDepthStepEPA(inAIncludingConvexRadius, inBIncludingConvexRadius, inPenetrationTolerance, ioV, outPointA, outPointB);
502
}
503
504
JPH_ASSERT(false);
505
return false;
506
}
507
508
/// Test if a cast shape inA moving from inStart to lambda * inStart.GetTranslation() + inDirection where lambda e [0, ioLambda> intersects inB
509
///
510
/// @param inStart Start position and orientation of the convex object
511
/// @param inDirection Direction of the sweep (ioLambda * inDirection determines length)
512
/// @param inCollisionTolerance The minimal distance between A and B before they are considered colliding
513
/// @param inPenetrationTolerance A factor that determines the accuracy of the result. If the change of the squared distance is less than inTolerance * current_penetration_depth^2 the algorithm will terminate. Should be bigger or equal to FLT_EPSILON.
514
/// @param inA The convex object A, must support the GetSupport(Vec3) function.
515
/// @param inB The convex object B, must support the GetSupport(Vec3) function.
516
/// @param inConvexRadiusA The convex radius of A, this will be added on all sides to pad A.
517
/// @param inConvexRadiusB The convex radius of B, this will be added on all sides to pad B.
518
/// @param inReturnDeepestPoint If the shapes are initially intersecting this determines if the EPA algorithm will run to find the deepest point
519
/// @param ioLambda The max fraction along the sweep, on output updated with the actual collision fraction.
520
/// @param outPointA is the contact point on A
521
/// @param outPointB is the contact point on B
522
/// @param outContactNormal is either the contact normal when the objects are touching or the penetration axis when the objects are penetrating at the start of the sweep (pointing from A to B, length will not be 1)
523
///
524
/// @return true if the a hit was found, in which case ioLambda, outPointA, outPointB and outSurfaceNormal are updated.
525
template <typename A, typename B>
526
bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inCollisionTolerance, float inPenetrationTolerance, const A &inA, const B &inB, float inConvexRadiusA, float inConvexRadiusB, bool inReturnDeepestPoint, float &ioLambda, Vec3 &outPointA, Vec3 &outPointB, Vec3 &outContactNormal)
527
{
528
JPH_IF_ENABLE_ASSERTS(mGJKTolerance = inCollisionTolerance;)
529
530
// First determine if there's a collision at all
531
if (!mGJK.CastShape(inStart, inDirection, inCollisionTolerance, inA, inB, inConvexRadiusA, inConvexRadiusB, ioLambda, outPointA, outPointB, outContactNormal))
532
return false;
533
534
// When our contact normal is too small, we don't have an accurate result
535
bool contact_normal_invalid = outContactNormal.IsNearZero(Square(inCollisionTolerance));
536
537
if (inReturnDeepestPoint
538
&& ioLambda == 0.0f // Only when lambda = 0 we can have the bodies overlap
539
&& (inConvexRadiusA + inConvexRadiusB == 0.0f // When no convex radius was provided we can never trust contact points at lambda = 0
540
|| contact_normal_invalid))
541
{
542
// If we're initially intersecting, we need to run the EPA algorithm in order to find the deepest contact point
543
AddConvexRadius add_convex_a(inA, inConvexRadiusA);
544
AddConvexRadius add_convex_b(inB, inConvexRadiusB);
545
TransformedConvexObject transformed_a(inStart, add_convex_a);
546
if (!GetPenetrationDepthStepEPA(transformed_a, add_convex_b, inPenetrationTolerance, outContactNormal, outPointA, outPointB))
547
return false;
548
}
549
else if (contact_normal_invalid)
550
{
551
// If we weren't able to calculate a contact normal, use the cast direction instead
552
outContactNormal = inDirection;
553
}
554
555
return true;
556
}
557
};
558
559
JPH_NAMESPACE_END
560
561