GJKClosestPoint.h 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Jolt/Core/NonCopyable.h>
  5. #include <Jolt/Core/FPException.h>
  6. #include <Jolt/Geometry/ClosestPoint.h>
  7. #include <Jolt/Geometry/ConvexSupport.h>
  8. //#define JPH_GJK_DEBUG
  9. #ifdef JPH_GJK_DEBUG
  10. #include <Jolt/Core/StringTools.h>
  11. #include <Jolt/Renderer/DebugRenderer.h>
  12. #endif
  13. namespace JPH {
  14. /// Convex vs convex collision detection
  15. /// Based on: A Fast and Robust GJK Implementation for Collision Detection of Convex Objects - Gino van den Bergen
  16. class GJKClosestPoint : public NonCopyable
  17. {
  18. private:
  19. /// Get new closest point to origin given simplex mY of mNumPoints points
  20. ///
  21. /// @param inPrevVLenSq Length of |outV|^2 from the previous iteration, used as a maximum value when selecting a new closest point.
  22. /// @param outV Closest point
  23. /// @param outVLenSq |outV|^2
  24. /// @param outSet Set of points that form the new simplex closest to the origin (bit 1 = mY[0], bit 2 = mY[1], ...)
  25. ///
  26. /// @return True if new closest point was found.
  27. /// False if the function failed, in this case the output variables are not modified
  28. bool GetClosest(float inPrevVLenSq, Vec3 &outV, float &outVLenSq, uint32 &outSet) const
  29. {
  30. #ifdef JPH_GJK_DEBUG
  31. for (int i = 0; i < mNumPoints; ++i)
  32. Trace("y[%d] = [%s], |y[%d]| = %g", i, ConvertToString(mY[i]).c_str(), i, mY[i].Length());
  33. #endif
  34. uint32 set;
  35. Vec3 v;
  36. switch (mNumPoints)
  37. {
  38. case 1:
  39. // Single point
  40. set = 0b0001;
  41. v = mY[0];
  42. break;
  43. case 2:
  44. // Line segment
  45. v = ClosestPoint::GetClosestPointOnLine(mY[0], mY[1], set);
  46. break;
  47. case 3:
  48. // Triangle
  49. v = ClosestPoint::GetClosestPointOnTriangle(mY[0], mY[1], mY[2], set);
  50. break;
  51. case 4:
  52. // Tetrahedron
  53. v = ClosestPoint::GetClosestPointOnTetrahedron(mY[0], mY[1], mY[2], mY[3], set);
  54. break;
  55. default:
  56. JPH_ASSERT(false);
  57. return false;
  58. }
  59. #ifdef JPH_GJK_DEBUG
  60. Trace("GetClosest: set = 0b%s, v = [%s], |v| = %g", NibbleToBinary(set), ConvertToString(v).c_str(), v.Length());
  61. #endif
  62. float v_len_sq = v.LengthSq();
  63. if (v_len_sq >= inPrevVLenSq)
  64. {
  65. // No better match found
  66. #ifdef JPH_GJK_DEBUG
  67. Trace("New closer point is further away, failed to converge");
  68. #endif
  69. return false;
  70. }
  71. // Return closest point
  72. outV = v;
  73. outVLenSq = v_len_sq;
  74. outSet = set;
  75. return true;
  76. }
  77. // Get max(|Y_0|^2 .. |Y_n|^2)
  78. float GetMaxYLengthSq() const
  79. {
  80. float y_len_sq = mY[0].LengthSq();
  81. for (int i = 1; i < mNumPoints; ++i)
  82. y_len_sq = max(y_len_sq, mY[i].LengthSq());
  83. return y_len_sq;
  84. }
  85. // Remove points that are not in the set, only updates mY
  86. void UpdatePointSetY(uint32 inSet)
  87. {
  88. int num_points = 0;
  89. for (int i = 0; i < mNumPoints; ++i)
  90. if ((inSet & (1 << i)) != 0)
  91. {
  92. mY[num_points] = mY[i];
  93. ++num_points;
  94. }
  95. mNumPoints = num_points;
  96. }
  97. // Remove points that are not in the set, only updates mP
  98. void UpdatePointSetP(uint32 inSet)
  99. {
  100. int num_points = 0;
  101. for (int i = 0; i < mNumPoints; ++i)
  102. if ((inSet & (1 << i)) != 0)
  103. {
  104. mP[num_points] = mP[i];
  105. ++num_points;
  106. }
  107. mNumPoints = num_points;
  108. }
  109. // Remove points that are not in the set, only updates mP and mQ
  110. void UpdatePointSetPQ(uint32 inSet)
  111. {
  112. int num_points = 0;
  113. for (int i = 0; i < mNumPoints; ++i)
  114. if ((inSet & (1 << i)) != 0)
  115. {
  116. mP[num_points] = mP[i];
  117. mQ[num_points] = mQ[i];
  118. ++num_points;
  119. }
  120. mNumPoints = num_points;
  121. }
  122. // Remove points that are not in the set, updates mY, mP and mQ
  123. void UpdatePointSetYPQ(uint32 inSet)
  124. {
  125. int num_points = 0;
  126. for (int i = 0; i < mNumPoints; ++i)
  127. if ((inSet & (1 << i)) != 0)
  128. {
  129. mY[num_points] = mY[i];
  130. mP[num_points] = mP[i];
  131. mQ[num_points] = mQ[i];
  132. ++num_points;
  133. }
  134. mNumPoints = num_points;
  135. }
  136. // Calculate closest points on A and B
  137. void CalculatePointAAndB(Vec3 &outPointA, Vec3 &outPointB)
  138. {
  139. switch (mNumPoints)
  140. {
  141. case 1:
  142. outPointA = mP[0];
  143. outPointB = mQ[0];
  144. break;
  145. case 2:
  146. {
  147. float u, v;
  148. ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], u, v);
  149. outPointA = u * mP[0] + v * mP[1];
  150. outPointB = u * mQ[0] + v * mQ[1];
  151. }
  152. break;
  153. case 3:
  154. {
  155. float u, v, w;
  156. ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], mY[2], u, v, w);
  157. outPointA = u * mP[0] + v * mP[1] + w * mP[2];
  158. outPointB = u * mQ[0] + v * mQ[1] + w * mQ[2];
  159. }
  160. break;
  161. case 4:
  162. #ifdef _DEBUG
  163. memset(&outPointA, 0xcd, sizeof(outPointA));
  164. memset(&outPointB, 0xcd, sizeof(outPointB));
  165. #endif
  166. break;
  167. }
  168. }
  169. public:
  170. /// Test if inA and inB intersect
  171. ///
  172. /// @param inA The convex object A, must support the GetSupport(Vec3) function.
  173. /// @param inB The convex object B, must support the GetSupport(Vec3) function.
  174. /// @param inTolerance Minimal distance between objects when the objects are considered to be colliding
  175. /// @param ioV is used as initial separating axis (provide a zero vector if you don't know yet)
  176. ///
  177. /// @return True if they intersect (in which case ioV = (0, 0, 0)).
  178. /// False if they don't intersect in which case ioV is a separating axis in the direction from A to B (magnitude is meaningless)
  179. template <typename A, typename B>
  180. bool Intersects(const A &inA, const B &inB, float inTolerance, Vec3 &ioV)
  181. {
  182. float tolerance_sq = Square(inTolerance);
  183. // Reset state
  184. mNumPoints = 0;
  185. #ifdef JPH_GJK_DEBUG
  186. for (int i = 0; i < 4; ++i)
  187. mY[i] = Vec3::sZero();
  188. #endif
  189. // Previous length^2 of v
  190. float prev_v_len_sq = FLT_MAX;
  191. for (;;)
  192. {
  193. #ifdef JPH_GJK_DEBUG
  194. Trace("v = [%s], num_points = %d", ConvertToString(ioV).c_str(), mNumPoints);
  195. #endif
  196. // Get support points for shape A and B in the direction of v
  197. Vec3 p = inA.GetSupport(ioV);
  198. Vec3 q = inB.GetSupport(-ioV);
  199. // Get support point of the minkowski sum A - B of v
  200. Vec3 w = p - q;
  201. // If the support point sA-B(v) is in the opposite direction as v, then we have found a separating axis and there is no intersection
  202. if (ioV.Dot(w) < 0.0f)
  203. {
  204. // Separating axis found
  205. #ifdef JPH_GJK_DEBUG
  206. Trace("Seperating axis");
  207. #endif
  208. return false;
  209. }
  210. // Store the point for later use
  211. mY[mNumPoints] = w;
  212. ++mNumPoints;
  213. #ifdef JPH_GJK_DEBUG
  214. Trace("w = [%s]", ConvertToString(w).c_str());
  215. #endif
  216. // Determine the new closest point
  217. float v_len_sq; // Length^2 of v
  218. uint32 set; // Set of points that form the new simplex
  219. if (!GetClosest(prev_v_len_sq, ioV, v_len_sq, set))
  220. return false;
  221. // If there are 4 points, the origin is inside the tetrahedron and we're done
  222. if (set == 0xf)
  223. {
  224. #ifdef JPH_GJK_DEBUG
  225. Trace("Full simplex");
  226. #endif
  227. ioV = Vec3::sZero();
  228. return true;
  229. }
  230. // If v is very close to zero, we consider this a collision
  231. if (v_len_sq <= tolerance_sq)
  232. {
  233. #ifdef JPH_GJK_DEBUG
  234. Trace("Distance zero");
  235. #endif
  236. ioV = Vec3::sZero();
  237. return true;
  238. }
  239. // If v is very small compared to the length of y, we also consider this a collision
  240. if (v_len_sq <= FLT_EPSILON * GetMaxYLengthSq())
  241. {
  242. #ifdef JPH_GJK_DEBUG
  243. Trace("Machine precision reached");
  244. #endif
  245. ioV = Vec3::sZero();
  246. return true;
  247. }
  248. // The next seperation axis to test is the negative of the closest point of the Minkowski sum to the origin
  249. // Note: This must be done before terminating as converged since the separating axis is -v
  250. ioV = -ioV;
  251. // If the squared length of v is not changing enough, we've converged and there is no collision
  252. JPH_ASSERT(prev_v_len_sq >= v_len_sq);
  253. if (prev_v_len_sq - v_len_sq <= FLT_EPSILON * prev_v_len_sq)
  254. {
  255. // v is a separating axis
  256. #ifdef JPH_GJK_DEBUG
  257. Trace("Converged");
  258. #endif
  259. return false;
  260. }
  261. prev_v_len_sq = v_len_sq;
  262. // Update the points of the simplex
  263. UpdatePointSetY(set);
  264. }
  265. }
  266. /// Get closest points between inA and inB
  267. ///
  268. /// @param inA The convex object A, must support the GetSupport(Vec3) function.
  269. /// @param inB The convex object B, must support the GetSupport(Vec3) function.
  270. /// @param inTolerance The minimal distance between A and B before the objects are considered colliding and processing is terminated.
  271. /// @param inMaxDistSq The maximum squared distance between A and B before the objects are considered infinitely far away and processing is terminated.
  272. /// @param ioV Initial guess for the separating axis. Start with any non-zero vector if you don't know.
  273. /// If return value is 0, ioV = (0, 0, 0).
  274. /// If the return value is bigger than 0 but smaller than FLT_MAX, ioV will be the separating axis in the direction from A to B and its length the squared distance between A and B.
  275. /// If the return value is FLT_MAX, ioV will be the separating axis in the direction from A to B and the magnitude of the vector is meaningless.
  276. /// @param outPointA , outPointB
  277. /// If the return value is 0 the points are invalid.
  278. /// If the return value is bigger than 0 but smaller than FLT_MAX these will contain the closest point on A and B.
  279. /// If the return value is FLT_MAX the points are invalid.
  280. ///
  281. /// @return The squared distance between A and B or FLT_MAX when they are further away than inMaxDistSq.
  282. template <typename A, typename B>
  283. float GetClosestPoints(const A &inA, const B &inB, float inTolerance, float inMaxDistSq, Vec3 &ioV, Vec3 &outPointA, Vec3 &outPointB)
  284. {
  285. float tolerance_sq = Square(inTolerance);
  286. // Reset state
  287. mNumPoints = 0;
  288. #ifdef JPH_GJK_DEBUG
  289. // Generate the hull of the Minkowski difference for visualization
  290. MinkowskiDifference<A, B> diff(inA, inB);
  291. mGeometry = DebugRenderer::sInstance->CreateTriangleGeometryForConvex([&diff](Vec3Arg inDirection) { return diff.GetSupport(inDirection); });
  292. for (int i = 0; i < 4; ++i)
  293. {
  294. mY[i] = Vec3::sZero();
  295. mP[i] = Vec3::sZero();
  296. mQ[i] = Vec3::sZero();
  297. }
  298. #endif
  299. // Length^2 of v
  300. float v_len_sq = ioV.LengthSq();
  301. // Previous length^2 of v
  302. float prev_v_len_sq = FLT_MAX;
  303. for (;;)
  304. {
  305. #ifdef JPH_GJK_DEBUG
  306. Trace("v = [%s], num_points = %d", ConvertToString(ioV).c_str(), mNumPoints);
  307. #endif
  308. // Get support points for shape A and B in the direction of v
  309. Vec3 p = inA.GetSupport(ioV);
  310. Vec3 q = inB.GetSupport(-ioV);
  311. // Get support point of the minkowski sum A - B of v
  312. Vec3 w = p - q;
  313. float dot = ioV.Dot(w);
  314. #ifdef JPH_GJK_DEBUG
  315. // Draw -ioV to show the closest point to the origin from the previous simplex
  316. DebugRenderer::sInstance->DrawArrow(mOffset, mOffset - ioV, Color::sOrange, 0.05f);
  317. // Draw ioV to show where we're probing next
  318. DebugRenderer::sInstance->DrawArrow(mOffset, mOffset + ioV, Color::sCyan, 0.05f);
  319. // Draw w, the support point
  320. DebugRenderer::sInstance->DrawArrow(mOffset, mOffset + w, Color::sGreen, 0.05f);
  321. DebugRenderer::sInstance->DrawMarker(mOffset + w, Color::sGreen, 1.0f);
  322. // Draw the simplex and the Minkowski difference around it
  323. DrawState();
  324. #endif
  325. // Test if we have a separation of more than inMaxDistSq, in which case we terminate early
  326. if (dot < 0.0f && dot * dot > v_len_sq * inMaxDistSq)
  327. {
  328. #ifdef JPH_GJK_DEBUG
  329. Trace("Distance bigger than max");
  330. #endif
  331. #ifdef _DEBUG
  332. memset(&outPointA, 0xcd, sizeof(outPointA));
  333. memset(&outPointB, 0xcd, sizeof(outPointB));
  334. #endif
  335. return FLT_MAX;
  336. }
  337. // Store the point for later use
  338. mY[mNumPoints] = w;
  339. mP[mNumPoints] = p;
  340. mQ[mNumPoints] = q;
  341. ++mNumPoints;
  342. #ifdef JPH_GJK_DEBUG
  343. Trace("w = [%s]", ConvertToString(w).c_str());
  344. #endif
  345. uint32 set;
  346. if (!GetClosest(prev_v_len_sq, ioV, v_len_sq, set))
  347. {
  348. --mNumPoints; // Undo add last point
  349. break;
  350. }
  351. // If there are 4 points, the origin is inside the tetrahedron and we're done
  352. if (set == 0xf)
  353. {
  354. #ifdef JPH_GJK_DEBUG
  355. Trace("Full simplex");
  356. #endif
  357. ioV = Vec3::sZero();
  358. v_len_sq = 0.0f;
  359. break;
  360. }
  361. // Update the points of the simplex
  362. UpdatePointSetYPQ(set);
  363. // If v is very close to zero, we consider this a collision
  364. if (v_len_sq <= tolerance_sq)
  365. {
  366. #ifdef JPH_GJK_DEBUG
  367. Trace("Distance zero");
  368. #endif
  369. ioV = Vec3::sZero();
  370. v_len_sq = 0.0f;
  371. break;
  372. }
  373. // If v is very small compared to the length of y, we also consider this a collision
  374. #ifdef JPH_GJK_DEBUG
  375. Trace("Check v small compared to y: %g <= %g", v_len_sq, FLT_EPSILON * GetMaxYLengthSq());
  376. #endif
  377. if (v_len_sq <= FLT_EPSILON * GetMaxYLengthSq())
  378. {
  379. #ifdef JPH_GJK_DEBUG
  380. Trace("Machine precision reached");
  381. #endif
  382. ioV = Vec3::sZero();
  383. v_len_sq = 0.0f;
  384. break;
  385. }
  386. // The next seperation axis to test is the negative of the closest point of the Minkowski sum to the origin
  387. // Note: This must be done before terminating as converged since the separating axis is -v
  388. ioV = -ioV;
  389. // If the squared length of v is not changing enough, we've converged and there is no collision
  390. #ifdef JPH_GJK_DEBUG
  391. Trace("Check v not changing enough: %g <= %g", prev_v_len_sq - v_len_sq, FLT_EPSILON * prev_v_len_sq);
  392. #endif
  393. JPH_ASSERT(prev_v_len_sq >= v_len_sq);
  394. if (prev_v_len_sq - v_len_sq <= FLT_EPSILON * prev_v_len_sq)
  395. {
  396. // v is a separating axis
  397. #ifdef JPH_GJK_DEBUG
  398. Trace("Converged");
  399. #endif
  400. break;
  401. }
  402. prev_v_len_sq = v_len_sq;
  403. }
  404. // Get the closest points
  405. CalculatePointAAndB(outPointA, outPointB);
  406. #ifdef JPH_GJK_DEBUG
  407. Trace("Return: v = [%s], |v| = %g", ConvertToString(ioV).c_str(), ioV.Length());
  408. // Draw -ioV to show the closest point to the origin from the previous simplex
  409. DebugRenderer::sInstance->DrawArrow(mOffset, mOffset - ioV, Color::sOrange, 0.05f);
  410. // Draw the closest points
  411. DebugRenderer::sInstance->DrawMarker(mOffset + outPointA, Color::sGreen, 1.0f);
  412. DebugRenderer::sInstance->DrawMarker(mOffset + outPointB, Color::sPurple, 1.0f);
  413. // Draw the simplex and the Minkowski difference around it
  414. DrawState();
  415. #endif
  416. JPH_ASSERT(ioV.LengthSq() == v_len_sq);
  417. return v_len_sq;
  418. }
  419. /// Get the resulting simplex after the GetClosestPoints algorithm finishes.
  420. /// If it returned a squared distance of 0, the origin will be contained in the simplex.
  421. void GetClosestPointsSimplex(Vec3 *outY, Vec3 *outP, Vec3 *outQ, uint &outNumPoints)
  422. {
  423. uint size = sizeof(Vec3) * mNumPoints;
  424. memcpy(outY, mY, size);
  425. memcpy(outP, mP, size);
  426. memcpy(outQ, mQ, size);
  427. outNumPoints = mNumPoints;
  428. }
  429. /// Test if a ray inRayOrigin + lambda * inRayDirection for lambda e [0, ioLambda> instersects inA
  430. ///
  431. /// Code based upon: Ray Casting against General Convex Objects with Application to Continuous Collision Detection - Gino van den Bergen
  432. ///
  433. /// @param inRayOrigin Origin of the ray
  434. /// @param inRayDirection Direction of the ray (ioLambda * inDirection determines length)
  435. /// @param inTolerance The minimal distance between the ray and A before it is considered colliding
  436. /// @param inA A convex object that has the GetSupport(Vec3) function
  437. /// @param ioLambda The max fraction along the ray, on output updated with the actual collision fraction.
  438. ///
  439. /// @return true if a hit was found, ioLambda is the solution for lambda.
  440. template <typename A>
  441. bool CastRay(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inTolerance, const A &inA, float &ioLambda)
  442. {
  443. float tolerance_sq = Square(inTolerance);
  444. // Reset state
  445. mNumPoints = 0;
  446. float lambda = 0.0f;
  447. Vec3 x = inRayOrigin;
  448. Vec3 v = x - inA.GetSupport(Vec3::sZero());
  449. float v_len_sq = FLT_MAX;
  450. bool allow_restart = false;
  451. for (;;)
  452. {
  453. #ifdef JPH_GJK_DEBUG
  454. Trace("v = [%s], num_points = %d", ConvertToString(v).c_str(), mNumPoints);
  455. #endif
  456. // Get new support point
  457. Vec3 p = inA.GetSupport(v);
  458. Vec3 w = x - p;
  459. #ifdef JPH_GJK_DEBUG
  460. Trace("w = [%s]", ConvertToString(w).c_str());
  461. #endif
  462. float v_dot_w = v.Dot(w);
  463. #ifdef JPH_GJK_DEBUG
  464. Trace("v . w = %g", v_dot_w);
  465. #endif
  466. if (v_dot_w > 0.0f)
  467. {
  468. // If ray and normal are in the same direction, we've passed A and there's no collision
  469. float v_dot_r = v.Dot(inRayDirection);
  470. #ifdef JPH_GJK_DEBUG
  471. Trace("v . r = %g", v_dot_r);
  472. #endif
  473. if (v_dot_r >= 0.0f)
  474. return false;
  475. // Update the lower bound for lambda
  476. float delta = v_dot_w / v_dot_r;
  477. float old_lambda = lambda;
  478. lambda -= delta;
  479. #ifdef JPH_GJK_DEBUG
  480. Trace("lambda = %g, delta = %g", lambda, delta);
  481. #endif
  482. // If lambda didn't change, we cannot converge any further and we assume a hit
  483. if (old_lambda == lambda)
  484. break;
  485. // If lambda is bigger or equal than max, we don't have a hit
  486. if (lambda >= ioLambda)
  487. return false;
  488. // Update x to new closest point on the ray
  489. x = inRayOrigin + lambda * inRayDirection;
  490. // We've shifted x, so reset v_len_sq so that it is not used as early out for GetClosest
  491. v_len_sq = FLT_MAX;
  492. // We allow rebuilding the simplex once after x changes because the simplex was built
  493. // for another x and numerical round off builds up as you keep adding points to an
  494. // existing simplex
  495. allow_restart = true;
  496. }
  497. // Add p to set P: P = P U {p}
  498. mP[mNumPoints] = p;
  499. ++mNumPoints;
  500. // Calculate Y = {x} - P
  501. for (int i = 0; i < mNumPoints; ++i)
  502. mY[i] = x - mP[i];
  503. // Determine the new closest point from Y to origin
  504. bool needs_restart = false;
  505. uint32 set; // Set of points that form the new simplex
  506. if (!GetClosest(v_len_sq, v, v_len_sq, set))
  507. {
  508. #ifdef JPH_GJK_DEBUG
  509. Trace("Failed to converge");
  510. #endif
  511. // We failed to converge, restart
  512. needs_restart = true;
  513. }
  514. else if (set == 0xf)
  515. {
  516. #ifdef JPH_GJK_DEBUG
  517. Trace("Full simplex");
  518. #endif
  519. // If there are 4 points, x is inside the tetrahedron and we've found a hit
  520. // Double check if this is indeed the case
  521. if (v_len_sq <= tolerance_sq)
  522. break;
  523. // We failed to converge, restart
  524. needs_restart = true;
  525. }
  526. if (needs_restart)
  527. {
  528. // Only allow 1 restart, if we still can't get a closest point
  529. // we're so close that we return this as a hit
  530. if (!allow_restart)
  531. break;
  532. // If we fail to converge, we start again with the last point as simplex
  533. #ifdef JPH_GJK_DEBUG
  534. Trace("Restarting");
  535. #endif
  536. allow_restart = false;
  537. mP[0] = p;
  538. mNumPoints = 1;
  539. v = x - p;
  540. v_len_sq = FLT_MAX;
  541. continue;
  542. }
  543. // Update the points P to form the new simplex
  544. // Note: We're not updating Y as Y will shift with x so we have to calculate it every iteration
  545. UpdatePointSetP(set);
  546. // Check if x is close enough to inA
  547. if (v_len_sq <= tolerance_sq)
  548. {
  549. #ifdef JPH_GJK_DEBUG
  550. Trace("Converged");
  551. #endif
  552. break;
  553. }
  554. }
  555. // Store hit fraction
  556. ioLambda = lambda;
  557. return true;
  558. }
  559. /// Test if a cast shape inA moving from inStart to lambda * inStart.GetTranslation() + inDirection where lambda e [0, ioLambda> instersects inB
  560. ///
  561. /// @param inStart Start position and orientation of the convex object
  562. /// @param inDirection Direction of the sweep (ioLambda * inDirection determines length)
  563. /// @param inTolerance The minimal distance between A and B before they are considered colliding
  564. /// @param inA The convex object A, must support the GetSupport(Vec3) function.
  565. /// @param inB The convex object B, must support the GetSupport(Vec3) function.
  566. /// @param ioLambda The max fraction along the sweep, on output updated with the actual collision fraction.
  567. ///
  568. /// @return true if a hit was found, ioLambda is the solution for lambda.
  569. template <typename A, typename B>
  570. bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inTolerance, const A &inA, const B &inB, float &ioLambda)
  571. {
  572. // Transform the shape to be cast to the starting position
  573. using TransformedA = TransformedConvexObject<A>;
  574. TransformedA transformed_a(inStart, inA);
  575. // Calculate the minkowski difference inB - inA
  576. // inA is moving, so we need to add the back side of inB to the front side of inA
  577. MinkowskiDifference<B, TransformedA> difference(inB, transformed_a);
  578. // Do a raycast against the Minkowski difference
  579. return CastRay(Vec3::sZero(), inDirection, inTolerance, difference, ioLambda);
  580. }
  581. /// Test if a cast shape inA moving from inStart to lambda * inStart.GetTranslation() + inDirection where lambda e [0, ioLambda> instersects inB
  582. ///
  583. /// @param inStart Start position and orientation of the convex object
  584. /// @param inDirection Direction of the sweep (ioLambda * inDirection determines length)
  585. /// @param inTolerance The minimal distance between A and B before they are considered colliding
  586. /// @param inA The convex object A, must support the GetSupport(Vec3) function.
  587. /// @param inB The convex object B, must support the GetSupport(Vec3) function.
  588. /// @param inConvexRadiusA The convex radius of A, this will be added on all sides to pad A.
  589. /// @param inConvexRadiusB The convex radius of B, this will be added on all sides to pad B.
  590. /// @param ioLambda The max fraction along the sweep, on output updated with the actual collision fraction.
  591. /// @param outPointA is the contact point on A (if outSeparatingAxis is near zero, this may not be not the deepest point)
  592. /// @param outPointB is the contact point on B (if outSeparatingAxis is near zero, this may not be not the deepest point)
  593. /// @param outSeparatingAxis On return this will contain a vector that points from A to B along the smallest distance of separation.
  594. /// The length of this vector indicates the separation of A and B without their convex radius.
  595. /// If it is near zero, the direction may not be accurate as the bodies may overlap when lambda = 0.
  596. ///
  597. /// @return true if a hit was found, ioLambda is the solution for lambda and outPoint and outSeparatingAxis are valid.
  598. template <typename A, typename B>
  599. bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inTolerance, const A &inA, const B &inB, float inConvexRadiusA, float inConvexRadiusB, float &ioLambda, Vec3 &outPointA, Vec3 &outPointB, Vec3 &outSeparatingAxis)
  600. {
  601. float tolerance_sq = Square(inTolerance);
  602. // Calculate how close A and B (without their convex radius) need to be to eachother in order for us to consider this a collision
  603. float sum_convex_radius = inConvexRadiusA + inConvexRadiusB;
  604. // Transform the shape to be cast to the starting position
  605. using TransformedA = TransformedConvexObject<A>;
  606. TransformedA transformed_a(inStart, inA);
  607. // Reset state
  608. mNumPoints = 0;
  609. float lambda = 0.0f;
  610. Vec3 x = Vec3::sZero(); // Since A is already transformed we can start the cast from zero
  611. Vec3 v = -inA.GetSupport(Vec3::sZero());
  612. float v_len_sq = FLT_MAX;
  613. bool allow_restart = false;
  614. // Keeps track of separating axis of the previous iteration.
  615. // Initialized at zero as we don't know if our first v is actually a separating axis.
  616. Vec3 prev_v = Vec3::sZero();
  617. for (;;)
  618. {
  619. #ifdef JPH_GJK_DEBUG
  620. Trace("v = [%s], num_points = %d", ConvertToString(v).c_str(), mNumPoints);
  621. #endif
  622. // Calculate the minkowski difference inB - inA
  623. // inA is moving, so we need to add the back side of inB to the front side of inA
  624. // Keep the support points on A and B separate so that in the end we can calculate a contact point
  625. Vec3 p = transformed_a.GetSupport(-v);
  626. Vec3 q = inB.GetSupport(v);
  627. Vec3 w = x - (q - p);
  628. #ifdef JPH_GJK_DEBUG
  629. Trace("w = [%s]", ConvertToString(w).c_str());
  630. #endif
  631. // Difference from article to this code:
  632. // We did not include the convex radius in p and q in order to be able to calculate a good separating axis at the end of the algorithm.
  633. // However when moving forward along inDirection we do need to take this into account so that we keep A and B separated by the sum of their convex radii.
  634. // From p we have to subtract: inConvexRadiusA * v / |v|
  635. // To q we have to add: inConvexRadiusB * v / |v|
  636. // This means that to w we have to add: -(inConvexRadiusA + inConvexRadiusB) * v / |v|
  637. // So to v . w we have to add: v . (-(inConvexRadiusA + inConvexRadiusB) * v / |v|) = -(inConvexRadiusA + inConvexRadiusB) * |v|
  638. float v_dot_w = v.Dot(w) - sum_convex_radius * v.Length();
  639. #ifdef JPH_GJK_DEBUG
  640. Trace("v . w = %g", v_dot_w);
  641. #endif
  642. if (v_dot_w > 0.0f)
  643. {
  644. // If ray and normal are in the same direction, we've passed A and there's no collision
  645. float v_dot_r = v.Dot(inDirection);
  646. #ifdef JPH_GJK_DEBUG
  647. Trace("v . r = %g", v_dot_r);
  648. #endif
  649. if (v_dot_r >= 0.0f)
  650. return false;
  651. // Update the lower bound for lambda
  652. float delta = v_dot_w / v_dot_r;
  653. float old_lambda = lambda;
  654. lambda -= delta;
  655. #ifdef JPH_GJK_DEBUG
  656. Trace("lambda = %g, delta = %g", lambda, delta);
  657. #endif
  658. // If lambda didn't change, we cannot converge any further and we assume a hit
  659. if (old_lambda == lambda)
  660. break;
  661. // If lambda is bigger or equal than max, we don't have a hit
  662. if (lambda >= ioLambda)
  663. return false;
  664. // Update x to new closest point on the ray
  665. x = lambda * inDirection;
  666. // We've shifted x, so reset v_len_sq so that it is not used as early out when GetClosest returns false
  667. v_len_sq = FLT_MAX;
  668. // Now that we've moved, we know that A and B are not intersecting at lambda = 0, so we can update our tolerance to stop iterating
  669. // as soon as A and B are inConvexRadiusA + inConvexRadiusB apart
  670. tolerance_sq = Square(inTolerance + sum_convex_radius);
  671. // We allow rebuilding the simplex once after x changes because the simplex was built
  672. // for another x and numerical round off builds up as you keep adding points to an
  673. // existing simplex
  674. allow_restart = true;
  675. }
  676. // Add p to set P, q to set Q: P = P U {p}, Q = Q U {q}
  677. mP[mNumPoints] = p;
  678. mQ[mNumPoints] = q;
  679. ++mNumPoints;
  680. // Calculate Y = {x} - (Q - P)
  681. for (int i = 0; i < mNumPoints; ++i)
  682. mY[i] = x - (mQ[i] - mP[i]);
  683. // Determine the new closest point from Y to origin
  684. bool needs_restart = false;
  685. uint32 set; // Set of points that form the new simplex
  686. if (!GetClosest(v_len_sq, v, v_len_sq, set))
  687. {
  688. #ifdef JPH_GJK_DEBUG
  689. Trace("Failed to converge");
  690. #endif
  691. // We failed to converge, restart
  692. needs_restart = true;
  693. }
  694. else if (set == 0xf)
  695. {
  696. #ifdef JPH_GJK_DEBUG
  697. Trace("Full simplex");
  698. #endif
  699. // If there are 4 points, x is inside the tetrahedron and we've found a hit
  700. // Double check that A and B are indeed touching according to our tolerance
  701. if (v_len_sq <= tolerance_sq)
  702. break;
  703. // We failed to converge, restart
  704. needs_restart = true;
  705. }
  706. if (needs_restart)
  707. {
  708. // Only allow 1 restart, if we still can't get a closest point
  709. // we're so close that we return this as a hit
  710. if (!allow_restart)
  711. break;
  712. // If we fail to converge, we start again with the last point as simplex
  713. #ifdef JPH_GJK_DEBUG
  714. Trace("Restarting");
  715. #endif
  716. allow_restart = false;
  717. mP[0] = p;
  718. mQ[0] = q;
  719. mNumPoints = 1;
  720. v = x - q;
  721. v_len_sq = FLT_MAX;
  722. continue;
  723. }
  724. // Update the points P and Q to form the new simplex
  725. // Note: We're not updating Y as Y will shift with x so we have to calculate it every iteration
  726. UpdatePointSetPQ(set);
  727. // Check if A and B are touching according to our tolerance
  728. if (v_len_sq <= tolerance_sq)
  729. {
  730. #ifdef JPH_GJK_DEBUG
  731. Trace("Converged");
  732. #endif
  733. break;
  734. }
  735. // Store our v to return as separating axis
  736. prev_v = v;
  737. }
  738. // Calculate Y = {x} - (Q - P) again so we can calculate the contact points
  739. for (int i = 0; i < mNumPoints; ++i)
  740. mY[i] = x - (mQ[i] - mP[i]);
  741. // Calculate the offset we need to apply to A and B to correct for the convex radius
  742. Vec3 normalized_v = v.NormalizedOr(Vec3::sZero());
  743. Vec3 convex_radius_a = inConvexRadiusA * normalized_v;
  744. Vec3 convex_radius_b = inConvexRadiusB * normalized_v;
  745. // Get the contact point
  746. // Note that A and B will coincide when lambda > 0. In this case we calculate only B as it is more accurate as it contains less terms.
  747. switch (mNumPoints)
  748. {
  749. case 1:
  750. outPointB = mQ[0] + convex_radius_b;
  751. outPointA = lambda > 0.0f? outPointB : mP[0] - convex_radius_a;
  752. break;
  753. case 2:
  754. {
  755. float bu, bv;
  756. ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], bu, bv);
  757. outPointB = bu * mQ[0] + bv * mQ[1] + convex_radius_b;
  758. outPointA = lambda > 0.0f? outPointB : bu * mP[0] + bv * mP[1] - convex_radius_a;
  759. }
  760. break;
  761. case 3:
  762. case 4: // A full simplex, we can't properly determine a contact point! As contact point we take the closest point of the previous iteration.
  763. {
  764. float bu, bv, bw;
  765. ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], mY[2], bu, bv, bw);
  766. outPointB = bu * mQ[0] + bv * mQ[1] + bw * mQ[2] + convex_radius_b;
  767. outPointA = lambda > 0.0f? outPointB : bu * mP[0] + bv * mP[1] + bw * mP[2] - convex_radius_a;
  768. }
  769. break;
  770. }
  771. // Store separating axis, in case we have a convex radius we can just return v,
  772. // otherwise v will be very small and we resort to returning previous v as an approximation.
  773. outSeparatingAxis = sum_convex_radius > 0.0f? -v : -prev_v;
  774. // Store hit fraction
  775. ioLambda = lambda;
  776. return true;
  777. }
  778. private:
  779. #ifdef JPH_GJK_DEBUG
  780. /// Draw state of algorithm
  781. void DrawState()
  782. {
  783. Mat44 origin = Mat44::sTranslation(mOffset);
  784. // Draw origin
  785. DebugRenderer::sInstance->DrawCoordinateSystem(origin, 1.0f);
  786. // Draw the hull
  787. DebugRenderer::sInstance->DrawGeometry(origin, mGeometry->mBounds, mGeometry->mBounds.GetExtent().LengthSq(), Color::sYellow, mGeometry);
  788. // Draw Y
  789. for (int i = 0; i < mNumPoints; ++i)
  790. {
  791. // Draw support point
  792. Vec3 y_i = origin * mY[i];
  793. DebugRenderer::sInstance->DrawMarker(y_i, Color::sRed, 1.0f);
  794. for (int j = i + 1; j < mNumPoints; ++j)
  795. {
  796. // Draw edge
  797. Vec3 y_j = origin * mY[j];
  798. DebugRenderer::sInstance->DrawLine(y_i, y_j, Color::sRed);
  799. for (int k = j + 1; k < mNumPoints; ++k)
  800. {
  801. // Make sure triangle faces the origin
  802. Vec3 y_k = origin * mY[k];
  803. Vec3 center = (y_i + y_j + y_k) / 3.0f;
  804. Vec3 normal = (y_j - y_i).Cross(y_k - y_i);
  805. if (normal.Dot(center) < 0.0f)
  806. DebugRenderer::sInstance->DrawTriangle(y_i, y_j, y_k, Color::sLightGrey);
  807. else
  808. DebugRenderer::sInstance->DrawTriangle(y_i, y_k, y_j, Color::sLightGrey);
  809. }
  810. }
  811. }
  812. // Offset to the right
  813. mOffset += Vec3(mGeometry->mBounds.GetSize().GetX() + 2.0f, 0, 0);
  814. }
  815. #endif // JPH_GJK_DEBUG
  816. Vec3 mY[4]; ///< Support points on A - B
  817. Vec3 mP[4]; ///< Support point on A
  818. Vec3 mQ[4]; ///< Support point on B
  819. int mNumPoints = 0; ///< Number of points in mY, mP and mQ that are valid
  820. #ifdef JPH_GJK_DEBUG
  821. DebugRenderer::GeometryRef mGeometry; ///< A visualization of the minkowski difference for state drawing
  822. Vec3 mOffset = Vec3::sZero(); ///< Offset to use for state drawing
  823. #endif
  824. };
  825. } // JPH