FunctionsTestCollision.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. // Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Collision/Functions.h>
  6. #include <AnKi/Collision/ConvexHullShape.h>
  7. #include <AnKi/Collision/Obb.h>
  8. #include <AnKi/Collision/LineSegment.h>
  9. #include <AnKi/Collision/Cone.h>
  10. #include <AnKi/Collision/Sphere.h>
  11. #include <AnKi/Collision/GjkEpa.h>
  12. namespace anki
  13. {
  14. template<typename T, typename Y>
  15. static Bool testCollisionGjk(const T& a, const Y& b)
  16. {
  17. auto callbackA = [](const void* shape, const Vec4& dir) {
  18. return static_cast<const T*>(shape)->computeSupport(dir);
  19. };
  20. auto callbackB = [](const void* shape, const Vec4& dir) {
  21. return static_cast<const Y*>(shape)->computeSupport(dir);
  22. };
  23. return gjkIntersection(&a, callbackA, &b, callbackB);
  24. }
  25. Bool testCollision(const Aabb& a, const Aabb& b)
  26. {
  27. #if ANKI_SIMD_SSE
  28. const __m128 gt0 = _mm_cmpgt_ps(a.getMin().getSimd(), b.getMax().getSimd());
  29. const __m128 gt1 = _mm_cmpgt_ps(b.getMin().getSimd(), a.getMax().getSimd());
  30. const __m128 combined = _mm_or_ps(gt0, gt1);
  31. const int res = _mm_movemask_ps(combined); // Will set the first bit of each byte of combined
  32. return res == 0;
  33. #else
  34. // if separated in x direction
  35. if(a.getMin().x() > b.getMax().x() || b.getMin().x() > a.getMax().x())
  36. {
  37. return false;
  38. }
  39. // if separated in y direction
  40. if(a.getMin().y() > b.getMax().y() || b.getMin().y() > a.getMax().y())
  41. {
  42. return false;
  43. }
  44. // if separated in z direction
  45. if(a.getMin().z() > b.getMax().z() || b.getMin().z() > a.getMax().z())
  46. {
  47. return false;
  48. }
  49. // no separation, must be intersecting
  50. return true;
  51. #endif
  52. }
  53. Bool testCollision(const Aabb& aabb, const Sphere& s)
  54. {
  55. const Vec4& c = s.getCenter();
  56. // Find the box's closest point to the sphere
  57. #if ANKI_SIMD_SSE
  58. __m128 gt = _mm_cmpgt_ps(c.getSimd(), aabb.getMax().getSimd());
  59. __m128 lt = _mm_cmplt_ps(c.getSimd(), aabb.getMin().getSimd());
  60. __m128 m = _mm_or_ps(_mm_and_ps(gt, aabb.getMax().getSimd()), _mm_andnot_ps(gt, c.getSimd()));
  61. __m128 n = _mm_or_ps(_mm_and_ps(lt, aabb.getMin().getSimd()), _mm_andnot_ps(lt, m));
  62. const Vec4 cp(n);
  63. #else
  64. Vec4 cp(c); // Closest Point
  65. for(U i = 0; i < 3; i++)
  66. {
  67. // if the center is greater than the max then the closest point is the max
  68. if(c[i] > aabb.getMax()[i])
  69. {
  70. cp[i] = aabb.getMax()[i];
  71. }
  72. else if(c[i] < aabb.getMin()[i]) // relative to the above
  73. {
  74. cp[i] = aabb.getMin()[i];
  75. }
  76. else
  77. {
  78. // the c lies between min and max, do nothing
  79. }
  80. }
  81. #endif
  82. // if the c lies totally inside the box then the sub is the zero, this means that the length is also zero and thus
  83. // it's always smaller than rsq
  84. const Vec4 sub = c - cp;
  85. return (sub.getLengthSquared() <= (s.getRadius() * s.getRadius())) ? true : false;
  86. }
  87. Bool testCollision(const Aabb& aabb, const Obb& obb)
  88. {
  89. return testCollisionGjk(aabb, obb);
  90. }
  91. Bool testCollision(const Aabb& aabb, const ConvexHullShape& hull)
  92. {
  93. return testCollisionGjk(aabb, hull);
  94. }
  95. Bool testCollision(const Aabb& aabb, const LineSegment& ls)
  96. {
  97. F32 maxS = MIN_F32;
  98. F32 minT = MAX_F32;
  99. // do tests against three sets of planes
  100. for(U i = 0; i < 3; ++i)
  101. {
  102. // segment is parallel to plane
  103. if(isZero(ls.getDirection()[i]))
  104. {
  105. // segment passes by box
  106. if(ls.getOrigin()[i] < aabb.getMin()[i] || ls.getOrigin()[i] > aabb.getMax()[i])
  107. {
  108. return false;
  109. }
  110. }
  111. else
  112. {
  113. // compute intersection parameters and sort
  114. F32 s = (aabb.getMin()[i] - ls.getOrigin()[i]) / ls.getDirection()[i];
  115. F32 t = (aabb.getMax()[i] - ls.getOrigin()[i]) / ls.getDirection()[i];
  116. if(s > t)
  117. {
  118. F32 temp = s;
  119. s = t;
  120. t = temp;
  121. }
  122. // adjust min and max values
  123. if(s > maxS)
  124. {
  125. maxS = s;
  126. }
  127. if(t < minT)
  128. {
  129. minT = t;
  130. }
  131. // check for intersection failure
  132. if(minT < 0.0 || maxS > 1.0 || maxS > minT)
  133. {
  134. return false;
  135. }
  136. }
  137. }
  138. // done, have intersection
  139. return true;
  140. }
  141. Bool testCollision(const Aabb& aabb, const Cone& cone)
  142. {
  143. ANKI_ASSERT(!"TODO");
  144. return false;
  145. }
  146. Bool testCollision(const Sphere& a, const Sphere& b)
  147. {
  148. const F32 tmp = a.getRadius() + b.getRadius();
  149. return (a.getCenter() - b.getCenter()).getLengthSquared() <= tmp * tmp;
  150. }
  151. Bool testCollision(const Sphere& sphere, const Obb& obb)
  152. {
  153. return testCollisionGjk(sphere, obb);
  154. }
  155. Bool testCollision(const Sphere& sphere, const ConvexHullShape& hull)
  156. {
  157. return testCollisionGjk(sphere, hull);
  158. }
  159. Bool testCollision(const Sphere& s, const LineSegment& ls)
  160. {
  161. const Vec4& v = ls.getDirection();
  162. const Vec4 w0 = s.getCenter() - ls.getOrigin();
  163. const F32 w0dv = w0.dot(v);
  164. const F32 rsq = s.getRadius() * s.getRadius();
  165. if(w0dv < 0.0f) // if the ang is >90
  166. {
  167. return w0.getLengthSquared() <= rsq;
  168. }
  169. const Vec4 w1 = w0 - v; // aka center - P1, where P1 = seg.origin + seg.dir
  170. const F32 w1dv = w1.dot(v);
  171. if(w1dv > 0.0f) // if the ang is <90
  172. {
  173. return w1.getLengthSquared() <= rsq;
  174. }
  175. // the big parenthesis is the projection of w0 to v
  176. const Vec4 tmp = w0 - (v * (w0.dot(v) / v.getLengthSquared()));
  177. return tmp.getLengthSquared() <= rsq;
  178. }
  179. Bool testCollision(const Sphere& sphere, const Cone& cone)
  180. {
  181. // https://bartwronski.com/2017/04/13/cull-that-cone/
  182. const F32 coneAngle = cone.getAngle() / 2.0f;
  183. const Vec4 V = sphere.getCenter() - cone.getOrigin();
  184. const F32 VlenSq = V.dot(V);
  185. const F32 V1len = V.dot(cone.getDirection());
  186. const F32 distanceClosestPoint = cos(coneAngle) * sqrt(VlenSq - V1len * V1len) - V1len * sin(coneAngle);
  187. const Bool angleCull = distanceClosestPoint > sphere.getRadius();
  188. const Bool frontCull = V1len > sphere.getRadius() + cone.getLength();
  189. const Bool backCull = V1len < -sphere.getRadius();
  190. return !(angleCull || frontCull || backCull);
  191. }
  192. Bool testCollision(const Obb& a, const Obb& b)
  193. {
  194. return testCollisionGjk(a, b);
  195. }
  196. Bool testCollision(const Obb& obb, const ConvexHullShape& hull)
  197. {
  198. return testCollisionGjk(obb, hull);
  199. }
  200. Bool testCollision(const Obb& obb, const LineSegment& ls)
  201. {
  202. F32 maxS = MIN_F32;
  203. F32 minT = MAX_F32;
  204. // compute difference vector
  205. const Vec4 diff = obb.getCenter() - ls.getOrigin();
  206. // for each axis do
  207. for(U i = 0; i < 3; ++i)
  208. {
  209. // get axis i
  210. const Vec4 axis = obb.getRotation().getColumn(i).xyz0();
  211. // project relative vector onto axis
  212. const F32 e = axis.dot(diff);
  213. const F32 f = ls.getDirection().dot(axis);
  214. // ray is parallel to plane
  215. if(isZero(f))
  216. {
  217. // ray passes by box
  218. if(-e - obb.getExtend()[i] > 0.0 || -e + obb.getExtend()[i] > 0.0)
  219. {
  220. return false;
  221. }
  222. continue;
  223. }
  224. F32 s = (e - obb.getExtend()[i]) / f;
  225. F32 t = (e + obb.getExtend()[i]) / f;
  226. // fix order
  227. if(s > t)
  228. {
  229. F32 temp = s;
  230. s = t;
  231. t = temp;
  232. }
  233. // adjust min and max values
  234. if(s > maxS)
  235. {
  236. maxS = s;
  237. }
  238. if(t < minT)
  239. {
  240. minT = t;
  241. }
  242. // check for intersection failure
  243. if(minT < 0.0 || maxS > 1.0 || maxS > minT)
  244. {
  245. return false;
  246. }
  247. }
  248. // done, have intersection
  249. return true;
  250. }
  251. Bool testCollision(const Obb& obb, const Cone& cone)
  252. {
  253. ANKI_ASSERT(!"TODO");
  254. return false;
  255. }
  256. Bool testCollision(const ConvexHullShape& a, const ConvexHullShape& b)
  257. {
  258. return testCollisionGjk(a, b);
  259. }
  260. Bool testCollision(const ConvexHullShape& hull, const LineSegment& ls)
  261. {
  262. ANKI_ASSERT(!"TODO");
  263. return false;
  264. }
  265. Bool testCollision(const ConvexHullShape& hull, const Cone& cone)
  266. {
  267. ANKI_ASSERT(!"TODO");
  268. return false;
  269. }
  270. Bool testCollision(const LineSegment& a, const LineSegment& b)
  271. {
  272. ANKI_ASSERT(!"TODO");
  273. return false;
  274. }
  275. Bool testCollision(const Cone& a, const Cone& b)
  276. {
  277. ANKI_ASSERT(!"TODO");
  278. return false;
  279. }
  280. Bool testCollision(const Plane& plane, const Ray& ray, Vec4& intersection)
  281. {
  282. Bool intersects = false;
  283. const F32 d = testPlane(plane, ray.getOrigin()); // Dist of origin to the plane
  284. const F32 a = plane.getNormal().dot(ray.getDirection());
  285. if(d > 0.0f && a < 0.0f)
  286. {
  287. // To have intersection the d should be positive and the s as well. So the 'a' must be negative and not zero
  288. // because of the division.
  289. const F32 s = -d / a;
  290. ANKI_ASSERT(s > 0.0f);
  291. intersection = ray.getOrigin() + s * ray.getDirection();
  292. intersects = true;
  293. }
  294. return intersects;
  295. }
  296. Bool testCollision(const Plane& plane, const Vec4& vector, Vec4& intersection)
  297. {
  298. ANKI_ASSERT(vector.w() == 0.0f);
  299. const Vec4 pp = vector.getNormalized();
  300. const F32 dot = pp.dot(plane.getNormal());
  301. if(!isZero(dot))
  302. {
  303. const F32 s = plane.getOffset() / dot;
  304. intersection = pp * s;
  305. return true;
  306. }
  307. else
  308. {
  309. return false;
  310. }
  311. }
  312. Bool intersect(const Sphere& sphere, const Ray& ray, Array<Vec4, 2>& intersectionPoints, U& intersectionPointCount)
  313. {
  314. ANKI_ASSERT(isZero(ray.getDirection().getLengthSquared() - 1.0f));
  315. // See https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection
  316. const Vec4& o = ray.getOrigin();
  317. const Vec4& l = ray.getDirection();
  318. const Vec4& c = sphere.getCenter();
  319. const F32 R2 = sphere.getRadius() * sphere.getRadius();
  320. const Vec4 o_c = o - c;
  321. const F32 a = l.dot(o_c);
  322. const F32 b = a * a - o_c.getLengthSquared() + R2;
  323. if(b < 0.0f)
  324. {
  325. intersectionPointCount = 0;
  326. return false;
  327. }
  328. else if(b == 0.0f)
  329. {
  330. intersectionPointCount = 1;
  331. intersectionPoints[0] = -a * l + o;
  332. return true;
  333. }
  334. else
  335. {
  336. F32 d = -a - sqrt(b);
  337. intersectionPointCount = 0;
  338. if(d > 0.0f)
  339. {
  340. intersectionPointCount = 1;
  341. intersectionPoints[0] = d * l + o;
  342. }
  343. d = -a + sqrt(b);
  344. if(d > 0.0f)
  345. {
  346. intersectionPoints[intersectionPointCount++] = d * l + o;
  347. }
  348. return intersectionPointCount > 0;
  349. }
  350. }
  351. } // end namespace anki