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:367
std::uint32_t uint32
Definition: Core.h:442
#define JPH_NAMESPACE_BEGIN
Definition: Core.h:361
constexpr T Clamp(T inV, T inMin, T inMax)
Clamp a value between two values.
Definition: Math.h:45
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:16
JPH_INLINE float Dot(Vec3Arg inV2) const
Dot product.
Definition: Vec3.inl:637
JPH_INLINE Vec3 Cross(Vec3Arg inV2) const
Cross product.
Definition: Vec3.inl:582
JPH_INLINE Vec4 DotV4(Vec3Arg inV2) const
Dot product, returns the dot product in X, Y, Z and W components.
Definition: Vec3.inl:621
static JPH_INLINE Vec3 sSelect(Vec3Arg inV1, Vec3Arg inV2, UVec4Arg inControl)
Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of in...
Definition: Vec3.inl:269
JPH_INLINE float LengthSq() const
Squared length of vector.
Definition: Vec3.inl:653
static JPH_INLINE Vec3 sZero()
Vector with all zeros.
Definition: Vec3.inl:107
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:741
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