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