mIntersector.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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 _MINTERSECTOR_H_
  23. #define _MINTERSECTOR_H_
  24. #ifndef _MCONSTANTS_H_
  25. #include "math/mConstants.h"
  26. #endif
  27. #ifndef _MBOX_H_
  28. #include "math/mBox.h"
  29. #endif
  30. #ifndef _MORIENTEDBOX_H_
  31. #include "math/mOrientedBox.h"
  32. #endif
  33. #ifndef _MSPHERE_H_
  34. #include "math/mSphere.h"
  35. #endif
  36. #ifndef _MPLANE_H_
  37. #include "math/mPlane.h"
  38. #endif
  39. #ifndef _MPOLYHEDRON_H_
  40. #include "math/mPolyhedron.h"
  41. #endif
  42. #ifndef _MPLANETRANSFORMER_H_
  43. #include "math/mPlaneTransformer.h"
  44. #endif
  45. #ifndef _PROFILER_H_
  46. #include "platform/profiler.h"
  47. #endif
  48. /// @file
  49. /// Precise and fast geometric intersection testing.
  50. /// Base class for intersector implementations.
  51. template< typename Tester, typename Testee >
  52. struct IntersectorBase
  53. {
  54. typedef Tester TesterType;
  55. typedef Testee TesteeType;
  56. protected:
  57. TesterType mTester;
  58. public:
  59. IntersectorBase() {}
  60. IntersectorBase( const TesterType& tester )
  61. : mTester( tester ) {}
  62. };
  63. /// Convex polyhedron / AABB intersection.
  64. ///
  65. /// This class implements the algorithm described in "Detecting Intersection of a Rectangular
  66. /// Solid and a Convex Polyhedron", Graphics Gems IV, Chapter 1.7, by Ned Greene.
  67. ///
  68. /// The polyhedron is preprocessed when an object of this class is constructed.
  69. ///
  70. /// This class also assumes that the polyhedron is represented in object space and thus also
  71. /// stores local copies of the polyhedron's planes transformed into world space.
  72. ///
  73. /// The approach of the algorithm is simple. It uses a maximum of three successive stages
  74. /// to determine intersection. Each stage can early out if it can draw a conclusive result
  75. /// already.
  76. ///
  77. /// 1. Simple tests of the polyhedron's bounding box against the input box.
  78. /// 2. Standard test on plane set.
  79. /// 3. Plane tests reduced to 2D and done for each of the orthographic side projections.
  80. ///
  81. /// @note The intersector depends on planes facing inwards.
  82. template< typename Polyhedron >
  83. struct PolyhedronBoxIntersector : public IntersectorBase< Polyhedron, Box3F >
  84. {
  85. typedef IntersectorBase< Polyhedron, Box3F > Parent;
  86. typedef Polyhedron PolyhedronType;
  87. protected:
  88. /// Bounds of the polyhedron.
  89. Box3F mBounds;
  90. /// World-space planes.
  91. Vector< PlaneF > mPlanes;
  92. /// Number of silhouette edges for each of the orthographic projections.
  93. Point3I mNumEdgeLines;
  94. /// Edge line equations. X projection first, then Y, then Z.
  95. /// X of each equation is mapped to a, Y to b, and Z to c of the standard
  96. /// implicit form of line equations.
  97. Vector< Point3F > mEdgeLines;
  98. /// Run the preprocessing step on the current polyhedron.
  99. void _preprocess( const MatrixF& objToWorld, const Point3F& scale );
  100. /// Project the point orthogonally down the given axis such that
  101. /// the orientation of the resulting coordinate system remains
  102. /// right-handed.
  103. Point2F _project( U32 axis, const Point3F& p )
  104. {
  105. switch( axis )
  106. {
  107. case 0: return Point2F( - p.y, p.z );
  108. case 1: return Point2F( p.x, p.z );
  109. default: // silence compiler
  110. case 2: return Point2F( p.x, - p.y );
  111. }
  112. }
  113. public:
  114. PolyhedronBoxIntersector() {}
  115. PolyhedronBoxIntersector( const PolyhedronType& polyhedron,
  116. const MatrixF& objToWorld,
  117. const Point3F& scale,
  118. const Box3F& wsBounds )
  119. : Parent( polyhedron ), mBounds( wsBounds )
  120. {
  121. _preprocess( objToWorld, scale );
  122. }
  123. OverlapTestResult test( const Box3F& box ) const;
  124. };
  125. //-----------------------------------------------------------------------------
  126. template< typename Polyhedron >
  127. void PolyhedronBoxIntersector< Polyhedron >::_preprocess( const MatrixF& objToWorld, const Point3F& scale )
  128. {
  129. PROFILE_SCOPE( PolyhedronBoxIntersector_preprocess );
  130. // Transform the planes.
  131. const U32 numPlanes = this->mTester.getNumPlanes();
  132. const typename Polyhedron::PlaneType* planes = this->mTester.getPlanes();
  133. PlaneTransformer transformer;
  134. transformer.set( objToWorld, scale );
  135. mPlanes.setSize( numPlanes );
  136. for( U32 i = 0; i < numPlanes; ++ i )
  137. transformer.transform( planes[ i ], mPlanes[ i ] );
  138. // Extract the silhouettes for each of the three
  139. // orthographic projections.
  140. const U32 numEdges = this->mTester.getNumEdges();
  141. const typename Polyhedron::EdgeType* edges = this->mTester.getEdges();
  142. const typename Polyhedron::PointType* points = this->mTester.getPoints();
  143. for( U32 i = 0; i < 3; ++ i )
  144. {
  145. U32 numEdgesThisProj = 0;
  146. // Gather edge-lines for this projection.
  147. for( U32 n = 0; n < numEdges; ++ n )
  148. {
  149. const typename Polyhedron::EdgeType& edge = edges[ n ];
  150. // Compute dot product with face normals. With our projection
  151. // pointing straight down the current axis, this is reduced to
  152. // '1*normal[i]'.
  153. F32 dotFace[ 2 ];
  154. dotFace[ 0 ] = mPlanes[ edge.face[ 0 ] ][ i ];
  155. dotFace[ 1 ] = mPlanes[ edge.face[ 1 ] ][ i ];
  156. // Skip edge if not a silhouette edge in this view.
  157. if( mSign( dotFace[ 0 ] ) == mSign( dotFace[ 1 ] ) )
  158. continue;
  159. // Find out which face is the front facing one. Since we expect normals
  160. // to be pointing inwards, this means a reversal of the normal back facing
  161. // test and we're looking for a normal facing the *same* way as our projection.
  162. const U32 frontFace = dotFace[ 0 ] > 0.f ? 0 : 1;
  163. if( dotFace[ frontFace ] <= 0.f )
  164. continue; // This face or other face is perpendicular to us.
  165. // Now we want to find the line equation for the edge. For that, we first need
  166. // the normal. The direction of the normal is important so that we identify
  167. // the half-spaces correctly. We want it to be pointing to the inside of the
  168. // polyhedron.
  169. Point3F v1 = points[ edge.vertex[ 0 ] ];
  170. Point3F v2 = points[ edge.vertex[ 1 ] ];
  171. v1.convolve( scale );
  172. v2.convolve( scale );
  173. objToWorld.mulP( v1 );
  174. objToWorld.mulP( v2 );
  175. Point2F q = _project( i, v1 ); // First point on line.
  176. Point2F p = _project( i, v2 ); // Second point on line.
  177. if( frontFace != 0 )
  178. T3D::swap( p, q );
  179. Point2F normal( - ( p.y - q.y ), p.x - q.x );
  180. normal.normalize();
  181. // Now compute c.
  182. const F32 c = mDot( - q, normal );
  183. // Add the edge.
  184. mEdgeLines.push_back(
  185. Point3F( normal.x, normal.y, c )
  186. );
  187. numEdgesThisProj ++;
  188. }
  189. mNumEdgeLines[ i ] = numEdgesThisProj;
  190. }
  191. }
  192. //-----------------------------------------------------------------------------
  193. template< typename Polyhedron >
  194. OverlapTestResult PolyhedronBoxIntersector< Polyhedron >::test( const Box3F& box ) const
  195. {
  196. PROFILE_SCOPE( PolyhedronBoxIntersector_test );
  197. // -- Bounding box tests. --
  198. // If the box does not intersect with the AABB of the polyhedron,
  199. // it must be outside.
  200. if( !mBounds.isOverlapped( box ) )
  201. return GeometryOutside;
  202. // If the polyhedron's bounding box is fully contained in the given box,
  203. // the box is intersecting.
  204. if( box.isContained( mBounds ) )
  205. return GeometryIntersecting;
  206. // -- Face-plane tests. --
  207. bool insideAll = true;
  208. // Test each of the planes to see if the bounding box lies
  209. // fully in the negative space of any one of them.
  210. const U32 numPlanes = mPlanes.size();
  211. for( U32 i = 0; i < numPlanes; ++ i )
  212. {
  213. const PlaneF& plane = mPlanes[ i ];
  214. PlaneF::Side boxSide = plane.whichSide( box );
  215. if( boxSide == PlaneF::Back )
  216. return GeometryOutside;
  217. insideAll &= ( boxSide == PlaneF::Front );
  218. }
  219. // If the box is on the positive space of all of the polyhedron's
  220. // planes, it's inside.
  221. if( insideAll )
  222. return GeometryInside;
  223. // -- Edge-line tests. --
  224. U32 edgeLineIndex = 0;
  225. for( U32 i = 0; i < 3; ++ i )
  226. {
  227. // Determine the mapping of 3D to 2D for this projection.
  228. U32 xIndex = 0;
  229. U32 yIndex = 0;
  230. switch( i )
  231. {
  232. case 0: xIndex = 1; yIndex = 2; break;
  233. case 1: xIndex = 0; yIndex = 2; break;
  234. case 2: xIndex = 0; yIndex = 1; break;
  235. }
  236. // Go through the edge-lines for this projection and
  237. // test the p-vertex for each edge line.
  238. const U32 numEdgesForThisProj = mNumEdgeLines[ i ];
  239. for( U32 n = 0; n < numEdgesForThisProj; ++ n, edgeLineIndex ++ )
  240. {
  241. const Point3F& edgeLine = mEdgeLines[ edgeLineIndex ];
  242. // Determine the p-vertex for the current AABB/edge combo.
  243. // Need to account for the axis flipping we have applied to maintain
  244. // a right-handed coordinate system.
  245. Point2F pVertex;
  246. switch( i )
  247. {
  248. case 0:
  249. pVertex.x = - ( edgeLine.x < 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ] );
  250. pVertex.y = edgeLine.y > 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ];
  251. break;
  252. case 1:
  253. pVertex.x = edgeLine.x > 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ];
  254. pVertex.y = edgeLine.y > 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ];
  255. break;
  256. case 2:
  257. pVertex.x = edgeLine.x > 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ];
  258. pVertex.y = - ( edgeLine.y < 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ] );
  259. break;
  260. }
  261. // See if the p-vertex lies inside in the negative half-space of the
  262. // edge line. If so, the AABB is not intersecting the polyhedron in
  263. // this projection so we can conclude our search here.
  264. const F32 d = edgeLine.x * pVertex.x + edgeLine.y * pVertex.y + edgeLine.z;
  265. if( d < 0.f )
  266. return GeometryOutside;
  267. }
  268. }
  269. // Done. Determined to be intersecting.
  270. return GeometryIntersecting;
  271. }
  272. /// Geometric intersecting testing.
  273. ///
  274. /// This class is meant to be used for testing multiple geometries against
  275. /// a specific geometric object. Unlike the various intersection test routines
  276. /// in other classes, this class might precompute and store data that is going
  277. /// to be used repeatedly in the tests. As such, Intersector can be faster
  278. /// in certain cases.
  279. ///
  280. /// Also, intersectors are required to implement *exact* intersection tests, i.e.
  281. /// it is not acceptable for an Intersector to produce false positives on any
  282. /// of the OverlapTestResult values.
  283. ///
  284. /// This class itself has no functionality. It depends on specializations.
  285. template< typename Tester, typename Testee >
  286. struct Intersector : public IntersectorBase< Tester, Testee > {};
  287. // Specializations.
  288. template<>
  289. struct Intersector< AnyPolyhedron, Box3F > : public PolyhedronBoxIntersector< AnyPolyhedron > {};
  290. #endif // !_MINTERSECTOR_H_