GJKClosestPoint.h 30 KB

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