mPlaneSet.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _MPLANESET_H_
  23. #define _MPLANESET_H_
  24. #ifndef _MPLANE_H_
  25. #include "math/mPlane.h"
  26. #endif
  27. #ifndef _MBOX_H_
  28. #include "math/mBox.h"
  29. #endif
  30. #ifndef _MSPHERE_H_
  31. #include "math/mSphere.h"
  32. #endif
  33. #ifndef _MORIENTEDBOX_H_
  34. #include "math/mOrientedBox.h"
  35. #endif
  36. #ifndef _TEMPALLOC_H_
  37. #include "util/tempAlloc.h"
  38. #endif
  39. #ifndef _TALGORITHM_H_
  40. #include "core/tAlgorithm.h"
  41. #endif
  42. /// Set of planes which can be tested against bounding volumes.
  43. ///
  44. /// This class is meant as a helper to collect functionality for working with sets
  45. /// of planes. As a helper, it does not define means to manage the data it uses.
  46. ///
  47. /// @warn This class does not manage the memory needed for the set.
  48. template< typename T >
  49. class PlaneSet
  50. {
  51. protected:
  52. /// Number of planes in #mPlanes.
  53. U32 mNumPlanes;
  54. /// Set of planes. The memory for this array is managed outside
  55. /// this class.
  56. const T* mPlanes;
  57. template< typename P > OverlapTestResult _testOverlap( const P& bounds ) const;
  58. template< typename P > bool _isContained( const P& bounds ) const;
  59. public:
  60. /// @name Constructors
  61. /// @{
  62. /// Create an uninitialized set.
  63. /// @warn None of the members will be initialized.
  64. PlaneSet() :mNumPlanes(0), mPlanes(NULL) {}
  65. /// Use the given set of planes to initialize the set.
  66. ///
  67. /// @param planes The planes. Memory must be valid for the entire
  68. /// lifetime of the plane set.
  69. /// @param numPlanes Number of planes.
  70. PlaneSet( const T* planes, U32 numPlanes )
  71. : mNumPlanes( numPlanes ), mPlanes( planes ) {}
  72. /// @}
  73. /// @name Accessors
  74. /// @{
  75. /// Return the number of planes that this set consists of.
  76. U32 getNumPlanes() const { return mNumPlanes; }
  77. /// Return the array of planes for this set.
  78. const T* getPlanes() const { return mPlanes; }
  79. /// @}
  80. /// @name Intersection
  81. /// All of these intersection methods are approximate in that they can produce
  82. /// false positives on GeometryIntersecting. For precise results, use Intersector.
  83. /// @{
  84. /// Test intersection of the given AABB with the volume defined by the plane set.
  85. ///
  86. /// @param aabb Axis-aligned bounding box.
  87. /// @return The type of overlap that the given AABB has with the plane set.
  88. ///
  89. /// @note The method will not test for those edge cases where none of the planes
  90. /// rejects the given AABB yet the AABB is actually outside the volume defined by the planes.
  91. /// This speeds up the test at the cost of losing precision which is acceptable for
  92. /// cases where doing the additional tests for all intersecting objects will generally
  93. /// waste more time than accepting a few additional non-intersecting objects as
  94. /// intersecting.
  95. OverlapTestResult testPotentialIntersection( const Box3F& aabb ) const
  96. {
  97. return _testOverlap( aabb );
  98. }
  99. /// Test intersection of the given sphere with the volume defined by the plane set.
  100. ///
  101. /// @param sphere Sphere.
  102. /// @return The type of overlap that the given sphere has with the plane set.
  103. ///
  104. /// @note The method will not test for those edge cases where none of the planes
  105. /// rejects the given sphere yet the sphere is actually outside the volume defined by the planes.
  106. /// This speeds up the test at the cost of losing precision which is acceptable for
  107. /// cases where doing the additional tests for all intersecting objects will generally
  108. /// waste more time than accepting a few additional non-intersecting objects as
  109. /// intersecting.
  110. OverlapTestResult testPotentialIntersection( const SphereF& sphere ) const
  111. {
  112. return _testOverlap( sphere );
  113. }
  114. /// Test intersection of the given OBB with the volume defined by the plane set.
  115. ///
  116. /// @param obb Oriented bounding box.
  117. /// @return The type of overlap that the given OBB has with the plane set.
  118. ///
  119. /// @note The method will not test for those edge cases where none of the planes
  120. /// rejects the given OBB yet the OBB is actually outside the volume defined by the planes.
  121. /// This speeds up the test at the cost of losing precision which is acceptable for
  122. /// cases where doing the additional tests for all intersecting objects will generally
  123. /// waste more time than accepting a few additional non-intersecting objects as
  124. /// intersecting.
  125. OverlapTestResult testPotentialIntersection( const OrientedBox3F& obb ) const
  126. {
  127. return _testOverlap( obb );
  128. }
  129. /// Returns a bitmask of which planes are hit by the given box.
  130. U32 testPlanes( const Box3F& bounds, U32 planeMask = 0xFFFFFFFF, F32 expand = 0.0f ) const;
  131. /// @}
  132. /// @name Containment
  133. /// Testing for containment of geometric shapes within the volume enclosed by the planes.
  134. /// @{
  135. /// Return true if the given point lies on the positive side of all the planes
  136. /// in the set.
  137. bool isContained( const Point3F& point, F32 epsilon = __EQUAL_CONST_F ) const;
  138. /// Return true if all of the given points lie on the positive side of all the planes
  139. /// in the set.
  140. bool isContained( const Point3F* points, U32 numPoints ) const;
  141. /// Return true if the given AABB lies on the positive side of all the planes
  142. /// in the set.
  143. bool isContained( const Box3F& aabb ) const { return _isContained( aabb ); }
  144. /// Return true if the given sphere lies on the positive side of all the planes
  145. /// in the set.
  146. bool isContained( const SphereF& sphere ) const { return _isContained( sphere ); }
  147. /// Return true if the given OBB lies on the positive side of all the planes
  148. /// in the set.
  149. bool isContained( const OrientedBox3F& obb ) const { return _isContained( obb ); }
  150. /// @}
  151. /// @name Clipping
  152. /// @{
  153. /// Clip the given line segment against the plane set. If the segment
  154. /// intersects with any of the planes, the points will be clipped at the
  155. /// intersection.
  156. ///
  157. /// @return True if any part of the segment is inside the volume defined by the plane set.
  158. bool clipSegment( Point3F& pnt0, Point3F& pnt1 ) const;
  159. /// Clip a convex polygon by all planes in the set.
  160. ///
  161. /// @param inVertices Array holding the vertices of the input polygon.
  162. /// @param inNumVertices Number of vertices in @a inVertices.
  163. /// @param outVertices Array to hold the vertices of the clipped polygon. Must have spaces for
  164. /// @a inNumVertices + numberOfPlanesInSet. Must not be the same as @a inVertices.
  165. /// @param maxOutVertices Maximum number of vertices that can be stored in @a outVertices. If insufficient to
  166. /// store the clipped polygon, the return value will be 0.
  167. ///
  168. /// @return Number of vertices in the clipped polygon, i.e. number of vertices stored in @a outVertices.
  169. ///
  170. /// @note Be aware that if the polygon fully lies on the negative side of all planes,
  171. /// the resulting @a outNumVertices will be zero, i.e. no polygon will result from the clip.
  172. U32 clipPolygon( const Point3F* inVertices, U32 inNumVertices, Point3F* outVertices, U32 maxOutVertices ) const;
  173. /// @}
  174. };
  175. typedef PlaneSet< PlaneF > PlaneSetF;
  176. typedef PlaneSet< PlaneD > PlaneSetD;
  177. //-----------------------------------------------------------------------------
  178. template< typename T >
  179. template< typename P >
  180. inline OverlapTestResult PlaneSet< T >::_testOverlap( const P& bounds ) const
  181. {
  182. bool allInside = true;
  183. // First, find out whether there is any plane for which the bounds completely
  184. // lie on the negative side. If so, the bounds are clearly outside of the volume.
  185. const U32 numPlanes = getNumPlanes();
  186. for( U32 nplane = 0; nplane < numPlanes; ++ nplane )
  187. {
  188. const PlaneF& plane = getPlanes()[ nplane ];
  189. const PlaneF::Side side = plane.whichSide( bounds );
  190. if( side == PlaneF::Back )
  191. return GeometryOutside;
  192. if( side != PlaneF::Front )
  193. allInside = false;
  194. }
  195. // Test for containment.
  196. if( allInside )
  197. return GeometryInside;
  198. // Otherwise classify as intersecting.
  199. return GeometryIntersecting;
  200. }
  201. //-----------------------------------------------------------------------------
  202. template< typename T >
  203. inline bool PlaneSet< T >::isContained( const Point3F& point, F32 epsilon ) const
  204. {
  205. epsilon = - epsilon;
  206. for( U32 i = 0; i < mNumPlanes; ++ i )
  207. {
  208. const F32 dist = mPlanes[ i ].distToPlane( point );
  209. if( dist < epsilon )
  210. return false;
  211. }
  212. return true;
  213. }
  214. //-----------------------------------------------------------------------------
  215. template< typename T >
  216. inline bool PlaneSet< T >::isContained( const Point3F* points, U32 numPoints ) const
  217. {
  218. for( U32 i = 0; i < numPoints; ++ i )
  219. if( !isContained( points[ i ] ) )
  220. return false;
  221. return true;
  222. }
  223. //-----------------------------------------------------------------------------
  224. template< typename T >
  225. template< typename P >
  226. inline bool PlaneSet< T >::_isContained( const P& bounds ) const
  227. {
  228. for( U32 i = 0; i < mNumPlanes; ++ i )
  229. if( mPlanes[ i ].whichSide( bounds ) != PlaneF::Front )
  230. return false;
  231. return true;
  232. }
  233. //-----------------------------------------------------------------------------
  234. template< typename T >
  235. U32 PlaneSet< T >::testPlanes( const Box3F& bounds, U32 planeMask, F32 expand ) const
  236. {
  237. AssertFatal( mNumPlanes <= 32, "PlaneSet::testPlanes - Too many planes in set!" );
  238. // This is based on the paper "A Faster Overlap Test for a Plane and a Bounding Box"
  239. // by Kenny Hoff. See http://www.cs.unc.edu/~hoff/research/vfculler/boxplane.html
  240. U32 retMask = 0;
  241. const U32 numPlanes = getMin( mNumPlanes, U32( 32 ) );
  242. for ( S32 i = 0; i < numPlanes; i++ )
  243. {
  244. U32 mask = ( 1 << i );
  245. if ( !( planeMask & mask ) )
  246. continue;
  247. const PlaneF& plane = mPlanes[ i ];
  248. Point3F minPoint, maxPoint;
  249. if( plane.x > 0 )
  250. {
  251. maxPoint.x = bounds.maxExtents.x;
  252. minPoint.x = bounds.minExtents.x;
  253. }
  254. else
  255. {
  256. maxPoint.x = bounds.minExtents.x;
  257. minPoint.x = bounds.maxExtents.x;
  258. }
  259. if( plane.y > 0 )
  260. {
  261. maxPoint.y = bounds.maxExtents.y;
  262. minPoint.y = bounds.minExtents.y;
  263. }
  264. else
  265. {
  266. maxPoint.y = bounds.minExtents.y;
  267. minPoint.y = bounds.maxExtents.y;
  268. }
  269. if( plane.z > 0 )
  270. {
  271. maxPoint.z = bounds.maxExtents.z;
  272. minPoint.z = bounds.minExtents.z;
  273. }
  274. else
  275. {
  276. maxPoint.z = bounds.minExtents.z;
  277. minPoint.z = bounds.maxExtents.z;
  278. }
  279. F32 maxDot = mDot( maxPoint, plane );
  280. if( maxDot <= - ( plane.d + expand ) )
  281. return -1;
  282. F32 minDot = mDot( minPoint, plane );
  283. if( ( minDot + plane.d ) < 0.0f )
  284. retMask |= mask;
  285. }
  286. return retMask;
  287. }
  288. //-----------------------------------------------------------------------------
  289. template< typename T >
  290. bool PlaneSet< T >::clipSegment( Point3F &pnt0, Point3F &pnt1 ) const
  291. {
  292. F32 tmin = F32_MAX;
  293. F32 tmax = -F32_MAX;
  294. U32 hitCount = 0;
  295. Point3F tpnt;
  296. const U32 numPlanes = mNumPlanes;
  297. for( U32 i = 0; i < numPlanes; ++ i )
  298. {
  299. const PlaneF &plane = mPlanes[ i ];
  300. F32 t = plane.intersect( pnt0, pnt1 );
  301. if( t >= 0.0f && t <= 1.0f )
  302. {
  303. tpnt.interpolate( pnt0, pnt1, t );
  304. if ( isContained( tpnt, 1.0e-004f ) )
  305. {
  306. tmin = getMin( tmin, t );
  307. tmax = getMax( tmax, t );
  308. hitCount ++;
  309. }
  310. }
  311. }
  312. // If we had no intersections then either both points are inside or both are outside.
  313. if( hitCount == 0 )
  314. return isContained( pnt0 );
  315. // If we had one intersection then we have one point inside.
  316. // tmin and tmax are the same here.
  317. if( hitCount == 1 )
  318. {
  319. if( isContained( pnt0 ) )
  320. pnt1.interpolate( pnt0, pnt1, tmax );
  321. else
  322. pnt0.interpolate( pnt0, pnt1, tmin );
  323. }
  324. else
  325. {
  326. Point3F prevPnt0( pnt0 );
  327. Point3F prevPnt1( pnt1 );
  328. if( tmin < F32_MAX )
  329. pnt0.interpolate( prevPnt0, prevPnt1, tmin );
  330. if( tmax > -F32_MAX )
  331. pnt1.interpolate( prevPnt0, prevPnt1, tmax );
  332. }
  333. return true;
  334. }
  335. //-----------------------------------------------------------------------------
  336. template< typename T >
  337. U32 PlaneSet< T >::clipPolygon( const Point3F* inVertices, U32 inNumVertices, Point3F* outVertices, U32 maxOutVertices ) const
  338. {
  339. TempAlloc< Point3F > tempBuffer( inNumVertices + mNumPlanes );
  340. // We use two buffers as interchanging roles as source and target.
  341. // For the first iteration, inVertices is the source.
  342. Point3F* tempPolygon = tempBuffer;
  343. Point3F* clippedPolygon = const_cast< Point3F* >( inVertices );
  344. U32 numClippedPolygonVertices = inNumVertices;
  345. U32 numTempPolygonVertices = 0;
  346. for( U32 nplane = 0; nplane < mNumPlanes; ++ nplane )
  347. {
  348. // Make the output of the last iteration the
  349. // input of this iteration.
  350. T3D::swap( tempPolygon, clippedPolygon );
  351. numTempPolygonVertices = numClippedPolygonVertices;
  352. if( maxOutVertices < numTempPolygonVertices + 1 )
  353. return 0;
  354. // Clip our current remainder of the original polygon
  355. // against the current plane.
  356. const PlaneF& plane = mPlanes[ nplane ];
  357. numClippedPolygonVertices = plane.clipPolygon( tempPolygon, numTempPolygonVertices, clippedPolygon );
  358. // If the polygon was completely on the backside of the plane,
  359. // then polygon is outside the frustum. In this case, return false
  360. // to indicate we haven't clipped anything.
  361. if( !numClippedPolygonVertices )
  362. return 0;
  363. // On first iteration, replace the inVertices with the
  364. // outVertices buffer.
  365. if( tempPolygon == inVertices )
  366. tempPolygon = outVertices;
  367. }
  368. // If outVertices isn't the target buffer of the last
  369. // iteration, copy the vertices over from the temporary
  370. // buffer.
  371. if( clippedPolygon != outVertices )
  372. dMemcpy( outVertices, clippedPolygon, numClippedPolygonVertices * sizeof( Point3F ) );
  373. return numClippedPolygonVertices;
  374. }
  375. #endif // !_MPLANESET_H_