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