Jolt Physics
A multi core friendly Game Physics Engine
Loading...
Searching...
No Matches
ClosestPoint.h
Go to the documentation of this file.
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
8
9// Turn off fused multiply add instruction because it makes the equations of the form a * b - c * d inaccurate below
10JPH_PRECISE_MATH_ON
11
13namespace ClosestPoint
14{
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
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
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
159 template <bool MustIncludeC = false>
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
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
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
412 template <bool MustIncludeD = false>
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
496JPH_PRECISE_MATH_OFF
497
#define JPH_NAMESPACE_END
Definition Core.h:379
std::uint32_t uint32
Definition Core.h:449
#define JPH_NAMESPACE_BEGIN
Definition Core.h:373
JPH_INLINE constexpr T Clamp(T inV, T inMin, T inMax)
Clamp a value between two values.
Definition Math.h:45
JPH_INLINE constexpr T Square(T inV)
Square a value.
Definition Math.h:52
Definition UVec4.h:12
JPH_INLINE uint32 GetZ() const
Definition UVec4.h:104
JPH_INLINE uint32 GetY() const
Definition UVec4.h:103
static JPH_INLINE UVec4 sReplicate(uint32 inV)
Replicate int inV across all components.
Definition UVec4.inl:56
JPH_INLINE uint32 GetW() const
Definition UVec4.h:105
JPH_INLINE uint32 GetX() const
Get individual components.
Definition UVec4.h:102
Definition Vec3.h:17
JPH_INLINE float Dot(Vec3Arg inV2) const
Dot product.
Definition Vec3.inl:649
JPH_INLINE Vec3 Cross(Vec3Arg inV2) const
Cross product.
Definition Vec3.inl:594
JPH_INLINE Vec4 DotV4(Vec3Arg inV2) const
Dot product, returns the dot product in X, Y, Z and W components.
Definition Vec3.inl:633
JPH_INLINE float LengthSq() const
Squared length of vector.
Definition Vec3.inl:665
static JPH_INLINE Vec3 sZero()
Vector with all zeros.
Definition Vec3.inl:107
static JPH_INLINE Vec3 sSelect(Vec3Arg inNotSet, Vec3Arg inSet, UVec4Arg inControl)
Component wise select, returns inNotSet when highest bit of inControl = 0 and inSet when highest bit ...
Definition Vec3.inl:269
Definition Vec4.h:14
static JPH_INLINE UVec4 sLessOrEqual(Vec4Arg inV1, Vec4Arg inV2)
Less than or equal (component wise)
Definition Vec4.inl:194
static JPH_INLINE UVec4 sLess(Vec4Arg inV1, Vec4Arg inV2)
Less than (component wise)
Definition Vec4.inl:180
static JPH_INLINE UVec4 sGreaterOrEqual(Vec4Arg inV1, Vec4Arg inV2)
Greater than or equal (component wise)
Definition Vec4.inl:222
JPH_INLINE int GetSignBits() const
Store if X is negative in bit 0, Y in bit 1, Z in bit 2 and W in bit 3.
Definition Vec4.inl:752
static JPH_INLINE Vec4 sReplicate(float inV)
Replicate inV across all components.
Definition Vec4.inl:74
Helper utils to find the closest point to a line segment, triangle or tetrahedron.
Definition ClosestPoint.h:14
bool GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, float &outU, float &outV)
Definition ClosestPoint.h:18
Vec3 GetClosestPointOnTriangle(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, uint32 &outSet)
Definition ClosestPoint.h:160
UVec4 OriginOutsideOfTetrahedronPlanes(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD)
Definition ClosestPoint.h:362
Vec3 GetClosestPointOnTetrahedron(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD, uint32 &outSet)
Definition ClosestPoint.h:413
bool OriginOutsideOfPlane(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD)
Check if the origin is outside the plane of triangle (inA, inB, inC). inD specifies the front side of...
Definition ClosestPoint.h:340
Vec3 GetClosestPointOnLine(Vec3Arg inA, Vec3Arg inB, uint32 &outSet)
Definition ClosestPoint.h:132