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