Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Geometry/ClosestPoint.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
JPH_NAMESPACE_BEGIN
8
9
// Turn off fused multiply add instruction because it makes the equations of the form a * b - c * d inaccurate below
10
JPH_PRECISE_MATH_ON
11
12
/// Helper utils to find the closest point to a line segment, triangle or tetrahedron
13
namespace ClosestPoint
14
{
15
/// Compute barycentric coordinates of closest point to origin for infinite line defined by (inA, inB)
16
/// Point can then be computed as inA * outU + inB * outV
17
/// Returns false if the points inA, inB do not form a line (are at the same point)
18
inline bool GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, float &outU, float &outV)
19
{
20
Vec3 ab = inB - inA;
21
float denominator = ab.LengthSq();
22
if (denominator < Square(FLT_EPSILON))
23
{
24
// Degenerate line segment, fallback to points
25
if (inA.LengthSq() < inB.LengthSq())
26
{
27
// A closest
28
outU = 1.0f;
29
outV = 0.0f;
30
}
31
else
32
{
33
// B closest
34
outU = 0.0f;
35
outV = 1.0f;
36
}
37
return false;
38
}
39
else
40
{
41
outV = -inA.Dot(ab) / denominator;
42
outU = 1.0f - outV;
43
}
44
return true;
45
}
46
47
/// Compute barycentric coordinates of closest point to origin for plane defined by (inA, inB, inC)
48
/// Point can then be computed as inA * outU + inB * outV + inC * outW
49
/// Returns false if the points inA, inB, inC do not form a plane (are on the same line or at the same point)
50
inline bool GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, float &outU, float &outV, float &outW)
51
{
52
// Taken from: Real-Time Collision Detection - Christer Ericson (Section: Barycentric Coordinates)
53
// With p = 0
54
// Adjusted to always include the shortest edge of the triangle in the calculation to improve numerical accuracy
55
56
// First calculate the three edges
57
Vec3 v0 = inB - inA;
58
Vec3 v1 = inC - inA;
59
Vec3 v2 = inC - inB;
60
61
// Make sure that the shortest edge is included in the calculation to keep the products a * b - c * d as small as possible to preserve accuracy
62
float d00 = v0.LengthSq();
63
float d11 = v1.LengthSq();
64
float d22 = v2.LengthSq();
65
if (d00 <= d22)
66
{
67
// Use v0 and v1 to calculate barycentric coordinates
68
float d01 = v0.Dot(v1);
69
70
// Denominator must be positive:
71
// |v0|^2 * |v1|^2 - (v0 . v1)^2 = |v0|^2 * |v1|^2 * (1 - cos(angle)^2) >= 0
72
float denominator = d00 * d11 - d01 * d01;
73
if (denominator < 1.0e-12f)
74
{
75
// Degenerate triangle, return coordinates along longest edge
76
if (d00 > d11)
77
{
78
GetBaryCentricCoordinates(inA, inB, outU, outV);
79
outW = 0.0f;
80
}
81
else
82
{
83
GetBaryCentricCoordinates(inA, inC, outU, outW);
84
outV = 0.0f;
85
}
86
return false;
87
}
88
else
89
{
90
float a0 = inA.Dot(v0);
91
float a1 = inA.Dot(v1);
92
outV = (d01 * a1 - d11 * a0) / denominator;
93
outW = (d01 * a0 - d00 * a1) / denominator;
94
outU = 1.0f - outV - outW;
95
}
96
}
97
else
98
{
99
// Use v1 and v2 to calculate barycentric coordinates
100
float d12 = v1.Dot(v2);
101
102
float denominator = d11 * d22 - d12 * d12;
103
if (denominator < 1.0e-12f)
104
{
105
// Degenerate triangle, return coordinates along longest edge
106
if (d11 > d22)
107
{
108
GetBaryCentricCoordinates(inA, inC, outU, outW);
109
outV = 0.0f;
110
}
111
else
112
{
113
GetBaryCentricCoordinates(inB, inC, outV, outW);
114
outU = 0.0f;
115
}
116
return false;
117
}
118
else
119
{
120
float c1 = inC.Dot(v1);
121
float c2 = inC.Dot(v2);
122
outU = (d22 * c1 - d12 * c2) / denominator;
123
outV = (d11 * c2 - d12 * c1) / denominator;
124
outW = 1.0f - outU - outV;
125
}
126
}
127
return true;
128
}
129
130
/// Get the closest point to the origin of line (inA, inB)
131
/// outSet describes which features are closest: 1 = a, 2 = b, 3 = line segment ab
132
inline Vec3 GetClosestPointOnLine(Vec3Arg inA, Vec3Arg inB, uint32 &outSet)
133
{
134
float u, v;
135
GetBaryCentricCoordinates(inA, inB, u, v);
136
if (v <= 0.0f)
137
{
138
// inA is closest point
139
outSet = 0b0001;
140
return inA;
141
}
142
else if (u <= 0.0f)
143
{
144
// inB is closest point
145
outSet = 0b0010;
146
return inB;
147
}
148
else
149
{
150
// Closest point lies on line inA inB
151
outSet = 0b0011;
152
return u * inA + v * inB;
153
}
154
}
155
156
/// Get the closest point to the origin of triangle (inA, inB, inC)
157
/// outSet describes which features are closest: 1 = a, 2 = b, 4 = c, 5 = line segment ac, 7 = triangle interior etc.
158
/// If MustIncludeC is true, the function assumes that C is part of the closest feature (vertex, edge, face) and does less work, if the assumption is not true then a closest point to the other features is returned.
159
template <bool MustIncludeC = false>
160
inline Vec3 GetClosestPointOnTriangle(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, uint32 &outSet)
161
{
162
// Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Triangle to Point)
163
// With p = 0
164
165
// The most accurate normal is calculated by using the two shortest edges
166
// See: https://box2d.org/posts/2014/01/troublesome-triangle/
167
// 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).
168
// Therefore we can suffice by just picking the shortest from 2 edges and use that with the 3rd edge to calculate the normal.
169
// We first check which of the edges is shorter and if bc is shorter than ac then we swap a with c to a is always on the shortest edge
170
UVec4 swap_ac;
171
{
172
Vec3 ac = inC - inA;
173
Vec3 bc = inC - inB;
174
swap_ac = Vec4::sLess(bc.DotV4(bc), ac.DotV4(ac));
175
}
176
Vec3 a = Vec3::sSelect(inA, inC, swap_ac);
177
Vec3 c = Vec3::sSelect(inC, inA, swap_ac);
178
179
// Calculate normal
180
Vec3 ab = inB - a;
181
Vec3 ac = c - a;
182
Vec3 n = ab.Cross(ac);
183
float n_len_sq = n.LengthSq();
184
185
// Check degenerate
186
if (n_len_sq < 1.0e-10f) // Square(FLT_EPSILON) was too small and caused numerical problems, see test case TestCollideParallelTriangleVsCapsule
187
{
188
// Degenerate, fallback to vertices and edges
189
190
// Start with vertex C being the closest
191
uint32 closest_set = 0b0100;
192
Vec3 closest_point = inC;
193
float best_dist_sq = inC.LengthSq();
194
195
// If the closest point must include C then A or B cannot be closest
196
// Note that we test vertices first because we want to prefer a closest vertex over a closest edge (this results in an outSet with fewer bits set)
197
if constexpr (!MustIncludeC)
198
{
199
// Try vertex A
200
float a_len_sq = inA.LengthSq();
201
if (a_len_sq < best_dist_sq)
202
{
203
closest_set = 0b0001;
204
closest_point = inA;
205
best_dist_sq = a_len_sq;
206
}
207
208
// Try vertex B
209
float b_len_sq = inB.LengthSq();
210
if (b_len_sq < best_dist_sq)
211
{
212
closest_set = 0b0010;
213
closest_point = inB;
214
best_dist_sq = b_len_sq;
215
}
216
}
217
218
// Edge AC
219
float ac_len_sq = ac.LengthSq();
220
if (ac_len_sq > Square(FLT_EPSILON))
221
{
222
float v = Clamp(-a.Dot(ac) / ac_len_sq, 0.0f, 1.0f);
223
Vec3 q = a + v * ac;
224
float dist_sq = q.LengthSq();
225
if (dist_sq < best_dist_sq)
226
{
227
closest_set = 0b0101;
228
closest_point = q;
229
best_dist_sq = dist_sq;
230
}
231
}
232
233
// Edge BC
234
Vec3 bc = inC - inB;
235
float bc_len_sq = bc.LengthSq();
236
if (bc_len_sq > Square(FLT_EPSILON))
237
{
238
float v = Clamp(-inB.Dot(bc) / bc_len_sq, 0.0f, 1.0f);
239
Vec3 q = inB + v * bc;
240
float dist_sq = q.LengthSq();
241
if (dist_sq < best_dist_sq)
242
{
243
closest_set = 0b0110;
244
closest_point = q;
245
best_dist_sq = dist_sq;
246
}
247
}
248
249
// If the closest point must include C then AB cannot be closest
250
if constexpr (!MustIncludeC)
251
{
252
// Edge AB
253
ab = inB - inA;
254
float ab_len_sq = ab.LengthSq();
255
if (ab_len_sq > Square(FLT_EPSILON))
256
{
257
float v = Clamp(-inA.Dot(ab) / ab_len_sq, 0.0f, 1.0f);
258
Vec3 q = inA + v * ab;
259
float dist_sq = q.LengthSq();
260
if (dist_sq < best_dist_sq)
261
{
262
closest_set = 0b0011;
263
closest_point = q;
264
best_dist_sq = dist_sq;
265
}
266
}
267
}
268
269
outSet = closest_set;
270
return closest_point;
271
}
272
273
// Check if P in vertex region outside A
274
Vec3 ap = -a;
275
float d1 = ab.Dot(ap);
276
float d2 = ac.Dot(ap);
277
if (d1 <= 0.0f && d2 <= 0.0f)
278
{
279
outSet = swap_ac.GetX()? 0b0100 : 0b0001;
280
return a; // barycentric coordinates (1,0,0)
281
}
282
283
// Check if P in vertex region outside B
284
Vec3 bp = -inB;
285
float d3 = ab.Dot(bp);
286
float d4 = ac.Dot(bp);
287
if (d3 >= 0.0f && d4 <= d3)
288
{
289
outSet = 0b0010;
290
return inB; // barycentric coordinates (0,1,0)
291
}
292
293
// Check if P in edge region of AB, if so return projection of P onto AB
294
if (d1 * d4 <= d3 * d2 && d1 >= 0.0f && d3 <= 0.0f)
295
{
296
float v = d1 / (d1 - d3);
297
outSet = swap_ac.GetX()? 0b0110 : 0b0011;
298
return a + v * ab; // barycentric coordinates (1-v,v,0)
299
}
300
301
// Check if P in vertex region outside C
302
Vec3 cp = -c;
303
float d5 = ab.Dot(cp);
304
float d6 = ac.Dot(cp);
305
if (d6 >= 0.0f && d5 <= d6)
306
{
307
outSet = swap_ac.GetX()? 0b0001 : 0b0100;
308
return c; // barycentric coordinates (0,0,1)
309
}
310
311
// Check if P in edge region of AC, if so return projection of P onto AC
312
if (d5 * d2 <= d1 * d6 && d2 >= 0.0f && d6 <= 0.0f)
313
{
314
float w = d2 / (d2 - d6);
315
outSet = 0b0101;
316
return a + w * ac; // barycentric coordinates (1-w,0,w)
317
}
318
319
// Check if P in edge region of BC, if so return projection of P onto BC
320
float d4_d3 = d4 - d3;
321
float d5_d6 = d5 - d6;
322
if (d3 * d6 <= d5 * d4 && d4_d3 >= 0.0f && d5_d6 >= 0.0f)
323
{
324
float w = d4_d3 / (d4_d3 + d5_d6);
325
outSet = swap_ac.GetX()? 0b0011 : 0b0110;
326
return inB + w * (c - inB); // barycentric coordinates (0,1-w,w)
327
}
328
329
// P inside face region.
330
// Here we deviate from Christer Ericson's article to improve accuracy.
331
// Determine distance between triangle and origin: distance = (centroid - origin) . normal / |normal|
332
// Closest point to origin is then: distance . normal / |normal|
333
// Note that this way of calculating the closest point is much more accurate than first calculating barycentric coordinates
334
// and then calculating the closest point based on those coordinates.
335
outSet = 0b0111;
336
return n * (a + inB + c).Dot(n) / (3.0f * n_len_sq);
337
}
338
339
/// Check if the origin is outside the plane of triangle (inA, inB, inC). inD specifies the front side of the plane.
340
inline bool OriginOutsideOfPlane(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD)
341
{
342
// Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Tetrahedron to Point)
343
// With p = 0
344
345
// Test if point p and d lie on opposite sides of plane through abc
346
Vec3 n = (inB - inA).Cross(inC - inA);
347
float signp = inA.Dot(n); // [AP AB AC]
348
float signd = (inD - inA).Dot(n); // [AD AB AC]
349
350
// Points on opposite sides if expression signs are the same
351
// Note that we left out the minus sign in signp so we need to check > 0 instead of < 0 as in Christer's book
352
// We compare against a small negative value to allow for a little bit of slop in the calculations
353
return signp * signd > -FLT_EPSILON;
354
}
355
356
/// Returns for each of the planes of the tetrahedron if the origin is inside it
357
/// Roughly equivalent to:
358
/// [OriginOutsideOfPlane(inA, inB, inC, inD),
359
/// OriginOutsideOfPlane(inA, inC, inD, inB),
360
/// OriginOutsideOfPlane(inA, inD, inB, inC),
361
/// OriginOutsideOfPlane(inB, inD, inC, inA)]
362
inline UVec4 OriginOutsideOfTetrahedronPlanes(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD)
363
{
364
Vec3 ab = inB - inA;
365
Vec3 ac = inC - inA;
366
Vec3 ad = inD - inA;
367
Vec3 bd = inD - inB;
368
Vec3 bc = inC - inB;
369
370
Vec3 ab_cross_ac = ab.Cross(ac);
371
Vec3 ac_cross_ad = ac.Cross(ad);
372
Vec3 ad_cross_ab = ad.Cross(ab);
373
Vec3 bd_cross_bc = bd.Cross(bc);
374
375
// For each plane get the side on which the origin is
376
float signp0 = inA.Dot(ab_cross_ac); // ABC
377
float signp1 = inA.Dot(ac_cross_ad); // ACD
378
float signp2 = inA.Dot(ad_cross_ab); // ADB
379
float signp3 = inB.Dot(bd_cross_bc); // BDC
380
Vec4 signp(signp0, signp1, signp2, signp3);
381
382
// For each plane get the side that is outside (determined by the 4th point)
383
float signd0 = ad.Dot(ab_cross_ac); // D
384
float signd1 = ab.Dot(ac_cross_ad); // B
385
float signd2 = ac.Dot(ad_cross_ab); // C
386
float signd3 = -ab.Dot(bd_cross_bc); // A
387
Vec4 signd(signd0, signd1, signd2, signd3);
388
389
// The winding of all triangles has been chosen so that signd should have the
390
// same sign for all components. If this is not the case the tetrahedron
391
// is degenerate and we return that the origin is in front of all sides
392
int sign_bits = signd.GetSignBits();
393
switch (sign_bits)
394
{
395
case 0:
396
// All positive
397
return Vec4::sGreaterOrEqual(signp, Vec4::sReplicate(-FLT_EPSILON));
398
399
case 0xf:
400
// All negative
401
return Vec4::sLessOrEqual(signp, Vec4::sReplicate(FLT_EPSILON));
402
403
default:
404
// Mixed signs, degenerate tetrahedron
405
return UVec4::sReplicate(0xffffffff);
406
}
407
}
408
409
/// Get the closest point between tetrahedron (inA, inB, inC, inD) to the origin
410
/// outSet specifies which feature was closest, 1 = a, 2 = b, 4 = c, 8 = d. Edges have 2 bits set, triangles 3 and if the point is in the interior 4 bits are set.
411
/// If MustIncludeD is true, the function assumes that D is part of the closest feature (vertex, edge, face, tetrahedron) and does less work, if the assumption is not true then a closest point to the other features is returned.
412
template <bool MustIncludeD = false>
413
inline Vec3 GetClosestPointOnTetrahedron(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD, uint32 &outSet)
414
{
415
// Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Tetrahedron to Point)
416
// With p = 0
417
418
// Start out assuming point inside all halfspaces, so closest to itself
419
uint32 closest_set = 0b1111;
420
Vec3 closest_point = Vec3::sZero();
421
float best_dist_sq = FLT_MAX;
422
423
// Determine for each of the faces of the tetrahedron if the origin is in front of the plane
424
UVec4 origin_out_of_planes = OriginOutsideOfTetrahedronPlanes(inA, inB, inC, inD);
425
426
// If point outside face abc then compute closest point on abc
427
if (origin_out_of_planes.GetX()) // OriginOutsideOfPlane(inA, inB, inC, inD)
428
{
429
if constexpr (MustIncludeD)
430
{
431
// If the closest point must include D then ABC cannot be closest but the closest point
432
// cannot be an interior point either so we return A as closest point
433
closest_set = 0b0001;
434
closest_point = inA;
435
}
436
else
437
{
438
// Test the face normally
439
closest_point = GetClosestPointOnTriangle<false>(inA, inB, inC, closest_set);
440
}
441
best_dist_sq = closest_point.LengthSq();
442
}
443
444
// Repeat test for face acd
445
if (origin_out_of_planes.GetY()) // OriginOutsideOfPlane(inA, inC, inD, inB)
446
{
447
uint32 set;
448
Vec3 q = GetClosestPointOnTriangle<MustIncludeD>(inA, inC, inD, set);
449
float dist_sq = q.LengthSq();
450
if (dist_sq < best_dist_sq)
451
{
452
best_dist_sq = dist_sq;
453
closest_point = q;
454
closest_set = (set & 0b0001) + ((set & 0b0110) << 1);
455
}
456
}
457
458
// Repeat test for face adb
459
if (origin_out_of_planes.GetZ()) // OriginOutsideOfPlane(inA, inD, inB, inC)
460
{
461
// Keep original vertex order, it doesn't matter if the triangle is facing inward or outward
462
// and it improves consistency for GJK which will always add a new vertex D and keep the closest
463
// feature from the previous iteration in ABC
464
uint32 set;
465
Vec3 q = GetClosestPointOnTriangle<MustIncludeD>(inA, inB, inD, set);
466
float dist_sq = q.LengthSq();
467
if (dist_sq < best_dist_sq)
468
{
469
best_dist_sq = dist_sq;
470
closest_point = q;
471
closest_set = (set & 0b0011) + ((set & 0b0100) << 1);
472
}
473
}
474
475
// Repeat test for face bdc
476
if (origin_out_of_planes.GetW()) // OriginOutsideOfPlane(inB, inD, inC, inA)
477
{
478
// Keep original vertex order, it doesn't matter if the triangle is facing inward or outward
479
// and it improves consistency for GJK which will always add a new vertex D and keep the closest
480
// feature from the previous iteration in ABC
481
uint32 set;
482
Vec3 q = GetClosestPointOnTriangle<MustIncludeD>(inB, inC, inD, set);
483
float dist_sq = q.LengthSq();
484
if (dist_sq < best_dist_sq)
485
{
486
closest_point = q;
487
closest_set = set << 1;
488
}
489
}
490
491
outSet = closest_set;
492
return closest_point;
493
}
494
};
495
496
JPH_PRECISE_MATH_OFF
497
498
JPH_NAMESPACE_END
499
500