@@ -53,6 +53,51 @@ namespace sndx::collision {
5353 size = std::min (uint8_t (size + 1u ), uint8_t (points.size ()));
5454 }
5555
56+ auto pointClosest () const {
57+ return points[0 ].out ;
58+ }
59+
60+ auto lineClosest () const {
61+ auto ab = points[1 ].out - points[0 ].out ;
62+ auto abSqr = glm::dot (ab, ab);
63+ if (abSqr < 0 .0000001f ) { // degenerate case
64+ return points[0 ].out ;
65+ }
66+
67+ auto t = glm::dot (-points[0 ].out , ab) / abSqr;
68+ return points[0 ].out + glm::clamp (t, 0 .0f , 1 .0f ) * points[1 ].out ;
69+ }
70+
71+ auto triangleClosest () const {
72+ // degenerate cases
73+ if (points[0 ].out == points[1 ].out ) {
74+ SimplexGJK tmp{};
75+ tmp.push_front (points[0 ]);
76+ tmp.push_front (points[2 ]);
77+ return tmp.lineClosest ();
78+ }
79+ else if (points[0 ].out == points[2 ].out ) {
80+ return lineClosest ();
81+ }
82+ else if (points[1 ].out == points[2 ].out ) {
83+ return lineClosest ();
84+ }
85+
86+ sndx::collision::Tri<Vec> tri{ points[0 ].out , points[1 ].out , points[2 ].out };
87+ return tri.closestPoint (glm::vec3{ 0 .0f });
88+ }
89+
90+ [[nodiscard]]
91+ Vec gjkClosest () const {
92+ switch (size) {
93+ case 1 : return pointClosest ();
94+ case 2 : return lineClosest ();
95+ case 3 : return triangleClosest ();
96+ default :
97+ throw std::logic_error (" GJK had weird number of points in simplex" );
98+ }
99+ }
100+
56101 bool lineOrigin (Vec& newDirection) {
57102 auto ab = points[1 ].out - points[0 ].out ;
58103 auto ao = -points[0 ].out ;
@@ -203,6 +248,135 @@ namespace sndx::collision {
203248 return std::nullopt ;
204249 }
205250
251+ struct ResDistGJK {
252+ bool hit = false ; // hit IS NOT TO BE TRUSTED
253+ glm::vec3 a{}, b{};
254+ };
255+
256+ // if the two shapes ARE colliding this has a high chance of failure.
257+ // use gjk to ensure no collision before using this.
258+ template <class SFnA , class SFnB > [[nodiscard]]
259+ ResDistGJK gjkDist (const SFnA& supportA, const SFnB& supportB) {
260+ auto support = detail::gjkMinkowski (supportA, supportB, glm::vec3 (1.0 , 0.0 , 0.0 ));
261+
262+ // force a triangle so we don't have to deal with lines or points later
263+ SimplexGJK simplex{};
264+ simplex.push_front (support);
265+ simplex.push_front (detail::gjkMinkowski (supportA, supportB, -support.out ));
266+
267+ auto dir = -simplex.gjkClosest ();
268+ if (glm::length (dir) <= 0 .000001f ) { // we hit the origin!
269+ return ResDistGJK{ true };
270+ }
271+ simplex.push_front (detail::gjkMinkowski (supportA, supportB, dir));
272+
273+ size_t iterations = 0 ;
274+ while (true ) {
275+ dir = -simplex.gjkClosest ();
276+ auto dirMag = glm::length (dir);
277+ if (dirMag <= 0 .000001f ) { // we hit the origin!
278+ return ResDistGJK{ true };
279+ }
280+
281+ support = detail::gjkMinkowski (supportA, supportB, dir);
282+
283+ auto alignment = glm::dot (support.out , dir);
284+ auto old = glm::dot (simplex.points [0 ].out , dir);
285+
286+ // check making progress
287+ if (alignment - old < 0 .000001f ) {
288+ break ;
289+ }
290+
291+ auto newAB = simplex;
292+ newAB.points [2 ] = support;
293+
294+ auto newAC = simplex;
295+ newAC.points [1 ] = support;
296+
297+ auto newBC = simplex;
298+ newBC.points [0 ] = support;
299+
300+ auto pAB = newAB.gjkClosest ();
301+ auto pAC = newAC.gjkClosest ();
302+ auto pBC = newBC.gjkClosest ();
303+
304+ auto abLen = glm::length (pAB);
305+ auto acLen = glm::length (pAC);
306+ auto bcLen = glm::length (pBC);
307+
308+ if (abLen < acLen) {
309+ if (abLen < bcLen) {
310+ simplex = newAB;
311+ dir = -pAB;
312+ }
313+ else {
314+ simplex = newBC;
315+ dir = -pBC;
316+ }
317+ }
318+ else {
319+ if (acLen < bcLen) {
320+ simplex = newAC;
321+ dir = -pAC;
322+ }
323+ else {
324+ simplex = newBC;
325+ dir = -pBC;
326+ }
327+ }
328+
329+ ++iterations;
330+ }
331+
332+ ResDistGJK out{ false };
333+
334+ // degenerate cases
335+ if (simplex.points [0 ].out == simplex.points [1 ].out ) { // A == B
336+ if (simplex.points [0 ].out == simplex.points [2 ].out ) { // point
337+ out.a = simplex.points [0 ].a ;
338+ out.b = simplex.points [0 ].b ;
339+ return out;
340+ }
341+
342+ // line AC
343+ auto ac = simplex.points [2 ].out - simplex.points [0 ].out ;
344+ auto t = glm::dot (-simplex.points [0 ].out , ac) / glm::dot (ac, ac);
345+ t = glm::clamp (t, 0 .0f , 1 .0f );
346+ out.a = simplex.points [0 ].a + t * simplex.points [2 ].a ;
347+ out.b = simplex.points [0 ].b + t * simplex.points [2 ].b ;
348+ return out;
349+ }
350+ else if (simplex.points [0 ].out == simplex.points [2 ].out ) { // A == C
351+ // line BC
352+ auto bc = simplex.points [2 ].out - simplex.points [1 ].out ;
353+ auto t = glm::dot (-simplex.points [1 ].out , bc) / glm::dot (bc, bc);
354+ t = glm::clamp (t, 0 .0f , 1 .0f );
355+ out.a = simplex.points [1 ].a + t * simplex.points [2 ].a ;
356+ out.b = simplex.points [1 ].b + t * simplex.points [2 ].b ;
357+ return out;
358+ }
359+ else if (simplex.points [1 ].out == simplex.points [2 ].out ) { // B == C
360+ // line AB
361+ auto ab = simplex.points [1 ].out - simplex.points [0 ].out ;
362+ auto t = glm::dot (-simplex.points [0 ].out , ab) / glm::dot (ab, ab);
363+ t = glm::clamp (t, 0 .0f , 1 .0f );
364+ out.a = simplex.points [0 ].a + t * simplex.points [1 ].a ;
365+ out.b = simplex.points [0 ].b + t * simplex.points [1 ].b ;
366+ return out;
367+ }
368+
369+ sndx::collision::Tri3D tri{ simplex.points [0 ].out , simplex.points [1 ].out , simplex.points [2 ].out };
370+ sndx::collision::Tri3D triA{ simplex.points [0 ].a , simplex.points [1 ].a , simplex.points [2 ].a };
371+ sndx::collision::Tri3D triB{ simplex.points [0 ].b , simplex.points [1 ].b , simplex.points [2 ].b };
372+
373+ auto uvw = tri.uvw (glm::vec3{ 0 .0f });
374+ out.a = triA.fromUVW (uvw);
375+ out.b = triB.fromUVW (uvw);
376+
377+ return out;
378+ }
379+
206380 struct EpaResult {
207381 glm::vec3 normal;
208382 float depth;
0 commit comments