GJKClosestPoint.h 31 KB

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