ImathFrustumTest.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. ///////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2011-2012, Industrial Light & Magic, a division of Lucas
  4. // Digital Ltd. LLC
  5. //
  6. // All rights reserved.
  7. //
  8. // Redistribution and use in source and binary forms, with or without
  9. // modification, are permitted provided that the following conditions are
  10. // met:
  11. // * Redistributions of source code must retain the above copyright
  12. // notice, this list of conditions and the following disclaimer.
  13. // * Redistributions in binary form must reproduce the above
  14. // copyright notice, this list of conditions and the following disclaimer
  15. // in the documentation and/or other materials provided with the
  16. // distribution.
  17. // * Neither the name of Industrial Light & Magic nor the names of
  18. // its contributors may be used to endorse or promote products derived
  19. // from this software without specific prior written permission.
  20. //
  21. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. //
  33. ///////////////////////////////////////////////////////////////////////////
  34. #ifndef INCLUDED_IMATHFRUSTUMTEST_H
  35. #define INCLUDED_IMATHFRUSTUMTEST_H
  36. //-------------------------------------------------------------------------
  37. //
  38. // This file contains algorithms applied to or in conjunction with
  39. // Frustum visibility testing (Imath::Frustum).
  40. //
  41. // Methods for frustum-based rejection of primitives are contained here.
  42. //
  43. //-------------------------------------------------------------------------
  44. #include "ImathFrustum.h"
  45. #include "ImathBox.h"
  46. #include "ImathSphere.h"
  47. #include "ImathMatrix.h"
  48. #include "ImathVec.h"
  49. #include "ImathNamespace.h"
  50. IMATH_INTERNAL_NAMESPACE_HEADER_ENTER
  51. /////////////////////////////////////////////////////////////////
  52. // FrustumTest
  53. //
  54. // template class FrustumTest<T>
  55. //
  56. // This is a helper class, designed to accelerate the case
  57. // where many tests are made against the same frustum.
  58. // That's a really common case.
  59. //
  60. // The acceleration is achieved by pre-computing the planes of
  61. // the frustum, along with the ablsolute values of the plane normals.
  62. //
  63. //////////////////////////////////////////////////////////////////
  64. // How to use this
  65. //
  66. // Given that you already have:
  67. // Imath::Frustum myFrustum
  68. // Imath::Matrix44 myCameraWorldMatrix
  69. //
  70. // First, make a frustum test object:
  71. // FrustumTest myFrustumTest(myFrustum, myCameraWorldMatrix)
  72. //
  73. // Whenever the camera or frustum changes, call:
  74. // myFrustumTest.setFrustum(myFrustum, myCameraWorldMatrix)
  75. //
  76. // For each object you want to test for visibility, call:
  77. // myFrustumTest.isVisible(myBox)
  78. // myFrustumTest.isVisible(mySphere)
  79. // myFrustumTest.isVisible(myVec3)
  80. // myFrustumTest.completelyContains(myBox)
  81. // myFrustumTest.completelyContains(mySphere)
  82. //
  83. //////////////////////////////////////////////////////////////////
  84. // Explanation of how it works
  85. //
  86. //
  87. // We store six world-space Frustum planes (nx, ny, nz, offset)
  88. //
  89. // Points: To test a Vec3 for visibility, test it against each plane
  90. // using the normal (v dot n - offset) method. (the result is exact)
  91. //
  92. // BBoxes: To test an axis-aligned bbox, test the center against each plane
  93. // using the normal (v dot n - offset) method, but offset by the
  94. // box extents dot the abs of the plane normal. (the result is NOT
  95. // exact, but will not return false-negatives.)
  96. //
  97. // Spheres: To test a sphere, test the center against each plane
  98. // using the normal (v dot n - offset) method, but offset by the
  99. // sphere's radius. (the result is NOT exact, but will not return
  100. // false-negatives.)
  101. //
  102. //
  103. // SPECIAL NOTE: "Where are the dot products?"
  104. // Actual dot products are currently slow for most SIMD architectures.
  105. // In order to keep this code optimization-ready, the dot products
  106. // are all performed using vector adds and multipies.
  107. //
  108. // In order to do this, the plane equations are stored in "transpose"
  109. // form, with the X components grouped into an X vector, etc.
  110. //
  111. template <class T>
  112. class FrustumTest
  113. {
  114. public:
  115. FrustumTest()
  116. {
  117. Frustum<T> frust;
  118. Matrix44<T> cameraMat;
  119. cameraMat.makeIdentity();
  120. setFrustum(frust, cameraMat);
  121. }
  122. FrustumTest(const Frustum<T> &frustum, const Matrix44<T> &cameraMat)
  123. {
  124. setFrustum(frustum, cameraMat);
  125. }
  126. ////////////////////////////////////////////////////////////////////
  127. // setFrustum()
  128. // This updates the frustum test with a new frustum and matrix.
  129. // This should usually be called just once per frame.
  130. void setFrustum(const Frustum<T> &frustum, const Matrix44<T> &cameraMat);
  131. ////////////////////////////////////////////////////////////////////
  132. // isVisible()
  133. // Check to see if shapes are visible.
  134. bool isVisible(const Sphere3<T> &sphere) const;
  135. bool isVisible(const Box<Vec3<T> > &box) const;
  136. bool isVisible(const Vec3<T> &vec) const;
  137. ////////////////////////////////////////////////////////////////////
  138. // completelyContains()
  139. // Check to see if shapes are entirely contained.
  140. bool completelyContains(const Sphere3<T> &sphere) const;
  141. bool completelyContains(const Box<Vec3<T> > &box) const;
  142. // These next items are kept primarily for debugging tools.
  143. // It's useful for drawing the culling environment, and also
  144. // for getting an "outside view" of the culling frustum.
  145. IMATH_INTERNAL_NAMESPACE::Matrix44<T> cameraMat() const {return cameraMatrix;}
  146. IMATH_INTERNAL_NAMESPACE::Frustum<T> currentFrustum() const {return currFrustum;}
  147. protected:
  148. // To understand why the planes are stored this way, see
  149. // the SPECIAL NOTE above.
  150. Vec3<T> planeNormX[2]; // The X compunents from 6 plane equations
  151. Vec3<T> planeNormY[2]; // The Y compunents from 6 plane equations
  152. Vec3<T> planeNormZ[2]; // The Z compunents from 6 plane equations
  153. Vec3<T> planeOffsetVec[2]; // The distance offsets from 6 plane equations
  154. // The absolute values are stored to assist with bounding box tests.
  155. Vec3<T> planeNormAbsX[2]; // The abs(X) compunents from 6 plane equations
  156. Vec3<T> planeNormAbsY[2]; // The abs(X) compunents from 6 plane equations
  157. Vec3<T> planeNormAbsZ[2]; // The abs(X) compunents from 6 plane equations
  158. // These are kept primarily for debugging tools.
  159. Frustum<T> currFrustum;
  160. Matrix44<T> cameraMatrix;
  161. };
  162. ////////////////////////////////////////////////////////////////////
  163. // setFrustum()
  164. // This should usually only be called once per frame, or however
  165. // often the camera moves.
  166. template<class T>
  167. void FrustumTest<T>::setFrustum(const Frustum<T> &frustum,
  168. const Matrix44<T> &cameraMat)
  169. {
  170. Plane3<T> frustumPlanes[6];
  171. frustum.planes(frustumPlanes, cameraMat);
  172. // Here's where we effectively transpose the plane equations.
  173. // We stuff all six X's into the two planeNormX vectors, etc.
  174. for (int i = 0; i < 2; ++i)
  175. {
  176. int index = i * 3;
  177. planeNormX[i] = Vec3<T>(frustumPlanes[index + 0].normal.x,
  178. frustumPlanes[index + 1].normal.x,
  179. frustumPlanes[index + 2].normal.x);
  180. planeNormY[i] = Vec3<T>(frustumPlanes[index + 0].normal.y,
  181. frustumPlanes[index + 1].normal.y,
  182. frustumPlanes[index + 2].normal.y);
  183. planeNormZ[i] = Vec3<T>(frustumPlanes[index + 0].normal.z,
  184. frustumPlanes[index + 1].normal.z,
  185. frustumPlanes[index + 2].normal.z);
  186. planeNormAbsX[i] = Vec3<T>(IMATH_INTERNAL_NAMESPACE::abs(planeNormX[i].x),
  187. IMATH_INTERNAL_NAMESPACE::abs(planeNormX[i].y),
  188. IMATH_INTERNAL_NAMESPACE::abs(planeNormX[i].z));
  189. planeNormAbsY[i] = Vec3<T>(IMATH_INTERNAL_NAMESPACE::abs(planeNormY[i].x),
  190. IMATH_INTERNAL_NAMESPACE::abs(planeNormY[i].y),
  191. IMATH_INTERNAL_NAMESPACE::abs(planeNormY[i].z));
  192. planeNormAbsZ[i] = Vec3<T>(IMATH_INTERNAL_NAMESPACE::abs(planeNormZ[i].x),
  193. IMATH_INTERNAL_NAMESPACE::abs(planeNormZ[i].y),
  194. IMATH_INTERNAL_NAMESPACE::abs(planeNormZ[i].z));
  195. planeOffsetVec[i] = Vec3<T>(frustumPlanes[index + 0].distance,
  196. frustumPlanes[index + 1].distance,
  197. frustumPlanes[index + 2].distance);
  198. }
  199. currFrustum = frustum;
  200. cameraMatrix = cameraMat;
  201. }
  202. ////////////////////////////////////////////////////////////////////
  203. // isVisible(Sphere)
  204. // Returns true if any part of the sphere is inside
  205. // the frustum.
  206. // The result MAY return close false-positives, but not false-negatives.
  207. //
  208. template<typename T>
  209. bool FrustumTest<T>::isVisible(const Sphere3<T> &sphere) const
  210. {
  211. Vec3<T> center = sphere.center;
  212. Vec3<T> radiusVec = Vec3<T>(sphere.radius, sphere.radius, sphere.radius);
  213. // This is a vertical dot-product on three vectors at once.
  214. Vec3<T> d0 = planeNormX[0] * center.x
  215. + planeNormY[0] * center.y
  216. + planeNormZ[0] * center.z
  217. - radiusVec
  218. - planeOffsetVec[0];
  219. if (d0.x >= 0 || d0.y >= 0 || d0.z >= 0)
  220. return false;
  221. Vec3<T> d1 = planeNormX[1] * center.x
  222. + planeNormY[1] * center.y
  223. + planeNormZ[1] * center.z
  224. - radiusVec
  225. - planeOffsetVec[1];
  226. if (d1.x >= 0 || d1.y >= 0 || d1.z >= 0)
  227. return false;
  228. return true;
  229. }
  230. ////////////////////////////////////////////////////////////////////
  231. // completelyContains(Sphere)
  232. // Returns true if every part of the sphere is inside
  233. // the frustum.
  234. // The result MAY return close false-negatives, but not false-positives.
  235. //
  236. template<typename T>
  237. bool FrustumTest<T>::completelyContains(const Sphere3<T> &sphere) const
  238. {
  239. Vec3<T> center = sphere.center;
  240. Vec3<T> radiusVec = Vec3<T>(sphere.radius, sphere.radius, sphere.radius);
  241. // This is a vertical dot-product on three vectors at once.
  242. Vec3<T> d0 = planeNormX[0] * center.x
  243. + planeNormY[0] * center.y
  244. + planeNormZ[0] * center.z
  245. + radiusVec
  246. - planeOffsetVec[0];
  247. if (d0.x >= 0 || d0.y >= 0 || d0.z >= 0)
  248. return false;
  249. Vec3<T> d1 = planeNormX[1] * center.x
  250. + planeNormY[1] * center.y
  251. + planeNormZ[1] * center.z
  252. + radiusVec
  253. - planeOffsetVec[1];
  254. if (d1.x >= 0 || d1.y >= 0 || d1.z >= 0)
  255. return false;
  256. return true;
  257. }
  258. ////////////////////////////////////////////////////////////////////
  259. // isVisible(Box)
  260. // Returns true if any part of the axis-aligned box
  261. // is inside the frustum.
  262. // The result MAY return close false-positives, but not false-negatives.
  263. //
  264. template<typename T>
  265. bool FrustumTest<T>::isVisible(const Box<Vec3<T> > &box) const
  266. {
  267. if (box.isEmpty())
  268. return false;
  269. Vec3<T> center = (box.min + box.max) / 2;
  270. Vec3<T> extent = (box.max - center);
  271. // This is a vertical dot-product on three vectors at once.
  272. Vec3<T> d0 = planeNormX[0] * center.x
  273. + planeNormY[0] * center.y
  274. + planeNormZ[0] * center.z
  275. - planeNormAbsX[0] * extent.x
  276. - planeNormAbsY[0] * extent.y
  277. - planeNormAbsZ[0] * extent.z
  278. - planeOffsetVec[0];
  279. if (d0.x >= 0 || d0.y >= 0 || d0.z >= 0)
  280. return false;
  281. Vec3<T> d1 = planeNormX[1] * center.x
  282. + planeNormY[1] * center.y
  283. + planeNormZ[1] * center.z
  284. - planeNormAbsX[1] * extent.x
  285. - planeNormAbsY[1] * extent.y
  286. - planeNormAbsZ[1] * extent.z
  287. - planeOffsetVec[1];
  288. if (d1.x >= 0 || d1.y >= 0 || d1.z >= 0)
  289. return false;
  290. return true;
  291. }
  292. ////////////////////////////////////////////////////////////////////
  293. // completelyContains(Box)
  294. // Returns true if every part of the axis-aligned box
  295. // is inside the frustum.
  296. // The result MAY return close false-negatives, but not false-positives.
  297. //
  298. template<typename T>
  299. bool FrustumTest<T>::completelyContains(const Box<Vec3<T> > &box) const
  300. {
  301. if (box.isEmpty())
  302. return false;
  303. Vec3<T> center = (box.min + box.max) / 2;
  304. Vec3<T> extent = (box.max - center);
  305. // This is a vertical dot-product on three vectors at once.
  306. Vec3<T> d0 = planeNormX[0] * center.x
  307. + planeNormY[0] * center.y
  308. + planeNormZ[0] * center.z
  309. + planeNormAbsX[0] * extent.x
  310. + planeNormAbsY[0] * extent.y
  311. + planeNormAbsZ[0] * extent.z
  312. - planeOffsetVec[0];
  313. if (d0.x >= 0 || d0.y >= 0 || d0.z >= 0)
  314. return false;
  315. Vec3<T> d1 = planeNormX[1] * center.x
  316. + planeNormY[1] * center.y
  317. + planeNormZ[1] * center.z
  318. + planeNormAbsX[1] * extent.x
  319. + planeNormAbsY[1] * extent.y
  320. + planeNormAbsZ[1] * extent.z
  321. - planeOffsetVec[1];
  322. if (d1.x >= 0 || d1.y >= 0 || d1.z >= 0)
  323. return false;
  324. return true;
  325. }
  326. ////////////////////////////////////////////////////////////////////
  327. // isVisible(Vec3)
  328. // Returns true if the point is inside the frustum.
  329. //
  330. template<typename T>
  331. bool FrustumTest<T>::isVisible(const Vec3<T> &vec) const
  332. {
  333. // This is a vertical dot-product on three vectors at once.
  334. Vec3<T> d0 = (planeNormX[0] * vec.x)
  335. + (planeNormY[0] * vec.y)
  336. + (planeNormZ[0] * vec.z)
  337. - planeOffsetVec[0];
  338. if (d0.x >= 0 || d0.y >= 0 || d0.z >= 0)
  339. return false;
  340. Vec3<T> d1 = (planeNormX[1] * vec.x)
  341. + (planeNormY[1] * vec.y)
  342. + (planeNormZ[1] * vec.z)
  343. - planeOffsetVec[1];
  344. if (d1.x >= 0 || d1.y >= 0 || d1.z >= 0)
  345. return false;
  346. return true;
  347. }
  348. typedef FrustumTest<float> FrustumTestf;
  349. typedef FrustumTest<double> FrustumTestd;
  350. IMATH_INTERNAL_NAMESPACE_HEADER_EXIT
  351. #endif // INCLUDED_IMATHFRUSTUMTEST_H