intersection.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. /*
  2. Copyright (c) 2013 Daniele Bartolini, Michele Rossi
  3. Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
  4. Permission is hereby granted, free of charge, to any person
  5. obtaining a copy of this software and associated documentation
  6. files (the "Software"), to deal in the Software without
  7. restriction, including without limitation the rights to use,
  8. copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. copies of the Software, and to permit persons to whom the
  10. Software is furnished to do so, subject to the following
  11. conditions:
  12. The above copyright notice and this permission notice shall be
  13. included in all copies or substantial portions of the Software.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  16. OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  19. WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  21. OTHER DEALINGS IN THE SOFTWARE.
  22. */
  23. #pragma once
  24. #include "ray.h"
  25. #include "plane.h"
  26. #include "sphere.h"
  27. #include "frustum.h"
  28. #include "math_utils.h"
  29. #include "types.h"
  30. #include "aabb.h"
  31. namespace crown
  32. {
  33. class Intersection
  34. {
  35. public:
  36. static bool test_ray_plane(const Ray& r, const Plane& p, float& distance, Vector3& inttersectionPoint_t);
  37. static bool test_ray_sphere(const Ray& r, const Sphere& s, float& distance, Vector3& intersectionPoint);
  38. static bool test_ray_box(const Ray& r, const AABB& b, float& distance, Vector3& intersectionPoint);
  39. static bool test_plane_3(const Plane& p1, const Plane& p2, const Plane& p3, Vector3& ip);
  40. static bool test_static_sphere_plane(const Sphere& s, const Plane& p);
  41. static bool test_static_sphere_sphere(const Sphere& a, const Sphere& b);
  42. static bool test_dynamic_sphere_plane(const Sphere& s, const Vector3& d, const Plane& p, float& it, Vector3& intersectionPoint);
  43. static bool test_dynamic_sphere_sphere(const Sphere& s1, const Vector3& d1, const Sphere& s2, const Vector3& d2, float& it, Vector3& intersectionPoint);
  44. static bool test_static_box_box(const AABB& b1, const AABB& b2);
  45. static bool test_dynamic_box_box(const AABB& b1, const Vector3& v1, const AABB& b2, const Vector3& v2, float& it);
  46. static bool test_frustum_sphere(const Frustum& f, const Sphere& s);
  47. static bool test_frustum_box(const Frustum& f, const AABB& box);
  48. private:
  49. // Disable construction
  50. Intersection();
  51. };
  52. //-----------------------------------------------------------------------------
  53. inline bool Intersection::test_ray_plane(const Ray& r, const Plane& p, float& distance, Vector3& intersectionPoint)
  54. {
  55. float nd = vector3::dot(r.direction(), p.n);
  56. float orpn = vector3::dot(r.origin(), p.n);
  57. if (nd < 0.0)
  58. {
  59. float dist = (-p.d - orpn) / nd;
  60. if (dist > 0.0)
  61. {
  62. distance = dist;
  63. intersectionPoint = r.origin() + r.direction() * distance;
  64. return true;
  65. }
  66. }
  67. return false;
  68. }
  69. //-----------------------------------------------------------------------------
  70. inline bool Intersection::test_ray_sphere(const Ray& r, const Sphere& s, float& distance, Vector3& intersectionPoint)
  71. {
  72. Vector3 v = s.center() - r.origin();
  73. float b = vector3::dot(v, r.direction());
  74. float det = (s.radius() * s.radius()) - vector3::dot(v, v) + (b * b);
  75. if (det < 0.0 || b < s.radius())
  76. {
  77. return false;
  78. }
  79. distance = b - math::sqrt(det);
  80. intersectionPoint = r.origin() + r.direction() * distance;
  81. return true;
  82. }
  83. //-----------------------------------------------------------------------------
  84. inline bool Intersection::test_ray_box(const Ray& r, const AABB& b, float& /*distance*/, Vector3& /*intersectionPoint*/)
  85. {
  86. if (r.origin().x < b.min.x)
  87. {
  88. if (r.direction().x < 0.0)
  89. {
  90. return false;
  91. }
  92. }
  93. if (r.origin().x > b.max.x)
  94. {
  95. if (r.direction().x > 0.0)
  96. {
  97. return false;
  98. }
  99. }
  100. if (r.origin().y < b.min.y)
  101. {
  102. if (r.direction().y < 0.0)
  103. {
  104. return false;
  105. }
  106. }
  107. if (r.origin().y > b.max.y)
  108. {
  109. if (r.direction().y > 0.0)
  110. {
  111. return false;
  112. }
  113. }
  114. if (r.origin().z < b.min.z)
  115. {
  116. if (r.direction().z < 0.0)
  117. {
  118. return false;
  119. }
  120. }
  121. if (r.origin().z > b.max.z)
  122. {
  123. if (r.direction().z > 0.0)
  124. {
  125. return false;
  126. }
  127. }
  128. // Possibly int32_tersects
  129. return true;
  130. // TODO
  131. }
  132. //-----------------------------------------------------------------------------
  133. inline bool Intersection::test_plane_3(const Plane& p1, const Plane& p2, const Plane& p3, Vector3& ip)
  134. {
  135. const Vector3& n1 = p1.n;
  136. const Vector3& n2 = p2.n;
  137. const Vector3& n3 = p3.n;
  138. float den = -vector3::dot(vector3::cross(n1, n2), n3);
  139. if (math::equals(den, (float)0.0))
  140. {
  141. return false;
  142. }
  143. Vector3 res = p1.d * vector3::cross(n2, n3) + p2.d * vector3::cross(n3, n1) + p3.d * vector3::cross(n1, n2);
  144. ip = res / den;
  145. return true;
  146. }
  147. //-----------------------------------------------------------------------------
  148. inline bool Intersection::test_static_sphere_plane(const Sphere& s, const Plane& p)
  149. {
  150. if (math::abs(plane::distance_to_point(p, s.center())) < s.radius())
  151. {
  152. return true;
  153. }
  154. return false;
  155. }
  156. //-----------------------------------------------------------------------------
  157. inline bool Intersection::test_static_sphere_sphere(const Sphere& a, const Sphere& b)
  158. {
  159. float dist = vector3::squared_length(b.center() - a.center());
  160. return (dist < (b.radius() + a.radius()) * (b.radius() + a.radius()));
  161. }
  162. //-----------------------------------------------------------------------------
  163. inline bool Intersection::test_dynamic_sphere_plane(const Sphere& s, const Vector3& d, const Plane& p, float& it, Vector3& intersectionPoint)
  164. {
  165. const Vector3& sphereCenter = s.center();
  166. const float sphereRadius = s.radius();
  167. float t0; // Time at which the sphere int32_tersects the plane remaining at the front side of the plane
  168. float t1; // Time at which the sphere int32_tersects the plane remaining at the back side of the plane
  169. float sphereToPlaneDistance = plane::distance_to_point(p, sphereCenter);
  170. float planeNormalDotVelocity = vector3::dot(p.n, d);
  171. if (planeNormalDotVelocity > 0.0)
  172. {
  173. return false;
  174. }
  175. // If the sphere is travelling parallel to the plane
  176. if (planeNormalDotVelocity == 0.0)
  177. {
  178. // If the sphere is embedded in the plane
  179. if (math::abs(sphereToPlaneDistance) < sphereRadius)
  180. {
  181. t0 = 0.0;
  182. t1 = 1.0;
  183. it = t0;
  184. intersectionPoint = s.center() - p.n * s.radius();
  185. return true;
  186. }
  187. return false;
  188. }
  189. t0 = (sphereRadius - sphereToPlaneDistance) / planeNormalDotVelocity;
  190. t1 = (-sphereRadius - sphereToPlaneDistance) / planeNormalDotVelocity;
  191. // If _both_ t0 and t1 are outside [0,1] then collision can never happen
  192. if (t0 >= 0.0 && t0 <= 1.0)
  193. {
  194. it = math::min(t0, t1);
  195. intersectionPoint = s.center() - p.n * s.radius() + (d * it);
  196. return true;
  197. }
  198. return false;
  199. }
  200. //-----------------------------------------------------------------------------
  201. inline bool Intersection::test_dynamic_sphere_sphere(const Sphere& s1, const Vector3& d1, const Sphere& s2, const Vector3& d2, float& it, Vector3& /*intersectionPoint*/)
  202. {
  203. // s1 == static sphere
  204. // s2 == moving sphere
  205. Vector3 d = d2 - d1;
  206. vector3::normalize(d);
  207. const Vector3& cs = s1.center();
  208. const Vector3& cm = s2.center();
  209. Vector3 e = cs - cm;
  210. float r = s1.radius() + s2.radius();
  211. // If ||e|| < r, int32_tersection occurs at t = 0
  212. if (vector3::length(e) < r)
  213. {
  214. it = 0.0;
  215. return true;
  216. }
  217. // it == Intersection Time
  218. float ed = vector3::dot(e, d);
  219. float squared = (ed * ed) + (r * r) - vector3::dot(e, e);
  220. // If the value inside the square root is neg, then no int32_tersection
  221. if (squared < 0.0)
  222. {
  223. return false;
  224. }
  225. float t = ed - math::sqrt(squared);
  226. float l = vector3::length(d2 - d1);
  227. // If t < 0 || t > l, then non int32_tersection in the considered period of time
  228. if (t < 0.0 || t > l)
  229. {
  230. return false;
  231. }
  232. it = t / l;
  233. return true;
  234. }
  235. //-----------------------------------------------------------------------------
  236. inline bool Intersection::test_static_box_box(const AABB& b1, const AABB& b2)
  237. {
  238. if (b1.min.x > b2.max.x || b1.max.x < b2.min.x)
  239. {
  240. return false;
  241. }
  242. if (b1.min.y > b2.max.y || b1.max.y < b2.min.y)
  243. {
  244. return false;
  245. }
  246. if (b1.min.z > b2.max.z || b1.max.z < b2.min.z)
  247. {
  248. return false;
  249. }
  250. return true;
  251. }
  252. //-----------------------------------------------------------------------------
  253. inline bool Intersection::test_dynamic_box_box(const AABB& b1, const Vector3& v1, const AABB& b2, const Vector3& v2, float& it)
  254. {
  255. // b1 == static box
  256. // b2 == moving box
  257. Vector3 d = v2 - v1;
  258. // Start time of int32_tersection aint64_t each axis
  259. Vector3 tEnterXYZ(0.0, 0.0, 0.0);
  260. // Stop time of int32_tersection aint64_t each axis
  261. Vector3 tLeaveXYZ(1.0, 1.0, 1.0);
  262. // If the resulting displacement equals zero, then fallback to static int32_tersection test
  263. if (math::equals(d.x, (float)0.0))
  264. {
  265. if (b1.min.x > b2.max.x || b1.max.x < b2.min.x)
  266. {
  267. return false;
  268. }
  269. }
  270. if (math::equals(d.y, (float)0.0))
  271. {
  272. if (b1.min.y > b2.max.y || b1.max.y < b2.min.y)
  273. {
  274. return false;
  275. }
  276. }
  277. if (math::equals(d.z, (float)0.0))
  278. {
  279. if (b1.min.z > b2.max.z || b1.max.z < b2.min.z)
  280. {
  281. return false;
  282. }
  283. }
  284. // Otherwise, compute the enter/leave times aint64_t each axis
  285. float oneOverD = (float)(1.0 / d.x);
  286. tEnterXYZ.x = (b1.min.x - b2.max.x) * oneOverD;
  287. tLeaveXYZ.x = (b1.max.x - b2.min.x) * oneOverD;
  288. oneOverD = (float)(1.0 / d.y);
  289. tEnterXYZ.y = (b1.min.y - b2.max.y) * oneOverD;
  290. tLeaveXYZ.y = (b1.max.y - b2.min.y) * oneOverD;
  291. oneOverD = (float)(1.0 / d.z);
  292. tEnterXYZ.z = (b1.min.z - b2.max.z) * oneOverD;
  293. tLeaveXYZ.z = (b1.max.z - b2.min.z) * oneOverD;
  294. // We must ensure that enter time < leave time
  295. if (tLeaveXYZ.x < tEnterXYZ.x)
  296. {
  297. math::swap(tLeaveXYZ.x, tEnterXYZ.x);
  298. }
  299. if (tLeaveXYZ.y < tEnterXYZ.y)
  300. {
  301. math::swap(tLeaveXYZ.y, tEnterXYZ.y);
  302. }
  303. if (tLeaveXYZ.z < tEnterXYZ.z)
  304. {
  305. math::swap(tLeaveXYZ.z, tEnterXYZ.z);
  306. }
  307. float tEnter = math::max(tEnterXYZ.x, math::max(tEnterXYZ.y, tEnterXYZ.z));
  308. float tLeave = math::min(tLeaveXYZ.x, math::min(tLeaveXYZ.y, tLeaveXYZ.z));
  309. // If tEnter > 1, then there is no int32_tersection in the period
  310. // of time cosidered
  311. if (tEnter > 1.0)
  312. {
  313. return false;
  314. }
  315. it = tEnter;
  316. return tEnter < tLeave;
  317. }
  318. //-----------------------------------------------------------------------------
  319. inline bool Intersection::test_frustum_sphere(const Frustum& f, const Sphere& s)
  320. {
  321. if (plane::distance_to_point(f.left, s.center()) < -s.radius() ||
  322. plane::distance_to_point(f.right, s.center()) < -s.radius())
  323. {
  324. return false;
  325. }
  326. if (plane::distance_to_point(f.bottom, s.center()) < -s.radius() ||
  327. plane::distance_to_point(f.top, s.center()) < -s.radius())
  328. {
  329. return false;
  330. }
  331. if (plane::distance_to_point(f.near, s.center()) < -s.radius() ||
  332. plane::distance_to_point(f.far, s.center()) < -s.radius())
  333. {
  334. return false;
  335. }
  336. return true;
  337. }
  338. //-----------------------------------------------------------------------------
  339. inline bool Intersection::test_frustum_box(const Frustum& f, const AABB& b)
  340. {
  341. uint8_t out;
  342. out = 0;
  343. if (plane::distance_to_point(f.left, aabb::vertex(b, 0)) < 0.0) out++;
  344. if (plane::distance_to_point(f.left, aabb::vertex(b, 1)) < 0.0) out++;
  345. if (plane::distance_to_point(f.left, aabb::vertex(b, 2)) < 0.0) out++;
  346. if (plane::distance_to_point(f.left, aabb::vertex(b, 3)) < 0.0) out++;
  347. if (plane::distance_to_point(f.left, aabb::vertex(b, 4)) < 0.0) out++;
  348. if (plane::distance_to_point(f.left, aabb::vertex(b, 5)) < 0.0) out++;
  349. if (plane::distance_to_point(f.left, aabb::vertex(b, 6)) < 0.0) out++;
  350. if (plane::distance_to_point(f.left, aabb::vertex(b, 7)) < 0.0) out++;
  351. // If all vertices are outside one face, then the box doesn't intersect the frustum
  352. if (out == 8) return false;
  353. out = 0;
  354. if (plane::distance_to_point(f.right, aabb::vertex(b, 0)) < 0.0) out++;
  355. if (plane::distance_to_point(f.right, aabb::vertex(b, 1)) < 0.0) out++;
  356. if (plane::distance_to_point(f.right, aabb::vertex(b, 2)) < 0.0) out++;
  357. if (plane::distance_to_point(f.right, aabb::vertex(b, 3)) < 0.0) out++;
  358. if (plane::distance_to_point(f.right, aabb::vertex(b, 4)) < 0.0) out++;
  359. if (plane::distance_to_point(f.right, aabb::vertex(b, 5)) < 0.0) out++;
  360. if (plane::distance_to_point(f.right, aabb::vertex(b, 6)) < 0.0) out++;
  361. if (plane::distance_to_point(f.right, aabb::vertex(b, 7)) < 0.0) out++;
  362. if (out == 8) return false;
  363. out = 0;
  364. if (plane::distance_to_point(f.bottom, aabb::vertex(b, 0)) < 0.0) out++;
  365. if (plane::distance_to_point(f.bottom, aabb::vertex(b, 1)) < 0.0) out++;
  366. if (plane::distance_to_point(f.bottom, aabb::vertex(b, 2)) < 0.0) out++;
  367. if (plane::distance_to_point(f.bottom, aabb::vertex(b, 3)) < 0.0) out++;
  368. if (plane::distance_to_point(f.bottom, aabb::vertex(b, 4)) < 0.0) out++;
  369. if (plane::distance_to_point(f.bottom, aabb::vertex(b, 5)) < 0.0) out++;
  370. if (plane::distance_to_point(f.bottom, aabb::vertex(b, 6)) < 0.0) out++;
  371. if (plane::distance_to_point(f.bottom, aabb::vertex(b, 7)) < 0.0) out++;
  372. if (out == 8) return false;
  373. out = 0;
  374. if (plane::distance_to_point(f.top, aabb::vertex(b, 0)) < 0.0) out++;
  375. if (plane::distance_to_point(f.top, aabb::vertex(b, 1)) < 0.0) out++;
  376. if (plane::distance_to_point(f.top, aabb::vertex(b, 2)) < 0.0) out++;
  377. if (plane::distance_to_point(f.top, aabb::vertex(b, 3)) < 0.0) out++;
  378. if (plane::distance_to_point(f.top, aabb::vertex(b, 4)) < 0.0) out++;
  379. if (plane::distance_to_point(f.top, aabb::vertex(b, 5)) < 0.0) out++;
  380. if (plane::distance_to_point(f.top, aabb::vertex(b, 6)) < 0.0) out++;
  381. if (plane::distance_to_point(f.top, aabb::vertex(b, 7)) < 0.0) out++;
  382. if (out == 8) return false;
  383. out = 0;
  384. if (plane::distance_to_point(f.near, aabb::vertex(b, 0)) < 0.0) out++;
  385. if (plane::distance_to_point(f.near, aabb::vertex(b, 1)) < 0.0) out++;
  386. if (plane::distance_to_point(f.near, aabb::vertex(b, 2)) < 0.0) out++;
  387. if (plane::distance_to_point(f.near, aabb::vertex(b, 3)) < 0.0) out++;
  388. if (plane::distance_to_point(f.near, aabb::vertex(b, 4)) < 0.0) out++;
  389. if (plane::distance_to_point(f.near, aabb::vertex(b, 5)) < 0.0) out++;
  390. if (plane::distance_to_point(f.near, aabb::vertex(b, 6)) < 0.0) out++;
  391. if (plane::distance_to_point(f.near, aabb::vertex(b, 7)) < 0.0) out++;
  392. if (out == 8) return false;
  393. out = 0;
  394. if (plane::distance_to_point(f.far, aabb::vertex(b, 0)) < 0.0) out++;
  395. if (plane::distance_to_point(f.far, aabb::vertex(b, 1)) < 0.0) out++;
  396. if (plane::distance_to_point(f.far, aabb::vertex(b, 2)) < 0.0) out++;
  397. if (plane::distance_to_point(f.far, aabb::vertex(b, 3)) < 0.0) out++;
  398. if (plane::distance_to_point(f.far, aabb::vertex(b, 4)) < 0.0) out++;
  399. if (plane::distance_to_point(f.far, aabb::vertex(b, 5)) < 0.0) out++;
  400. if (plane::distance_to_point(f.far, aabb::vertex(b, 6)) < 0.0) out++;
  401. if (plane::distance_to_point(f.far, aabb::vertex(b, 7)) < 0.0) out++;
  402. if (out == 8) return false;
  403. // If we are here, it is because either the box intersects or it is contained in the frustum
  404. return true;
  405. }
  406. } // namespace crown