FunctionsMisc.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Copyright (C) 2009-present, 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/Sphere.h>
  7. namespace anki {
  8. void extractClipPlane(const Mat4& mvp, FrustumPlaneType id, Plane& plane)
  9. {
  10. // This code extracts the planes assuming the projection matrices are DX-like right-handed (eg D3DXMatrixPerspectiveFovRH)
  11. // See "Fast Extraction of Viewing Frustum Planes from the WorldView-Projection Matrix" paper for more info
  12. #define ANKI_CASE(i, a, op, b) \
  13. case i: \
  14. { \
  15. const Vec4 planeEqationCoefs = mvp.getRow(a) op mvp.getRow(b); \
  16. const Vec4 n = planeEqationCoefs.xyz0(); \
  17. const F32 len = n.getLength(); \
  18. plane = Plane(n / len, -planeEqationCoefs.w() / len); \
  19. break; \
  20. }
  21. switch(id)
  22. {
  23. case FrustumPlaneType::kNear:
  24. {
  25. const Vec4 planeEqationCoefs = mvp.getRow(2);
  26. const Vec4 n = planeEqationCoefs.xyz0();
  27. const F32 len = n.getLength();
  28. plane = Plane(n / len, -planeEqationCoefs.w() / len);
  29. break;
  30. }
  31. ANKI_CASE(FrustumPlaneType::kFar, 3, -, 2)
  32. ANKI_CASE(FrustumPlaneType::kLeft, 3, +, 0)
  33. ANKI_CASE(FrustumPlaneType::kRight, 3, -, 0)
  34. ANKI_CASE(FrustumPlaneType::kTop, 3, -, 1)
  35. ANKI_CASE(FrustumPlaneType::kBottom, 3, +, 1)
  36. default:
  37. ANKI_ASSERT(0);
  38. }
  39. #undef ANKI_CASE
  40. }
  41. void extractClipPlanes(const Mat4& mvp, Array<Plane, 6>& planes)
  42. {
  43. for(U i = 0; i < 6; ++i)
  44. {
  45. extractClipPlane(mvp, static_cast<FrustumPlaneType>(i), planes[i]);
  46. }
  47. }
  48. void computeEdgesOfFrustum(F32 far, F32 fovX, F32 fovY, Vec3 points[4])
  49. {
  50. // This came from unprojecting. It works, don't touch it
  51. const F32 x = far * tan(fovY / 2.0f) * fovX / fovY;
  52. const F32 y = tan(fovY / 2.0f) * far;
  53. const F32 z = -far;
  54. points[0] = Vec3(x, y, z); // top right
  55. points[1] = Vec3(-x, y, z); // top left
  56. points[2] = Vec3(-x, -y, z); // bot left
  57. points[3] = Vec3(x, -y, z); // bot right
  58. }
  59. static Vec4 computeBoundingSphere2(const Vec3 O, const Vec3 A)
  60. {
  61. const Vec3 a = A - O;
  62. const Vec3 o = 0.5f * a;
  63. const F32 radius = o.getLength() + kEpsilonf;
  64. const Vec3 center = O + o;
  65. return Vec4(center, radius);
  66. }
  67. static Vec4 computeBoundingSphere3(const Vec3 O, const Vec3 A, const Vec3 B)
  68. {
  69. const Vec3 a = A - O;
  70. const Vec3 b = B - O;
  71. const Vec3 acrossb = a.cross(b);
  72. const F32 denominator = 2.0f * (acrossb.dot(acrossb));
  73. if(denominator == 0.0f) [[unlikely]]
  74. {
  75. // A pair in A,B,O are the same point or they are in the same line
  76. if(a.getLengthSquared() > b.getLengthSquared())
  77. {
  78. return computeBoundingSphere2(O, A);
  79. }
  80. else
  81. {
  82. return computeBoundingSphere2(O, B);
  83. }
  84. }
  85. Vec3 o = b.dot(b) * acrossb.cross(a);
  86. o += a.dot(a) * b.cross(acrossb);
  87. o /= denominator;
  88. const F32 radius = o.getLength() + kEpsilonf;
  89. const Vec3 center = O + o;
  90. return Vec4(center, radius);
  91. }
  92. Vec4 computeBoundingSphereRecursive(WeakArray<const Vec3*> pPoints, U32 begin, U32 p, U32 b)
  93. {
  94. Vec4 sphere;
  95. switch(b)
  96. {
  97. case 0:
  98. sphere = Vec4(0.0f, 0.0f, 0.0f, 0.0f);
  99. break;
  100. case 1:
  101. sphere = Vec4(*pPoints[begin - 1], kEpsilonf);
  102. break;
  103. case 2:
  104. sphere = computeBoundingSphere2(*pPoints[begin - 1], *pPoints[begin - 2]);
  105. break;
  106. case 3:
  107. sphere = computeBoundingSphere3(*pPoints[begin - 1], *pPoints[begin - 2], *pPoints[begin - 3]);
  108. return sphere;
  109. break;
  110. #if 0
  111. // There is this case as well but it fails if points are coplanar so avoid it
  112. case 4:
  113. {
  114. const Vec3 O = *pPoints[begin - 1];
  115. const Vec3 A = *pPoints[begin - 2];
  116. const Vec3 B = *pPoints[begin - 3];
  117. const Vec3 C = *pPoints[begin - 4];
  118. const Vec3 a = A - O;
  119. const Vec3 b = B - O;
  120. const Vec3 c = C - O;
  121. auto compDet = [](F32 m11, F32 m12, F32 m13, F32 m21, F32 m22, F32 m23, F32 m31, F32 m32, F32 m33) {
  122. return m11 * (m22 * m33 - m32 * m23) - m21 * (m12 * m33 - m32 * m13) + m31 * (m12 * m23 - m22 * m13);
  123. };
  124. const F32 denominator = 2.0f * compDet(a.x(), a.y(), a.z(), b.x(), b.y(), b.z(), c.x(), c.y(), c.z());
  125. Vec3 o = c.dot(c) * a.cross(b);
  126. o += b.dot(b) * c.cross(a);
  127. o += a.dot(a) * b.cross(c);
  128. o /= denominator;
  129. const F32 radius = o.getLength() + kEpsilonf;
  130. const Vec3 center = O + o;
  131. sphere = Vec4(center, radius);
  132. return sphere;
  133. }
  134. #endif
  135. default:
  136. ANKI_ASSERT(0);
  137. }
  138. for(U32 i = 0; i < p; i++)
  139. {
  140. const F32 distSq = (sphere.xyz() - *pPoints[begin + i]).getLengthSquared();
  141. const F32 radiusSq = sphere.w() * sphere.w();
  142. if(distSq > radiusSq)
  143. {
  144. for(U32 j = i; j > 0; j--)
  145. {
  146. const Vec3* T = pPoints[begin + j];
  147. pPoints[begin + j] = pPoints[begin + j - 1];
  148. pPoints[begin + j - 1] = T;
  149. }
  150. sphere = computeBoundingSphereRecursive(pPoints, begin + 1, i, b + 1);
  151. }
  152. }
  153. return sphere;
  154. }
  155. Sphere computeBoundingSphere(const Vec3* firstPoint, U32 pointCount, PtrSize stride)
  156. {
  157. ANKI_ASSERT(firstPoint);
  158. ANKI_ASSERT(pointCount >= 3);
  159. ANKI_ASSERT(stride >= sizeof(Vec3));
  160. DynamicArray<const Vec3*> pPointsDyn;
  161. Array<const Vec3*, 8> pPointsArr;
  162. WeakArray<const Vec3*> pPoints;
  163. if(pointCount > pPointsArr.getSize())
  164. {
  165. pPointsDyn.resize(pointCount);
  166. pPoints = pPointsDyn;
  167. }
  168. else
  169. {
  170. pPoints = {&pPointsArr[0], pointCount};
  171. }
  172. for(U32 i = 0; i < pointCount; ++i)
  173. {
  174. pPoints[i] = reinterpret_cast<const Vec3*>(ptrToNumber(firstPoint) + i * stride);
  175. }
  176. const Vec4 sphere = computeBoundingSphereRecursive(pPoints, 0, pPoints.getSize(), 0);
  177. return Sphere(sphere.xyz(), sphere.w());
  178. }
  179. Aabb computeBoundingAabb(const Vec3* firstPoint, U32 pointCount, PtrSize stride)
  180. {
  181. ANKI_ASSERT(firstPoint);
  182. ANKI_ASSERT(pointCount >= 3);
  183. ANKI_ASSERT(stride >= sizeof(Vec3));
  184. Vec3 min(kMaxF32);
  185. Vec3 max(kMinF32);
  186. for(U32 i = 0; i < pointCount; ++i)
  187. {
  188. const Vec3& point = *reinterpret_cast<const Vec3*>(ptrToNumber(firstPoint) + i * stride);
  189. min = min.min(point);
  190. max = max.max(point);
  191. }
  192. max += 10.0f * kEpsilonf;
  193. return Aabb(min, max);
  194. }
  195. } // end namespace anki