intersection.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. * Copyright (c) 2012-2021 Daniele Bartolini et al.
  3. * License: https://github.com/dbartolini/crown/blob/master/LICENSE
  4. */
  5. #include "core/math/aabb.inl"
  6. #include "core/math/constants.h"
  7. #include "core/math/intersection.h"
  8. #include "core/math/plane3.inl"
  9. #include "core/math/sphere.inl"
  10. #include "core/math/vector3.inl"
  11. #include <float.h> // FLT_MAX
  12. namespace crown
  13. {
  14. f32 ray_plane_intersection(const Vector3& from, const Vector3& dir, const Plane3& p)
  15. {
  16. const f32 num = dot(from, p.n);
  17. const f32 den = dot(dir, p.n);
  18. if (fequal(den, 0.0f))
  19. return -1.0f;
  20. return (-p.d - num) / den;
  21. }
  22. f32 ray_disc_intersection(const Vector3& from, const Vector3& dir, const Vector3& center, f32 radius, const Vector3& normal)
  23. {
  24. const Plane3 p = plane3::from_point_and_normal(center, normal);
  25. const f32 t = ray_plane_intersection(from, dir, p);
  26. if (t == -1.0f)
  27. return -1.0f;
  28. const Vector3 intersection_point = from + dir * t;
  29. if (distance_squared(intersection_point, center) < radius*radius)
  30. return t;
  31. return -1.0f;
  32. }
  33. f32 ray_sphere_intersection(const Vector3& from, const Vector3& dir, const Sphere& s)
  34. {
  35. const Vector3 v = s.c - from;
  36. const f32 b = dot(v, dir);
  37. const f32 rr = s.r * s.r;
  38. const f32 bb = b * b;
  39. const f32 det = rr - dot(v, v) + bb;
  40. if (det < 0.0f || b < s.r)
  41. return -1.0f;
  42. return b - fsqrt(det);
  43. }
  44. // http://www.opengl-tutorial.org/miscellaneous/clicking-on-objects/picking-with-custom-ray-obb-function/
  45. f32 ray_obb_intersection(const Vector3& from, const Vector3& dir, const Matrix4x4& tm, const Vector3& half_extents)
  46. {
  47. f32 tmin = 0.0f;
  48. f32 tmax = FLT_MAX;
  49. const Vector3 obb_pos = vector3(tm.t.x, tm.t.y, tm.t.z);
  50. const Vector3 delta = obb_pos - from;
  51. {
  52. const Vector3 xaxis = vector3(tm.x.x, tm.x.y, tm.x.z);
  53. const f32 e = dot(xaxis, delta);
  54. const f32 f = dot(dir, xaxis);
  55. if (fabs(f) > 0.001f)
  56. {
  57. f32 t1 = (e-half_extents.x)/f;
  58. f32 t2 = (e+half_extents.x)/f;
  59. if (t1 > t2)
  60. exchange(t1, t2);
  61. if (t2 < tmax)
  62. tmax = t2;
  63. if (t1 > tmin)
  64. tmin = t1;
  65. if (tmax < tmin)
  66. return -1.0f;
  67. }
  68. else
  69. {
  70. if (-e-half_extents.x > 0.0f || -e+half_extents.x < 0.0f)
  71. return -1.0f;
  72. }
  73. }
  74. {
  75. const Vector3 yaxis = vector3(tm.y.x, tm.y.y, tm.y.z);
  76. const f32 e = dot(yaxis, delta);
  77. const f32 f = dot(dir, yaxis);
  78. if (fabs(f) > 0.001f)
  79. {
  80. f32 t1 = (e-half_extents.y)/f;
  81. f32 t2 = (e+half_extents.y)/f;
  82. if (t1 > t2)
  83. exchange(t1, t2);
  84. if (t2 < tmax)
  85. tmax = t2;
  86. if (t1 > tmin)
  87. tmin = t1;
  88. if (tmin > tmax)
  89. return -1.0f;
  90. }
  91. else
  92. {
  93. if (-e-half_extents.y > 0.0f || -e+half_extents.y < 0.0f)
  94. return -1.0f;
  95. }
  96. }
  97. {
  98. const Vector3 zaxis = vector3(tm.z.x, tm.z.y, tm.z.z);
  99. const f32 e = dot(zaxis, delta);
  100. const f32 f = dot(dir, zaxis);
  101. if (fabs(f) > 0.001f)
  102. {
  103. f32 t1 = (e-half_extents.z)/f;
  104. f32 t2 = (e+half_extents.z)/f;
  105. if (t1 > t2)
  106. exchange(t1, t2);
  107. if (t2 < tmax)
  108. tmax = t2;
  109. if (t1 > tmin)
  110. tmin = t1;
  111. if (tmin > tmax)
  112. return -1.0f;
  113. }
  114. else
  115. {
  116. if (-e-half_extents.z > 0.0f || -e+half_extents.z < 0.0f)
  117. return -1.0f;
  118. }
  119. }
  120. return tmin;
  121. }
  122. f32 ray_triangle_intersection(const Vector3& from, const Vector3& dir, const Vector3& v0, const Vector3& v1, const Vector3& v2)
  123. {
  124. const Vector3 verts[] = { v0, v1, v2 };
  125. const u16 inds[] = { 0, 1, 2 };
  126. return ray_mesh_intersection(from, dir, MATRIX4X4_IDENTITY, verts, sizeof(Vector3), inds, 3);
  127. }
  128. f32 ray_mesh_intersection(const Vector3& from, const Vector3& dir, const Matrix4x4& tm, const void* vertices, u32 stride, const u16* indices, u32 num)
  129. {
  130. bool hit = false;
  131. f32 tmin = FLT_MAX;
  132. for (u32 i = 0; i < num; i += 3)
  133. {
  134. const u32 i0 = indices[i + 0];
  135. const u32 i1 = indices[i + 1];
  136. const u32 i2 = indices[i + 2];
  137. const Vector3& v0 = *(const Vector3*)((const char*)vertices + i0*stride) * tm;
  138. const Vector3& v1 = *(const Vector3*)((const char*)vertices + i1*stride) * tm;
  139. const Vector3& v2 = *(const Vector3*)((const char*)vertices + i2*stride) * tm;
  140. // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
  141. // Find vectors for two edges sharing v0
  142. const Vector3 e1 = v1 - v0;
  143. const Vector3 e2 = v2 - v0;
  144. // Begin calculating determinant - also used to calculate u parameter
  145. const Vector3 P = cross(dir, e2);
  146. // If determinant is near zero, ray lies in plane of triangle
  147. const f32 det = dot(e1, P);
  148. if (fequal(det, 0.0f))
  149. continue;
  150. const f32 inv_det = 1.0f / det;
  151. // Distance from v0 to ray origin
  152. const Vector3 T = from - v0;
  153. // u parameter and test bound
  154. const f32 u = dot(T, P) * inv_det;
  155. // The intersection lies outside of the triangle
  156. if (u < 0.0f || u > 1.0f)
  157. continue;
  158. // Prepare to test v parameter
  159. const Vector3 Q = cross(T, e1);
  160. // v parameter and test bound
  161. const f32 v = dot(dir, Q) * inv_det;
  162. // The intersection lies outside of the triangle
  163. if (v < 0.0f || u + v > 1.0f)
  164. continue;
  165. const f32 t = dot(e2, Q) * inv_det;
  166. // Ray intersection
  167. if (t > FLOAT_EPSILON)
  168. {
  169. hit = true;
  170. tmin = min(t, tmin);
  171. }
  172. }
  173. return hit ? tmin : -1.0f;
  174. }
  175. bool plane_3_intersection(const Plane3& a, const Plane3& b, const Plane3& c, Vector3& ip)
  176. {
  177. const Vector3 na = a.n;
  178. const Vector3 nb = b.n;
  179. const Vector3 nc = c.n;
  180. const f32 den = -dot(cross(na, nb), nc);
  181. if (fequal(den, 0.0f))
  182. return false;
  183. const f32 inv_den = 1.0f / den;
  184. const Vector3 nbnc = a.d * cross(nb, nc);
  185. const Vector3 ncna = b.d * cross(nc, na);
  186. const Vector3 nanb = c.d * cross(na, nb);
  187. ip = (nbnc + ncna + nanb) * inv_den;
  188. return true;
  189. }
  190. bool frustum_sphere_intersection(const Frustum& f, const Sphere& s)
  191. {
  192. if (plane3::distance_to_point(f.plane_left, s.c) < -s.r ||
  193. plane3::distance_to_point(f.plane_right, s.c) < -s.r)
  194. {
  195. return false;
  196. }
  197. if (plane3::distance_to_point(f.plane_bottom, s.c) < -s.r ||
  198. plane3::distance_to_point(f.plane_top, s.c) < -s.r)
  199. {
  200. return false;
  201. }
  202. if (plane3::distance_to_point(f.plane_near, s.c) < -s.r ||
  203. plane3::distance_to_point(f.plane_far, s.c) < -s.r)
  204. {
  205. return false;
  206. }
  207. return true;
  208. }
  209. bool frustum_box_intersection(const Frustum& f, const AABB& b)
  210. {
  211. const Vector3 v0 = aabb::vertex(b, 0);
  212. const Vector3 v1 = aabb::vertex(b, 1);
  213. const Vector3 v2 = aabb::vertex(b, 2);
  214. const Vector3 v3 = aabb::vertex(b, 3);
  215. const Vector3 v4 = aabb::vertex(b, 4);
  216. const Vector3 v5 = aabb::vertex(b, 5);
  217. const Vector3 v6 = aabb::vertex(b, 6);
  218. const Vector3 v7 = aabb::vertex(b, 7);
  219. u8 out = 0;
  220. out += (plane3::distance_to_point(f.plane_left, v0) < 0.0f) ? 1 : 0;
  221. out += (plane3::distance_to_point(f.plane_left, v1) < 0.0f) ? 1 : 0;
  222. out += (plane3::distance_to_point(f.plane_left, v2) < 0.0f) ? 1 : 0;
  223. out += (plane3::distance_to_point(f.plane_left, v3) < 0.0f) ? 1 : 0;
  224. out += (plane3::distance_to_point(f.plane_left, v4) < 0.0f) ? 1 : 0;
  225. out += (plane3::distance_to_point(f.plane_left, v5) < 0.0f) ? 1 : 0;
  226. out += (plane3::distance_to_point(f.plane_left, v6) < 0.0f) ? 1 : 0;
  227. out += (plane3::distance_to_point(f.plane_left, v7) < 0.0f) ? 1 : 0;
  228. if (out == 8) return false;
  229. out = 0;
  230. out += (plane3::distance_to_point(f.plane_right, v0) < 0.0f) ? 1 : 0;
  231. out += (plane3::distance_to_point(f.plane_right, v1) < 0.0f) ? 1 : 0;
  232. out += (plane3::distance_to_point(f.plane_right, v2) < 0.0f) ? 1 : 0;
  233. out += (plane3::distance_to_point(f.plane_right, v3) < 0.0f) ? 1 : 0;
  234. out += (plane3::distance_to_point(f.plane_right, v4) < 0.0f) ? 1 : 0;
  235. out += (plane3::distance_to_point(f.plane_right, v5) < 0.0f) ? 1 : 0;
  236. out += (plane3::distance_to_point(f.plane_right, v6) < 0.0f) ? 1 : 0;
  237. out += (plane3::distance_to_point(f.plane_right, v7) < 0.0f) ? 1 : 0;
  238. if (out == 8) return false;
  239. out = 0;
  240. out += (plane3::distance_to_point(f.plane_bottom, v0) < 0.0f) ? 1 : 0;
  241. out += (plane3::distance_to_point(f.plane_bottom, v1) < 0.0f) ? 1 : 0;
  242. out += (plane3::distance_to_point(f.plane_bottom, v2) < 0.0f) ? 1 : 0;
  243. out += (plane3::distance_to_point(f.plane_bottom, v3) < 0.0f) ? 1 : 0;
  244. out += (plane3::distance_to_point(f.plane_bottom, v4) < 0.0f) ? 1 : 0;
  245. out += (plane3::distance_to_point(f.plane_bottom, v5) < 0.0f) ? 1 : 0;
  246. out += (plane3::distance_to_point(f.plane_bottom, v6) < 0.0f) ? 1 : 0;
  247. out += (plane3::distance_to_point(f.plane_bottom, v7) < 0.0f) ? 1 : 0;
  248. if (out == 8) return false;
  249. out = 0;
  250. out += (plane3::distance_to_point(f.plane_top, v0) < 0.0f) ? 1 : 0;
  251. out += (plane3::distance_to_point(f.plane_top, v1) < 0.0f) ? 1 : 0;
  252. out += (plane3::distance_to_point(f.plane_top, v2) < 0.0f) ? 1 : 0;
  253. out += (plane3::distance_to_point(f.plane_top, v3) < 0.0f) ? 1 : 0;
  254. out += (plane3::distance_to_point(f.plane_top, v4) < 0.0f) ? 1 : 0;
  255. out += (plane3::distance_to_point(f.plane_top, v5) < 0.0f) ? 1 : 0;
  256. out += (plane3::distance_to_point(f.plane_top, v6) < 0.0f) ? 1 : 0;
  257. out += (plane3::distance_to_point(f.plane_top, v7) < 0.0f) ? 1 : 0;
  258. if (out == 8) return false;
  259. out = 0;
  260. out += (plane3::distance_to_point(f.plane_near, v0) < 0.0f) ? 1 : 0;
  261. out += (plane3::distance_to_point(f.plane_near, v1) < 0.0f) ? 1 : 0;
  262. out += (plane3::distance_to_point(f.plane_near, v2) < 0.0f) ? 1 : 0;
  263. out += (plane3::distance_to_point(f.plane_near, v3) < 0.0f) ? 1 : 0;
  264. out += (plane3::distance_to_point(f.plane_near, v4) < 0.0f) ? 1 : 0;
  265. out += (plane3::distance_to_point(f.plane_near, v5) < 0.0f) ? 1 : 0;
  266. out += (plane3::distance_to_point(f.plane_near, v6) < 0.0f) ? 1 : 0;
  267. out += (plane3::distance_to_point(f.plane_near, v7) < 0.0f) ? 1 : 0;
  268. if (out == 8) return false;
  269. out = 0;
  270. out += (plane3::distance_to_point(f.plane_far, v0) < 0.0f) ? 1 : 0;
  271. out += (plane3::distance_to_point(f.plane_far, v1) < 0.0f) ? 1 : 0;
  272. out += (plane3::distance_to_point(f.plane_far, v2) < 0.0f) ? 1 : 0;
  273. out += (plane3::distance_to_point(f.plane_far, v3) < 0.0f) ? 1 : 0;
  274. out += (plane3::distance_to_point(f.plane_far, v4) < 0.0f) ? 1 : 0;
  275. out += (plane3::distance_to_point(f.plane_far, v5) < 0.0f) ? 1 : 0;
  276. out += (plane3::distance_to_point(f.plane_far, v6) < 0.0f) ? 1 : 0;
  277. out += (plane3::distance_to_point(f.plane_far, v7) < 0.0f) ? 1 : 0;
  278. if (out == 8) return false;
  279. // If we are here, it is because either the box intersects or it is contained in the frustum
  280. return true;
  281. }
  282. } // namespace crown