IntrSphere3Cone3.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/FIQuery.h>
  9. #include <Mathematics/TIQuery.h>
  10. #include <Mathematics/Cone.h>
  11. #include <Mathematics/Hypersphere.h>
  12. #include <Mathematics/Vector3.h>
  13. // The test-intersection query is based on the document
  14. // https://www.geometrictools.com/Documentation/IntersectionSphereCone.pdf
  15. //
  16. // The find-intersection returns a single point in the set of intersection
  17. // when that intersection is not empty.
  18. namespace WwiseGTE
  19. {
  20. template <typename Real>
  21. class TIQuery<Real, Sphere3<Real>, Cone3<Real>>
  22. {
  23. public:
  24. struct Result
  25. {
  26. bool intersect;
  27. };
  28. Result operator()(Sphere3<Real> const& sphere, Cone3<Real> const& cone)
  29. {
  30. Result result;
  31. if (cone.GetMinHeight() > (Real)0)
  32. {
  33. if (cone.IsFinite())
  34. {
  35. result.intersect = DoQueryConeFrustum(sphere, cone);
  36. }
  37. else
  38. {
  39. result.intersect = DoQueryInfiniteTruncatedCone(sphere, cone);
  40. }
  41. }
  42. else
  43. {
  44. if (cone.IsFinite())
  45. {
  46. result.intersect = DoQueryFiniteCone(sphere, cone);
  47. }
  48. else
  49. {
  50. result.intersect = DoQueryInfiniteCone(sphere, cone);
  51. }
  52. }
  53. return result;
  54. }
  55. private:
  56. bool DoQueryInfiniteCone(Sphere3<Real> const& sphere, Cone3<Real> const& cone)
  57. {
  58. Vector3<Real> U = cone.ray.origin - (sphere.radius * cone.invSinAngle) * cone.ray.direction;
  59. Vector3<Real> CmU = sphere.center - U;
  60. Real AdCmU = Dot(cone.ray.direction, CmU);
  61. if (AdCmU > (Real)0)
  62. {
  63. Real sqrLengthCmU = Dot(CmU, CmU);
  64. if (AdCmU * AdCmU >= sqrLengthCmU * cone.cosAngleSqr)
  65. {
  66. Vector3<Real> CmV = sphere.center - cone.ray.origin;
  67. Real AdCmV = Dot(cone.ray.direction, CmV);
  68. if (AdCmV < -sphere.radius)
  69. {
  70. return false;
  71. }
  72. Real rSinAngle = sphere.radius * cone.sinAngle;
  73. if (AdCmV >= -rSinAngle)
  74. {
  75. return true;
  76. }
  77. Real sqrLengthCmV = Dot(CmV, CmV);
  78. return sqrLengthCmV <= sphere.radius * sphere.radius;
  79. }
  80. }
  81. return false;
  82. }
  83. bool DoQueryInfiniteTruncatedCone(Sphere3<Real> const& sphere, Cone3<Real> const& cone)
  84. {
  85. Vector3<Real> U = cone.ray.origin - (sphere.radius * cone.invSinAngle) * cone.ray.direction;
  86. Vector3<Real> CmU = sphere.center - U;
  87. Real AdCmU = Dot(cone.ray.direction, CmU);
  88. if (AdCmU > (Real)0)
  89. {
  90. Real sqrLengthCmU = Dot(CmU, CmU);
  91. if (AdCmU * AdCmU >= sqrLengthCmU * cone.cosAngleSqr)
  92. {
  93. Vector3<Real> CmV = sphere.center - cone.ray.origin;
  94. Real AdCmV = Dot(cone.ray.direction, CmV);
  95. Real minHeight = cone.GetMinHeight();
  96. if (AdCmV < minHeight - sphere.radius)
  97. {
  98. return false;
  99. }
  100. Real rSinAngle = sphere.radius * cone.sinAngle;
  101. if (AdCmV >= -rSinAngle)
  102. {
  103. return true;
  104. }
  105. Vector3<Real> D = CmV - minHeight * cone.ray.direction;
  106. Real lengthAxD = Length(Cross(cone.ray.direction, D));
  107. Real hminTanAngle = minHeight * cone.tanAngle;
  108. if (lengthAxD <= hminTanAngle)
  109. {
  110. return true;
  111. }
  112. Real AdD = AdCmV - minHeight;
  113. Real diff = lengthAxD - hminTanAngle;
  114. Real sqrLengthCmK = AdD * AdD + diff * diff;
  115. return sqrLengthCmK <= sphere.radius * sphere.radius;
  116. }
  117. }
  118. return false;
  119. }
  120. bool DoQueryFiniteCone(Sphere3<Real> const& sphere, Cone3<Real> const& cone)
  121. {
  122. Vector3<Real> U = cone.ray.origin - (sphere.radius * cone.invSinAngle) * cone.ray.direction;
  123. Vector3<Real> CmU = sphere.center - U;
  124. Real AdCmU = Dot(cone.ray.direction, CmU);
  125. if (AdCmU > (Real)0)
  126. {
  127. Real sqrLengthCmU = Dot(CmU, CmU);
  128. if (AdCmU * AdCmU >= sqrLengthCmU * cone.cosAngleSqr)
  129. {
  130. Vector3<Real> CmV = sphere.center - cone.ray.origin;
  131. Real AdCmV = Dot(cone.ray.direction, CmV);
  132. if (AdCmV < -sphere.radius)
  133. {
  134. return false;
  135. }
  136. Real maxHeight = cone.GetMaxHeight();
  137. if (AdCmV > cone.GetMaxHeight() + sphere.radius)
  138. {
  139. return false;
  140. }
  141. Real rSinAngle = sphere.radius * cone.sinAngle;
  142. if (AdCmV >= -rSinAngle)
  143. {
  144. if (AdCmV <= maxHeight - rSinAngle)
  145. {
  146. return true;
  147. }
  148. else
  149. {
  150. Vector3<Real> barD = CmV - maxHeight * cone.ray.direction;
  151. Real lengthAxBarD = Length(Cross(cone.ray.direction, barD));
  152. Real hmaxTanAngle = maxHeight * cone.tanAngle;
  153. if (lengthAxBarD <= hmaxTanAngle)
  154. {
  155. return true;
  156. }
  157. Real AdBarD = AdCmV - maxHeight;
  158. Real diff = lengthAxBarD - hmaxTanAngle;
  159. Real sqrLengthCmBarK = AdBarD * AdBarD + diff * diff;
  160. return sqrLengthCmBarK <= sphere.radius * sphere.radius;
  161. }
  162. }
  163. else
  164. {
  165. Real sqrLengthCmV = Dot(CmV, CmV);
  166. return sqrLengthCmV <= sphere.radius * sphere.radius;
  167. }
  168. }
  169. }
  170. return false;
  171. }
  172. bool DoQueryConeFrustum(Sphere3<Real> const& sphere, Cone3<Real> const& cone)
  173. {
  174. Vector3<Real> U = cone.ray.origin - (sphere.radius * cone.invSinAngle) * cone.ray.direction;
  175. Vector3<Real> CmU = sphere.center - U;
  176. Real AdCmU = Dot(cone.ray.direction, CmU);
  177. if (AdCmU > (Real)0)
  178. {
  179. Real sqrLengthCmU = Dot(CmU, CmU);
  180. if (AdCmU * AdCmU >= sqrLengthCmU * cone.cosAngleSqr)
  181. {
  182. Vector3<Real> CmV = sphere.center - cone.ray.origin;
  183. Real AdCmV = Dot(cone.ray.direction, CmV);
  184. Real minHeight = cone.GetMinHeight();
  185. if (AdCmV < minHeight - sphere.radius)
  186. {
  187. return false;
  188. }
  189. Real maxHeight = cone.GetMaxHeight();
  190. if (AdCmV > maxHeight + sphere.radius)
  191. {
  192. return false;
  193. }
  194. Real rSinAngle = sphere.radius * cone.sinAngle;
  195. if (AdCmV >= minHeight - rSinAngle)
  196. {
  197. if (AdCmV <= maxHeight - rSinAngle)
  198. {
  199. return true;
  200. }
  201. else
  202. {
  203. Vector3<Real> barD = CmV - maxHeight * cone.ray.direction;
  204. Real lengthAxBarD = Length(Cross(cone.ray.direction, barD));
  205. Real hmaxTanAngle = maxHeight * cone.tanAngle;
  206. if (lengthAxBarD <= hmaxTanAngle)
  207. {
  208. return true;
  209. }
  210. Real AdBarD = AdCmV - maxHeight;
  211. Real diff = lengthAxBarD - hmaxTanAngle;
  212. Real sqrLengthCmBarK = AdBarD * AdBarD + diff * diff;
  213. return sqrLengthCmBarK <= sphere.radius * sphere.radius;
  214. }
  215. }
  216. else
  217. {
  218. Vector3<Real> D = CmV - minHeight * cone.ray.direction;
  219. Real lengthAxD = Length(Cross(cone.ray.direction, D));
  220. Real hminTanAngle = minHeight * cone.tanAngle;
  221. if (lengthAxD <= hminTanAngle)
  222. {
  223. return true;
  224. }
  225. Real AdD = AdCmV - minHeight;
  226. Real diff = lengthAxD - hminTanAngle;
  227. Real sqrLengthCmK = AdD * AdD + diff * diff;
  228. return sqrLengthCmK <= sphere.radius * sphere.radius;
  229. }
  230. }
  231. }
  232. return false;
  233. }
  234. };
  235. template <typename Real>
  236. class FIQuery<Real, Sphere3<Real>, Cone3<Real>>
  237. {
  238. public:
  239. struct Result
  240. {
  241. // If an intersection occurs, it is potentially an infinite set.
  242. // If the cone vertex is inside the sphere, 'point' is set to the
  243. // cone vertex. If the sphere center is inside the cone, 'point'
  244. // is set to the sphere center. Otherwise, 'point' is set to the
  245. // cone point that is closest to the cone vertex and inside the
  246. // sphere.
  247. bool intersect;
  248. Vector3<Real> point;
  249. };
  250. Result operator()(Sphere3<Real> const& sphere, Cone3<Real> const& cone)
  251. {
  252. Result result;
  253. // Test whether the cone vertex is inside the sphere.
  254. Vector3<Real> diff = sphere.center - cone.ray.origin;
  255. Real rSqr = sphere.radius * sphere.radius;
  256. Real lenSqr = Dot(diff, diff);
  257. if (lenSqr <= rSqr)
  258. {
  259. // The cone vertex is inside the sphere, so the sphere and
  260. // cone intersect.
  261. result.intersect = true;
  262. result.point = cone.ray.origin;
  263. return result;
  264. }
  265. // Test whether the sphere center is inside the cone.
  266. Real dot = Dot(diff, cone.ray.direction);
  267. Real dotSqr = dot * dot;
  268. if (dotSqr >= lenSqr * cone.cosAngleSqr && dot > (Real)0)
  269. {
  270. // The sphere center is inside cone, so the sphere and cone
  271. // intersect.
  272. result.intersect = true;
  273. result.point = sphere.center;
  274. return result;
  275. }
  276. // The sphere center is outside the cone. The problem now reduces
  277. // to computing an intersection between the circle and the ray in
  278. // the plane containing the cone vertex and spanned by the cone
  279. // axis and vector from the cone vertex to the sphere center.
  280. // The ray is parameterized by t * D + V with t >= 0, |D| = 1 and
  281. // dot(A,D) = cos(angle). Also, D = e * A + f * (C - V).
  282. // Substituting the ray equation into the sphere equation yields
  283. // R^2 = |t * D + V - C|^2, so the quadratic for intersections is
  284. // t^2 - 2 * dot(D, C - V) * t + |C - V|^2 - R^2 = 0. An
  285. // intersection occurs if and only if the discriminant is
  286. // nonnegative. This test becomes
  287. // dot(D, C - V)^2 >= dot(C - V, C - V) - R^2
  288. // Note that if the right-hand side is nonpositive, then the
  289. // inequality is true (the sphere contains V). This is already
  290. // ruled out in the first block of code in this function.
  291. Real uLen = std::sqrt(std::max(lenSqr - dotSqr, (Real)0));
  292. Real test = cone.cosAngle * dot + cone.sinAngle * uLen;
  293. Real discr = test * test - lenSqr + rSqr;
  294. if (discr >= (Real)0 && test >= (Real)0)
  295. {
  296. // Compute the point of intersection closest to the cone
  297. // vertex.
  298. result.intersect = true;
  299. Real t = test - std::sqrt(std::max(discr, (Real)0));
  300. Vector3<Real> B = diff - dot * cone.ray.direction;
  301. Real tmp = cone.sinAngle / uLen;
  302. result.point = t * (cone.cosAngle * cone.ray.direction + tmp * B);
  303. }
  304. else
  305. {
  306. result.intersect = false;
  307. }
  308. return result;
  309. }
  310. };
  311. }