Collision.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. // ----------------------------------------------------------------
  2. // From Game Programming in C++ by Sanjay Madhav
  3. // Copyright (C) 2017 Sanjay Madhav. All rights reserved.
  4. //
  5. // Released under the BSD License
  6. // See LICENSE in root directory for full details.
  7. // ----------------------------------------------------------------
  8. // MinDistSq between two line segments:
  9. // Copyright 2001 softSurfer, 2012 Dan Sunday
  10. // This code may be freely used, distributed and modified for any purpose
  11. // providing that this copyright notice is included with it.
  12. // SoftSurfer makes no warranty for this code, and cannot be held
  13. // liable for any real or imagined damage resulting from its use.
  14. // Users of this code must verify correctness for their application.
  15. // ----------------------------------------------------------------
  16. #include "Collision.h"
  17. #include <algorithm>
  18. #include <array>
  19. LineSegment::LineSegment(const Vector3& start, const Vector3& end)
  20. :mStart(start)
  21. ,mEnd(end)
  22. {
  23. }
  24. Vector3 LineSegment::PointOnSegment(float t) const
  25. {
  26. return mStart + (mEnd - mStart) * t;
  27. }
  28. float LineSegment::MinDistSq(const Vector3& point) const
  29. {
  30. // Construct vectors
  31. Vector3 ab = mEnd - mStart;
  32. Vector3 ba = -1.0f * ab;
  33. Vector3 ac = point - mStart;
  34. Vector3 bc = point - mEnd;
  35. // Case 1: C projects prior to A
  36. if (Vector3::Dot(ab, ac) < 0.0f)
  37. {
  38. return ac.LengthSq();
  39. }
  40. // Case 2: C projects after B
  41. else if (Vector3::Dot(ba, bc) < 0.0f)
  42. {
  43. return bc.LengthSq();
  44. }
  45. // Case 3: C projects onto line
  46. else
  47. {
  48. // Compute p
  49. float scalar = Vector3::Dot(ac, ab)
  50. / Vector3::Dot(ab, ab);
  51. Vector3 p = scalar * ab;
  52. // Compute length squared of ac - p
  53. return (ac - p).LengthSq();
  54. }
  55. }
  56. float LineSegment::MinDistSq(const LineSegment & s1, const LineSegment & s2)
  57. {
  58. Vector3 u = s1.mEnd - s1.mStart;
  59. Vector3 v = s2.mEnd - s2.mStart;
  60. Vector3 w = s1.mStart - s2.mStart;
  61. float a = Vector3::Dot(u, u); // always >= 0
  62. float b = Vector3::Dot(u, v);
  63. float c = Vector3::Dot(v, v); // always >= 0
  64. float d = Vector3::Dot(u, w);
  65. float e = Vector3::Dot(v, w);
  66. float D = a*c - b*b; // always >= 0
  67. float sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0
  68. float tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0
  69. // compute the line parameters of the two closest points
  70. if (Math::NearZero(D)) { // the lines are almost parallel
  71. sN = 0.0; // force using point P0 on segment S1
  72. sD = 1.0; // to prevent possible division by 0.0 later
  73. tN = e;
  74. tD = c;
  75. }
  76. else { // get the closest points on the infinite lines
  77. sN = (b*e - c*d);
  78. tN = (a*e - b*d);
  79. if (sN < 0.0) { // sc < 0 => the s=0 edge is visible
  80. sN = 0.0;
  81. tN = e;
  82. tD = c;
  83. }
  84. else if (sN > sD) { // sc > 1 => the s=1 edge is visible
  85. sN = sD;
  86. tN = e + b;
  87. tD = c;
  88. }
  89. }
  90. if (tN < 0.0) { // tc < 0 => the t=0 edge is visible
  91. tN = 0.0;
  92. // recompute sc for this edge
  93. if (-d < 0.0)
  94. sN = 0.0;
  95. else if (-d > a)
  96. sN = sD;
  97. else {
  98. sN = -d;
  99. sD = a;
  100. }
  101. }
  102. else if (tN > tD) { // tc > 1 => the t=1 edge is visible
  103. tN = tD;
  104. // recompute sc for this edge
  105. if ((-d + b) < 0.0)
  106. sN = 0;
  107. else if ((-d + b) > a)
  108. sN = sD;
  109. else {
  110. sN = (-d + b);
  111. sD = a;
  112. }
  113. }
  114. // finally do the division to get sc and tc
  115. sc = (Math::NearZero(sN) ? 0.0f : sN / sD);
  116. tc = (Math::NearZero(tN) ? 0.0f : tN / tD);
  117. // get the difference of the two closest points
  118. Vector3 dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc)
  119. return dP.LengthSq(); // return the closest distance squared
  120. }
  121. Plane::Plane(const Vector3& normal, float d)
  122. :mNormal(normal)
  123. ,mD(d)
  124. {
  125. }
  126. Plane::Plane(const Vector3& a, const Vector3& b, const Vector3& c)
  127. {
  128. // Compute vectors from a to b and a to c
  129. Vector3 ab = b - a;
  130. Vector3 ac = c - a;
  131. // Cross product and normalize to get normal
  132. mNormal = Vector3::Cross(ab, ac);
  133. mNormal.Normalize();
  134. // d = -P dot n
  135. mD = -Vector3::Dot(a, mNormal);
  136. }
  137. float Plane::SignedDist(const Vector3& point) const
  138. {
  139. return Vector3::Dot(point, mNormal) - mD;
  140. }
  141. Sphere::Sphere(const Vector3& center, float radius)
  142. :mCenter(center)
  143. , mRadius(radius)
  144. {
  145. }
  146. bool Sphere::Contains(const Vector3& point) const
  147. {
  148. // Get distance squared between center and point
  149. float distSq = (mCenter - point).LengthSq();
  150. return distSq <= (mRadius * mRadius);
  151. }
  152. AABB::AABB(const Vector3& min, const Vector3& max)
  153. : mMin(min)
  154. , mMax(max)
  155. {
  156. }
  157. void AABB::UpdateMinMax(const Vector3& point)
  158. {
  159. // Update each component separately
  160. mMin.x = Math::Min(mMin.x, point.x);
  161. mMin.y = Math::Min(mMin.y, point.y);
  162. mMin.z = Math::Min(mMin.z, point.z);
  163. mMax.x = Math::Max(mMax.x, point.x);
  164. mMax.y = Math::Max(mMax.y, point.y);
  165. mMax.z = Math::Max(mMax.z, point.z);
  166. }
  167. void AABB::Rotate(const Quaternion& q)
  168. {
  169. // Construct the 8 points for the corners of the box
  170. std::array<Vector3, 8> points;
  171. // Min point is always a corner
  172. points[0] = mMin;
  173. // Permutations with 2 min and 1 max
  174. points[1] = Vector3(mMax.x, mMin.y, mMin.z);
  175. points[2] = Vector3(mMin.x, mMax.y, mMin.z);
  176. points[3] = Vector3(mMin.x, mMin.y, mMax.z);
  177. // Permutations with 2 max and 1 min
  178. points[4] = Vector3(mMin.x, mMax.y, mMax.z);
  179. points[5] = Vector3(mMax.x, mMin.y, mMax.z);
  180. points[6] = Vector3(mMax.x, mMax.y, mMin.z);
  181. // Max point corner
  182. points[7] = Vector3(mMax);
  183. // Rotate first point
  184. Vector3 p = Vector3::Transform(points[0], q);
  185. // Reset min/max to first point rotated
  186. mMin = p;
  187. mMax = p;
  188. // Update min/max based on remaining points, rotated
  189. for (size_t i = 1; i < points.size(); i++)
  190. {
  191. p = Vector3::Transform(points[i], q);
  192. UpdateMinMax(p);
  193. }
  194. }
  195. bool AABB::Contains(const Vector3& point) const
  196. {
  197. bool outside = point.x < mMin.x ||
  198. point.y < mMin.y ||
  199. point.z < mMin.z ||
  200. point.x > mMax.x ||
  201. point.y > mMax.y ||
  202. point.z > mMax.z;
  203. // If none of these are true, the point is inside the box
  204. return !outside;
  205. }
  206. float AABB::MinDistSq(const Vector3& point) const
  207. {
  208. // Compute differences for each axis
  209. float dx = Math::Max(mMin.x - point.x, 0.0f);
  210. dx = Math::Max(dx, point.x - mMax.x);
  211. float dy = Math::Max(mMin.y - point.y, 0.0f);
  212. dy = Math::Max(dy, point.y - mMax.y);
  213. float dz = Math::Max(mMin.z - point.z, 0.0f);
  214. dz = Math::Max(dy, point.z - mMax.z);
  215. // Distance squared formula
  216. return dx * dx + dy * dy + dz * dz;
  217. }
  218. Capsule::Capsule(const Vector3& start, const Vector3& end, float radius)
  219. :mSegment(start, end)
  220. , mRadius(radius)
  221. {
  222. }
  223. Vector3 Capsule::PointOnSegment(float t) const
  224. {
  225. return mSegment.PointOnSegment(t);
  226. }
  227. bool Capsule::Contains(const Vector3& point) const
  228. {
  229. // Get minimal dist. sq. between point and line segment
  230. float distSq = mSegment.MinDistSq(point);
  231. return distSq <= (mRadius * mRadius);
  232. }
  233. bool ConvexPolygon::Contains(const Vector2& point) const
  234. {
  235. float sum = 0.0f;
  236. Vector2 a, b;
  237. for (size_t i = 0; i < mVertices.size() - 1; i++)
  238. {
  239. // From point to first vertex
  240. a = mVertices[i] - point;
  241. a.Normalize();
  242. // From point to second vertex
  243. b = mVertices[i + 1] - point;
  244. b.Normalize();
  245. // Add angle to sum
  246. sum += Math::Acos(Vector2::Dot(a, b));
  247. }
  248. // Have to add angle for last vertex and first vertex
  249. a = mVertices.back() - point;
  250. a.Normalize();
  251. b = mVertices.front() - point;
  252. b.Normalize();
  253. sum += Math::Acos(Vector2::Dot(a, b));
  254. // Return true if approximately 2pi
  255. return Math::NearZero(sum - Math::TwoPi);
  256. }
  257. bool Intersect(const Sphere& a, const Sphere& b)
  258. {
  259. float distSq = (a.mCenter - b.mCenter).LengthSq();
  260. float sumRadii = a.mRadius + b.mRadius;
  261. return distSq <= (sumRadii * sumRadii);
  262. }
  263. bool Intersect(const AABB& a, const AABB& b)
  264. {
  265. bool no = a.mMax.x < b.mMin.x ||
  266. a.mMax.y < b.mMin.y ||
  267. a.mMax.z < b.mMin.z ||
  268. b.mMax.x < a.mMin.x ||
  269. b.mMax.y < a.mMin.y ||
  270. b.mMax.z < a.mMin.z;
  271. // If none of these are true, they must intersect
  272. return !no;
  273. }
  274. bool Intersect(const Capsule& a, const Capsule& b)
  275. {
  276. float distSq = LineSegment::MinDistSq(a.mSegment,
  277. b.mSegment);
  278. float sumRadii = a.mRadius + b.mRadius;
  279. return distSq <= (sumRadii * sumRadii);
  280. }
  281. bool Intersect(const Sphere& s, const AABB& box)
  282. {
  283. float distSq = box.MinDistSq(s.mCenter);
  284. return distSq <= (s.mRadius * s.mRadius);
  285. }
  286. bool Intersect(const LineSegment& l, const Sphere& s, float& outT)
  287. {
  288. // Compute X, Y, a, b, c as per equations
  289. Vector3 X = l.mStart - s.mCenter;
  290. Vector3 Y = l.mEnd - l.mStart;
  291. float a = Vector3::Dot(Y, Y);
  292. float b = 2.0f * Vector3::Dot(X, Y);
  293. float c = Vector3::Dot(X, X) - s.mRadius * s.mRadius;
  294. // Compute discriminant
  295. float disc = b * b - 4.0f * a * c;
  296. if (disc < 0.0f)
  297. {
  298. return false;
  299. }
  300. else
  301. {
  302. disc = Math::Sqrt(disc);
  303. // Compute min and max solutions of t
  304. float tMin = (-b - disc) / (2.0f * a);
  305. float tMax = (-b + disc) / (2.0f * a);
  306. // Check whether either t is within bounds of segment
  307. if (tMin >= 0.0f && tMin <= 1.0f)
  308. {
  309. outT = tMin;
  310. return true;
  311. }
  312. else if (tMax >= 0.0f && tMax <= 1.0f)
  313. {
  314. outT = tMax;
  315. return true;
  316. }
  317. else
  318. {
  319. return false;
  320. }
  321. }
  322. }
  323. bool Intersect(const LineSegment& l, const Plane& p, float& outT)
  324. {
  325. // First test if there's a solution for t
  326. float denom = Vector3::Dot(l.mEnd - l.mStart,
  327. p.mNormal);
  328. if (Math::NearZero(denom))
  329. {
  330. // The only way they intersect is if start
  331. // is a point on the plane (P dot N) == d
  332. if (Math::NearZero(Vector3::Dot(l.mStart, p.mNormal)
  333. - p.mD))
  334. {
  335. return true;
  336. }
  337. else
  338. {
  339. return false;
  340. }
  341. }
  342. else
  343. {
  344. float numer = -Vector3::Dot(l.mStart, p.mNormal) -
  345. p.mD;
  346. outT = numer / denom;
  347. // Validate t is within bounds of the line segment
  348. if (outT >= 0.0f && outT <= 1.0f)
  349. {
  350. return true;
  351. }
  352. else
  353. {
  354. return false;
  355. }
  356. }
  357. }
  358. bool TestSidePlane(float start, float end, float negd, const Vector3& norm,
  359. std::vector<std::pair<float, Vector3>>& out)
  360. {
  361. float denom = end - start;
  362. if (Math::NearZero(denom))
  363. {
  364. return false;
  365. }
  366. else
  367. {
  368. float numer = -start + negd;
  369. float t = numer / denom;
  370. // Test that t is within bounds
  371. if (t >= 0.0f && t <= 1.0f)
  372. {
  373. out.emplace_back(t, norm);
  374. return true;
  375. }
  376. else
  377. {
  378. return false;
  379. }
  380. }
  381. }
  382. bool Intersect(const LineSegment& l, const AABB& b, float& outT,
  383. Vector3& outNorm)
  384. {
  385. // Vector to save all possible t values, and normals for those sides
  386. std::vector<std::pair<float, Vector3>> tValues;
  387. // Test the x planes
  388. TestSidePlane(l.mStart.x, l.mEnd.x, b.mMin.x, Vector3::NegUnitX,
  389. tValues);
  390. TestSidePlane(l.mStart.x, l.mEnd.x, b.mMax.x, Vector3::UnitX,
  391. tValues);
  392. // Test the y planes
  393. TestSidePlane(l.mStart.y, l.mEnd.y, b.mMin.y, Vector3::NegUnitY,
  394. tValues);
  395. TestSidePlane(l.mStart.y, l.mEnd.y, b.mMax.y, Vector3::UnitY,
  396. tValues);
  397. // Test the z planes
  398. TestSidePlane(l.mStart.z, l.mEnd.z, b.mMin.z, Vector3::NegUnitZ,
  399. tValues);
  400. TestSidePlane(l.mStart.z, l.mEnd.z, b.mMax.z, Vector3::UnitZ,
  401. tValues);
  402. // Sort the t values in ascending order
  403. std::sort(tValues.begin(), tValues.end(), [](
  404. const std::pair<float, Vector3>& a,
  405. const std::pair<float, Vector3>& b) {
  406. return a.first < b.first;
  407. });
  408. // Test if the box contains any of these points of intersection
  409. Vector3 point;
  410. for (auto& t : tValues)
  411. {
  412. point = l.PointOnSegment(t.first);
  413. if (b.Contains(point))
  414. {
  415. outT = t.first;
  416. outNorm = t.second;
  417. return true;
  418. }
  419. }
  420. //None of the intersections are within bounds of box
  421. return false;
  422. }
  423. bool SweptSphere(const Sphere& P0, const Sphere& P1,
  424. const Sphere& Q0, const Sphere& Q1, float& outT)
  425. {
  426. // Compute X, Y, a, b, and c
  427. Vector3 X = P0.mCenter - Q0.mCenter;
  428. Vector3 Y = P1.mCenter - P0.mCenter -
  429. (Q1.mCenter - Q0.mCenter);
  430. float a = Vector3::Dot(Y, Y);
  431. float b = 2.0f * Vector3::Dot(X, Y);
  432. float sumRadii = P0.mRadius + Q0.mRadius;
  433. float c = Vector3::Dot(X, X) - sumRadii * sumRadii;
  434. // Solve discriminant
  435. float disc = b * b - 4.0f * a * c;
  436. if (disc < 0.0f)
  437. {
  438. return false;
  439. }
  440. else
  441. {
  442. disc = Math::Sqrt(disc);
  443. // We only care about the smaller solution
  444. outT = (-b - disc) / (2.0f * a);
  445. if (outT >= 0.0f && outT <= 0.0f)
  446. {
  447. return true;
  448. }
  449. else
  450. {
  451. return false;
  452. }
  453. }
  454. }