IntrAlignedBox3Sphere3.h 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/DistPointAlignedBox.h>
  9. #include <Mathematics/IntrRay3AlignedBox3.h>
  10. #include <Mathematics/Hypersphere.h>
  11. // The find-intersection query is based on the document
  12. // https://www.geometrictools.com/Documentation/IntersectionMovingSphereBox.pdf
  13. // and also uses the method of separating axes.
  14. // https://www.geometrictools.com/Documentation/MethodOfSeparatingAxes.pdf
  15. namespace WwiseGTE
  16. {
  17. template <typename Real>
  18. class TIQuery<Real, AlignedBox3<Real>, Sphere3<Real>>
  19. {
  20. public:
  21. // The intersection query considers the box and sphere to be solids;
  22. // that is, the sphere object includes the region inside the spherical
  23. // boundary and the box object includes the region inside the cuboid
  24. // boundary. If the sphere object and box object object overlap, the
  25. // objects intersect.
  26. struct Result
  27. {
  28. bool intersect;
  29. };
  30. Result operator()(AlignedBox3<Real> const& box, Sphere3<Real> const& sphere)
  31. {
  32. DCPQuery<Real, Vector3<Real>, AlignedBox3<Real>> pbQuery;
  33. auto pbResult = pbQuery(sphere.center, box);
  34. Result result;
  35. result.intersect = (pbResult.sqrDistance <= sphere.radius * sphere.radius);
  36. return result;
  37. }
  38. };
  39. template <typename Real>
  40. class FIQuery<Real, AlignedBox3<Real>, Sphere3<Real>>
  41. {
  42. public:
  43. // Currently, only a dynamic query is supported. A static query will
  44. // need to compute the intersection set of (solid) box and sphere.
  45. struct Result
  46. {
  47. // The cases are
  48. // 1. Objects initially overlapping. The contactPoint is only one
  49. // of infinitely many points in the overlap.
  50. // intersectionType = -1
  51. // contactTime = 0
  52. // contactPoint = sphere.center
  53. // 2. Objects initially separated but do not intersect later. The
  54. // contactTime and contactPoint are invalid.
  55. // intersectionType = 0
  56. // contactTime = 0
  57. // contactPoint = (0,0,0)
  58. // 3. Objects initially separated but intersect later.
  59. // intersectionType = +1
  60. // contactTime = first time T > 0
  61. // contactPoint = corresponding first contact
  62. int intersectionType;
  63. Real contactTime;
  64. Vector3<Real> contactPoint;
  65. // TODO: To support arbitrary precision for the contactTime,
  66. // return q0, q1 and q2 where contactTime = (q0 - sqrt(q1)) / q2.
  67. // The caller can compute contactTime to desired number of digits
  68. // of precision. These are valid when intersectionType is +1 but
  69. // are set to zero (invalid) in the other cases. Do the same for
  70. // the contactPoint.
  71. };
  72. Result operator()(AlignedBox3<Real> const& box, Vector3<Real> const& boxVelocity,
  73. Sphere3<Real> const& sphere, Vector3<Real> const& sphereVelocity)
  74. {
  75. Result result = { 0, (Real)0, { (Real)0, (Real)0, (Real)0 } };
  76. // Translate the sphere and box so that the box center becomes
  77. // the origin. Compute the velocity of the sphere relative to
  78. // the box.
  79. Vector3<Real> boxCenter = (box.max + box.min) * (Real)0.5;
  80. Vector3<Real> extent = (box.max - box.min) * (Real)0.5;
  81. Vector3<Real> C = sphere.center - boxCenter;
  82. Vector3<Real> V = sphereVelocity - boxVelocity;
  83. // Test for no-intersection that leads to an early exit. The test
  84. // is fast, using the method of separating axes.
  85. AlignedBox3<Real> superBox;
  86. for (int i = 0; i < 3; ++i)
  87. {
  88. superBox.max[i] = extent[i] + sphere.radius;
  89. superBox.min[i] = -superBox.max[i];
  90. }
  91. TIQuery<Real, Ray3<Real>, AlignedBox3<Real>> rbQuery;
  92. auto rbResult = rbQuery(Ray3<Real>(C, V), superBox);
  93. if (!rbResult.intersect)
  94. {
  95. return result;
  96. }
  97. // Change signs on components, if necessary, to transform C to the
  98. // first quadrant. Adjust the velocity accordingly.
  99. Real sign[3];
  100. for (int i = 0; i < 3; ++i)
  101. {
  102. if (C[i] >= (Real)0)
  103. {
  104. sign[i] = (Real)1;
  105. }
  106. else
  107. {
  108. C[i] = -C[i];
  109. V[i] = -V[i];
  110. sign[i] = (Real)-1;
  111. }
  112. }
  113. DoQuery(extent, C, sphere.radius, V, result);
  114. if (result.intersectionType != 0)
  115. {
  116. // Translate back to the original coordinate system.
  117. for (int i = 0; i < 3; ++i)
  118. {
  119. if (sign[i] < (Real)0)
  120. {
  121. result.contactPoint[i] = -result.contactPoint[i];
  122. }
  123. }
  124. result.contactPoint += boxCenter;
  125. }
  126. return result;
  127. }
  128. protected:
  129. // The query assumes the box is axis-aligned with center at the
  130. // origin. Callers need to convert the results back to the original
  131. // coordinate system of the query.
  132. void DoQuery(Vector3<Real> const& K, Vector3<Real> const& C,
  133. Real radius, Vector3<Real> const& V, Result& result)
  134. {
  135. Vector3<Real> delta = C - K;
  136. if (delta[2] <= radius)
  137. {
  138. if (delta[1] <= radius)
  139. {
  140. if (delta[0] <= radius)
  141. {
  142. if (delta[2] <= (Real)0)
  143. {
  144. if (delta[1] <= (Real)0)
  145. {
  146. if (delta[0] <= (Real)0)
  147. {
  148. InteriorOverlap(C, result);
  149. }
  150. else
  151. {
  152. // x-face
  153. FaceOverlap(0, 1, 2, K, C, radius, delta, result);
  154. }
  155. }
  156. else
  157. {
  158. if (delta[0] <= (Real)0)
  159. {
  160. // y-face
  161. FaceOverlap(1, 2, 0, K, C, radius, delta, result);
  162. }
  163. else
  164. {
  165. // xy-edge
  166. if (delta[0] * delta[0] + delta[1] * delta[1] <= radius * radius)
  167. {
  168. EdgeOverlap(0, 1, 2, K, C, radius, delta, result);
  169. }
  170. else
  171. {
  172. EdgeSeparated(0, 1, 2, K, C, radius, delta, V, result);
  173. }
  174. }
  175. }
  176. }
  177. else
  178. {
  179. if (delta[1] <= (Real)0)
  180. {
  181. if (delta[0] <= (Real)0)
  182. {
  183. // z-face
  184. FaceOverlap(2, 0, 1, K, C, radius, delta, result);
  185. }
  186. else
  187. {
  188. // xz-edge
  189. if (delta[0] * delta[0] + delta[2] * delta[2] <= radius * radius)
  190. {
  191. EdgeOverlap(2, 0, 1, K, C, radius, delta, result);
  192. }
  193. else
  194. {
  195. EdgeSeparated(2, 0, 1, K, C, radius, delta, V, result);
  196. }
  197. }
  198. }
  199. else
  200. {
  201. if (delta[0] <= (Real)0)
  202. {
  203. // yz-edge
  204. if (delta[1] * delta[1] + delta[2] * delta[2] <= radius * radius)
  205. {
  206. EdgeOverlap(1, 2, 0, K, C, radius, delta, result);
  207. }
  208. else
  209. {
  210. EdgeSeparated(1, 2, 0, K, C, radius, delta, V, result);
  211. }
  212. }
  213. else
  214. {
  215. // xyz-vertex
  216. if (Dot(delta, delta) <= radius * radius)
  217. {
  218. VertexOverlap(K, radius, delta, result);
  219. }
  220. else
  221. {
  222. VertexSeparated(K, radius, delta, V, result);
  223. }
  224. }
  225. }
  226. }
  227. }
  228. else
  229. {
  230. // x-face
  231. FaceUnbounded(0, 1, 2, K, C, radius, delta, V, result);
  232. }
  233. }
  234. else
  235. {
  236. if (delta[0] <= radius)
  237. {
  238. // y-face
  239. FaceUnbounded(1, 2, 0, K, C, radius, delta, V, result);
  240. }
  241. else
  242. {
  243. // xy-edge
  244. EdgeUnbounded(0, 1, 2, K, C, radius, delta, V, result);
  245. }
  246. }
  247. }
  248. else
  249. {
  250. if (delta[1] <= radius)
  251. {
  252. if (delta[0] <= radius)
  253. {
  254. // z-face
  255. FaceUnbounded(2, 0, 1, K, C, radius, delta, V, result);
  256. }
  257. else
  258. {
  259. // xz-edge
  260. EdgeUnbounded(2, 0, 1, K, C, radius, delta, V, result);
  261. }
  262. }
  263. else
  264. {
  265. if (delta[0] <= radius)
  266. {
  267. // yz-edge
  268. EdgeUnbounded(1, 2, 0, K, C, radius, delta, V, result);
  269. }
  270. else
  271. {
  272. // xyz-vertex
  273. VertexUnbounded(K, C, radius, delta, V, result);
  274. }
  275. }
  276. }
  277. }
  278. private:
  279. void InteriorOverlap(Vector3<Real> const& C, Result& result)
  280. {
  281. result.intersectionType = -1;
  282. result.contactTime = (Real)0;
  283. result.contactPoint = C;
  284. }
  285. void VertexOverlap(Vector3<Real> const& K, Real radius,
  286. Vector3<Real> const& delta, Result& result)
  287. {
  288. result.intersectionType = (Dot(delta, delta) < radius * radius ? -1 : 1);
  289. result.contactTime = (Real)0;
  290. result.contactPoint = K;
  291. }
  292. void EdgeOverlap(int i0, int i1, int i2, Vector3<Real> const& K,
  293. Vector3<Real> const& C, Real radius, Vector3<Real> const& delta,
  294. Result& result)
  295. {
  296. result.intersectionType = (delta[i0] * delta[i0] + delta[i1] * delta[i1] < radius * radius ? -1 : 1);
  297. result.contactTime = (Real)0;
  298. result.contactPoint[i0] = K[i0];
  299. result.contactPoint[i1] = K[i1];
  300. result.contactPoint[i2] = C[i2];
  301. }
  302. void FaceOverlap(int i0, int i1, int i2, Vector3<Real> const& K,
  303. Vector3<Real> const& C, Real radius, Vector3<Real> const& delta,
  304. Result& result)
  305. {
  306. result.intersectionType = (delta[i0] < radius ? -1 : 1);
  307. result.contactTime = (Real)0;
  308. result.contactPoint[i0] = K[i0];
  309. result.contactPoint[i1] = C[i1];
  310. result.contactPoint[i2] = C[i2];
  311. }
  312. void VertexSeparated(Vector3<Real> const& K, Real radius,
  313. Vector3<Real> const& delta, Vector3<Real> const& V, Result& result)
  314. {
  315. if (V[0] < (Real)0 || V[1] < (Real)0 || V[2] < (Real)0)
  316. {
  317. DoQueryRayRoundedVertex(K, radius, delta, V, result);
  318. }
  319. }
  320. void EdgeSeparated(int i0, int i1, int i2, Vector3<Real> const& K,
  321. Vector3<Real> const& C, Real radius, Vector3<Real> const& delta,
  322. Vector3<Real> const& V, Result& result)
  323. {
  324. if (V[i0] < (Real)0 || V[i1] < (Real)0)
  325. {
  326. DoQueryRayRoundedEdge(i0, i1, i2, K, C, radius, delta, V, result);
  327. }
  328. }
  329. void VertexUnbounded(Vector3<Real> const& K, Vector3<Real> const& C, Real radius,
  330. Vector3<Real> const& delta, Vector3<Real> const& V, Result& result)
  331. {
  332. if (V[0] < (Real)0 && V[1] < (Real)0 && V[2] < (Real)0)
  333. {
  334. // Determine the face of the rounded box that is intersected
  335. // by the ray C+T*V.
  336. Real T = (radius - delta[0]) / V[0];
  337. int j0 = 0;
  338. Real temp = (radius - delta[1]) / V[1];
  339. if (temp > T)
  340. {
  341. T = temp;
  342. j0 = 1;
  343. }
  344. temp = (radius - delta[2]) / V[2];
  345. if (temp > T)
  346. {
  347. T = temp;
  348. j0 = 2;
  349. }
  350. // The j0-rounded face is the candidate for intersection.
  351. int j1 = (j0 + 1) % 3;
  352. int j2 = (j1 + 1) % 3;
  353. DoQueryRayRoundedFace(j0, j1, j2, K, C, radius, delta, V, result);
  354. }
  355. }
  356. void EdgeUnbounded(int i0, int i1, int /* i2 */, Vector3<Real> const& K,
  357. Vector3<Real> const& C, Real radius, Vector3<Real> const& delta,
  358. Vector3<Real> const& V, Result& result)
  359. {
  360. if (V[i0] < (Real)0 && V[i1] < (Real)0)
  361. {
  362. // Determine the face of the rounded box that is intersected
  363. // by the ray C+T*V.
  364. Real T = (radius - delta[i0]) / V[i0];
  365. int j0 = i0;
  366. Real temp = (radius - delta[i1]) / V[i1];
  367. if (temp > T)
  368. {
  369. T = temp;
  370. j0 = i1;
  371. }
  372. // The j0-rounded face is the candidate for intersection.
  373. int j1 = (j0 + 1) % 3;
  374. int j2 = (j1 + 1) % 3;
  375. DoQueryRayRoundedFace(j0, j1, j2, K, C, radius, delta, V, result);
  376. }
  377. }
  378. void FaceUnbounded(int i0, int i1, int i2, Vector3<Real> const& K,
  379. Vector3<Real> const& C, Real radius, Vector3<Real> const& delta,
  380. Vector3<Real> const& V, Result& result)
  381. {
  382. if (V[i0] < (Real)0)
  383. {
  384. DoQueryRayRoundedFace(i0, i1, i2, K, C, radius, delta, V, result);
  385. }
  386. }
  387. void DoQueryRayRoundedVertex(Vector3<Real> const& K, Real radius,
  388. Vector3<Real> const& delta, Vector3<Real> const& V, Result& result)
  389. {
  390. Real a1 = Dot(V, delta);
  391. if (a1 < (Real)0)
  392. {
  393. // The caller must ensure that a0 > 0 and a2 > 0.
  394. Real a0 = Dot(delta, delta) - radius * radius;
  395. Real a2 = Dot(V, V);
  396. Real adiscr = a1 * a1 - a2 * a0;
  397. if (adiscr >= (Real)0)
  398. {
  399. // The ray intersects the rounded vertex, so the sphere-box
  400. // contact point is the vertex.
  401. result.intersectionType = 1;
  402. result.contactTime = -(a1 + std::sqrt(adiscr)) / a2;
  403. result.contactPoint = K;
  404. }
  405. }
  406. }
  407. void DoQueryRayRoundedEdge(int i0, int i1, int i2, Vector3<Real> const& K,
  408. Vector3<Real> const& C, Real radius, Vector3<Real> const& delta,
  409. Vector3<Real> const& V, Result& result)
  410. {
  411. Real b1 = V[i0] * delta[i0] + V[i1] * delta[i1];
  412. if (b1 < (Real)0)
  413. {
  414. // The caller must ensure that b0 > 0 and b2 > 0.
  415. Real b0 = delta[i0] * delta[i0] + delta[i1] * delta[i1] - radius * radius;
  416. Real b2 = V[i0] * V[i0] + V[i1] * V[i1];
  417. Real bdiscr = b1 * b1 - b2 * b0;
  418. if (bdiscr >= (Real)0)
  419. {
  420. Real T = -(b1 + std::sqrt(bdiscr)) / b2;
  421. Real p2 = C[i2] + T * V[i2];
  422. if (-K[i2] <= p2)
  423. {
  424. if (p2 <= K[i2])
  425. {
  426. // The ray intersects the finite cylinder of the
  427. // rounded edge, so the sphere-box contact point
  428. // is on the corresponding box edge.
  429. result.intersectionType = 1;
  430. result.contactTime = T;
  431. result.contactPoint[i0] = K[i0];
  432. result.contactPoint[i1] = K[i1];
  433. result.contactPoint[i2] = p2;
  434. }
  435. else
  436. {
  437. // The ray intersects the infinite cylinder but
  438. // not the finite cylinder of the rounded edge.
  439. // It is possible the ray intersects the rounded
  440. // vertex for K.
  441. DoQueryRayRoundedVertex(K, radius, delta, V, result);
  442. }
  443. }
  444. else
  445. {
  446. // The ray intersects the infinite cylinder but
  447. // not the finite cylinder of the rounded edge.
  448. // It is possible the ray intersects the rounded
  449. // vertex for otherK.
  450. Vector3<Real> otherK, otherDelta;
  451. otherK[i0] = K[i0];
  452. otherK[i1] = K[i1];
  453. otherK[i2] = -K[i2];
  454. otherDelta[i0] = C[i0] - otherK[i0];
  455. otherDelta[i1] = C[i1] - otherK[i1];
  456. otherDelta[i2] = C[i2] - otherK[i2];
  457. DoQueryRayRoundedVertex(otherK, radius, otherDelta, V, result);
  458. }
  459. }
  460. }
  461. }
  462. void DoQueryRayRoundedFace(int i0, int i1, int i2, Vector3<Real> const& K,
  463. Vector3<Real> const& C, Real radius, Vector3<Real> const& delta,
  464. Vector3<Real> const& V, Result& result)
  465. {
  466. Vector3<Real> otherK, otherDelta;
  467. Real T = (radius - delta[i0]) / V[i0];
  468. Real p1 = C[i1] + T * V[i1];
  469. Real p2 = C[i2] + T * V[i2];
  470. if (p1 < -K[i1])
  471. {
  472. // The ray potentially intersects the rounded (i0,i1)-edge
  473. // whose top-most vertex is otherK.
  474. otherK[i0] = K[i0];
  475. otherK[i1] = -K[i1];
  476. otherK[i2] = K[i2];
  477. otherDelta[i0] = C[i0] - otherK[i0];
  478. otherDelta[i1] = C[i1] - otherK[i1];
  479. otherDelta[i2] = C[i2] - otherK[i2];
  480. DoQueryRayRoundedEdge(i0, i1, i2, otherK, C, radius, otherDelta, V, result);
  481. if (result.intersectionType == 0)
  482. {
  483. if (p2 < -K[i2])
  484. {
  485. // The ray potentially intersects the rounded
  486. // (i2,i0)-edge whose right-most vertex is otherK.
  487. otherK[i0] = K[i0];
  488. otherK[i1] = K[i1];
  489. otherK[i2] = -K[i2];
  490. otherDelta[i0] = C[i0] - otherK[i0];
  491. otherDelta[i1] = C[i1] - otherK[i1];
  492. otherDelta[i2] = C[i2] - otherK[i2];
  493. DoQueryRayRoundedEdge(i2, i0, i1, otherK, C, radius, otherDelta, V, result);
  494. }
  495. else if (p2 > K[i2])
  496. {
  497. // The ray potentially intersects the rounded
  498. // (i2,i0)-edge whose right-most vertex is K.
  499. DoQueryRayRoundedEdge(i2, i0, i1, K, C, radius, delta, V, result);
  500. }
  501. }
  502. }
  503. else if (p1 <= K[i1])
  504. {
  505. if (p2 < -K[i2])
  506. {
  507. // The ray potentially intersects the rounded
  508. // (i2,i0)-edge whose right-most vertex is otherK.
  509. otherK[i0] = K[i0];
  510. otherK[i1] = K[i1];
  511. otherK[i2] = -K[i2];
  512. otherDelta[i0] = C[i0] - otherK[i0];
  513. otherDelta[i1] = C[i1] - otherK[i1];
  514. otherDelta[i2] = C[i2] - otherK[i2];
  515. DoQueryRayRoundedEdge(i2, i0, i1, otherK, C, radius, otherDelta, V, result);
  516. }
  517. else if (p2 <= K[i2])
  518. {
  519. // The ray intersects the i0-face of the rounded box, so
  520. // the sphere-box contact point is on the corresponding
  521. // box face.
  522. result.intersectionType = 1;
  523. result.contactTime = T;
  524. result.contactPoint[i0] = K[i0];
  525. result.contactPoint[i1] = p1;
  526. result.contactPoint[i2] = p2;
  527. }
  528. else // p2 > K[i2]
  529. {
  530. // The ray potentially intersects the rounded
  531. // (i2,i0)-edge whose right-most vertex is K.
  532. DoQueryRayRoundedEdge(i2, i0, i1, K, C, radius, delta, V, result);
  533. }
  534. }
  535. else // p1 > K[i1]
  536. {
  537. // The ray potentially intersects the rounded (i0,i1)-edge
  538. // whose top-most vertex is K.
  539. DoQueryRayRoundedEdge(i0, i1, i2, K, C, radius, delta, V, result);
  540. if (result.intersectionType == 0)
  541. {
  542. if (p2 < -K[i2])
  543. {
  544. // The ray potentially intersects the rounded
  545. // (i2,i0)-edge whose right-most vertex is otherK.
  546. otherK[i0] = K[i0];
  547. otherK[i1] = K[i1];
  548. otherK[i2] = -K[i2];
  549. otherDelta[i0] = C[i0] - otherK[i0];
  550. otherDelta[i1] = C[i1] - otherK[i1];
  551. otherDelta[i2] = C[i2] - otherK[i2];
  552. DoQueryRayRoundedEdge(i2, i0, i1, otherK, C, radius, otherDelta, V, result);
  553. }
  554. else if (p2 > K[i2])
  555. {
  556. // The ray potentially intersects the rounded
  557. // (i2,i0)-edge whose right-most vertex is K.
  558. DoQueryRayRoundedEdge(i2, i0, i1, K, C, radius, delta, V, result);
  559. }
  560. }
  561. }
  562. }
  563. };
  564. }