mIntersector.h
Engine/source/math/mIntersector.h
Precise and fast geometric intersection testing.
Classes:
class
Geometric intersecting testing.
class
Base class for intersector implementations.
class
Convex polyhedron / AABB intersection.
Detailed Description
Precise and fast geometric intersection testing.
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#ifndef _MINTERSECTOR_H_ 25#define _MINTERSECTOR_H_ 26 27#ifndef _MCONSTANTS_H_ 28#include "math/mConstants.h" 29#endif 30 31#ifndef _MBOX_H_ 32#include "math/mBox.h" 33#endif 34 35#ifndef _MORIENTEDBOX_H_ 36#include "math/mOrientedBox.h" 37#endif 38 39#ifndef _MSPHERE_H_ 40#include "math/mSphere.h" 41#endif 42 43#ifndef _MPLANE_H_ 44#include "math/mPlane.h" 45#endif 46 47#ifndef _MPOLYHEDRON_H_ 48#include "math/mPolyhedron.h" 49#endif 50 51#ifndef _MPLANETRANSFORMER_H_ 52#include "math/mPlaneTransformer.h" 53#endif 54 55#ifndef _PROFILER_H_ 56#include "platform/profiler.h" 57#endif 58 59 60/// @file 61/// Precise and fast geometric intersection testing. 62 63 64/// Base class for intersector implementations. 65template< typename Tester, typename Testee > 66struct IntersectorBase 67{ 68 typedef Tester TesterType; 69 typedef Testee TesteeType; 70 71 protected: 72 73 TesterType mTester; 74 75 public: 76 77 IntersectorBase() {} 78 IntersectorBase( const TesterType& tester ) 79 : mTester( tester ) {} 80}; 81 82 83/// Convex polyhedron / AABB intersection. 84/// 85/// This class implements the algorithm described in "Detecting Intersection of a Rectangular 86/// Solid and a Convex Polyhedron", Graphics Gems IV, Chapter 1.7, by Ned Greene. 87/// 88/// The polyhedron is preprocessed when an object of this class is constructed. 89/// 90/// This class also assumes that the polyhedron is represented in object space and thus also 91/// stores local copies of the polyhedron's planes transformed into world space. 92/// 93/// The approach of the algorithm is simple. It uses a maximum of three successive stages 94/// to determine intersection. Each stage can early out if it can draw a conclusive result 95/// already. 96/// 97/// 1. Simple tests of the polyhedron's bounding box against the input box. 98/// 2. Standard test on plane set. 99/// 3. Plane tests reduced to 2D and done for each of the orthographic side projections. 100/// 101/// @note The intersector depends on planes facing inwards. 102template< typename Polyhedron > 103struct PolyhedronBoxIntersector : public IntersectorBase< Polyhedron, Box3F > 104{ 105 typedef IntersectorBase< Polyhedron, Box3F> Parent; 106 typedef Polyhedron PolyhedronType; 107 108 protected: 109 110 /// Bounds of the polyhedron. 111 Box3F mBounds; 112 113 /// World-space planes. 114 Vector< PlaneF> mPlanes; 115 116 /// Number of silhouette edges for each of the orthographic projections. 117 Point3I mNumEdgeLines; 118 119 /// Edge line equations. X projection first, then Y, then Z. 120 /// X of each equation is mapped to a, Y to b, and Z to c of the standard 121 /// implicit form of line equations. 122 Vector< Point3F> mEdgeLines; 123 124 /// Run the preprocessing step on the current polyhedron. 125 void _preprocess( const MatrixF& objToWorld, const Point3F& scale ); 126 127 /// Project the point orthogonally down the given axis such that 128 /// the orientation of the resulting coordinate system remains 129 /// right-handed. 130 Point2F _project( U32 axis, const Point3F& p ) 131 { 132 switch( axis ) 133 { 134 case 0: return Point2F( - p.y, p.z ); 135 case 1: return Point2F( p.x, p.z ); 136 default: // silence compiler 137 case 2: return Point2F( p.x, - p.y ); 138 } 139 } 140 141 public: 142 143 PolyhedronBoxIntersector() {} 144 PolyhedronBoxIntersector( const PolyhedronType& polyhedron, 145 const MatrixF& objToWorld, 146 const Point3F& scale, 147 const Box3F& wsBounds ) 148 : Parent( polyhedron ), mBounds( wsBounds ) 149 { 150 _preprocess( objToWorld, scale ); 151 } 152 153 OverlapTestResult test( const Box3F& box ) const; 154}; 155 156//----------------------------------------------------------------------------- 157 158template< typename Polyhedron > 159void PolyhedronBoxIntersector< Polyhedron >::_preprocess( const MatrixF& objToWorld, const Point3F& scale ) 160{ 161 PROFILE_SCOPE( PolyhedronBoxIntersector_preprocess ); 162 163 // Transform the planes. 164 165 const U32 numPlanes = this->mTester.getNumPlanes(); 166 const typename Polyhedron::PlaneType* planes = this->mTester.getPlanes(); 167 168 PlaneTransformer transformer; 169 transformer.set( objToWorld, scale ); 170 171 mPlanes.setSize( numPlanes ); 172 for( U32 i = 0; i < numPlanes; ++ i ) 173 transformer.transform( planes[ i ], mPlanes[ i ] ); 174 175 // Extract the silhouettes for each of the three 176 // orthographic projections. 177 178 const U32 numEdges = this->mTester.getNumEdges(); 179 const typename Polyhedron::EdgeType* edges = this->mTester.getEdges(); 180 const typename Polyhedron::PointType* points = this->mTester.getPoints(); 181 182 for( U32 i = 0; i < 3; ++ i ) 183 { 184 U32 numEdgesThisProj = 0; 185 186 // Gather edge-lines for this projection. 187 188 for( U32 n = 0; n < numEdges; ++ n ) 189 { 190 const typename Polyhedron::EdgeType& edge = edges[ n ]; 191 192 // Compute dot product with face normals. With our projection 193 // pointing straight down the current axis, this is reduced to 194 // '1*normal[i]'. 195 196 F32 dotFace[ 2 ]; 197 198 dotFace[ 0 ] = mPlanes[ edge.face[ 0 ] ][ i ]; 199 dotFace[ 1 ] = mPlanes[ edge.face[ 1 ] ][ i ]; 200 201 // Skip edge if not a silhouette edge in this view. 202 203 if( mSign( dotFace[ 0 ] ) == mSign( dotFace[ 1 ] ) ) 204 continue; 205 206 // Find out which face is the front facing one. Since we expect normals 207 // to be pointing inwards, this means a reversal of the normal back facing 208 // test and we're looking for a normal facing the *same* way as our projection. 209 210 const U32 frontFace = dotFace[ 0 ] > 0.f ? 0 : 1; 211 if( dotFace[ frontFace ] <= 0.f ) 212 continue; // This face or other face is perpendicular to us. 213 214 // Now we want to find the line equation for the edge. For that, we first need 215 // the normal. The direction of the normal is important so that we identify 216 // the half-spaces correctly. We want it to be pointing to the inside of the 217 // polyhedron. 218 219 Point3F v1 = points[ edge.vertex[ 0 ] ]; 220 Point3F v2 = points[ edge.vertex[ 1 ] ]; 221 222 v1.convolve( scale ); 223 v2.convolve( scale ); 224 225 objToWorld.mulP( v1 ); 226 objToWorld.mulP( v2 ); 227 228 Point2F q = _project( i, v1 ); // First point on line. 229 Point2F p = _project( i, v2 ); // Second point on line. 230 231 if( frontFace != 0 ) 232 T3D::swap( p, q ); 233 234 Point2F normal( - ( p.y - q.y ), p.x - q.x ); 235 normal.normalize(); 236 237 // Now compute c. 238 239 const F32 c = mDot( - q, normal ); 240 241 // Add the edge. 242 243 mEdgeLines.push_back( 244 Point3F( normal.x, normal.y, c ) 245 ); 246 247 numEdgesThisProj ++; 248 } 249 250 mNumEdgeLines[ i ] = numEdgesThisProj; 251 } 252} 253 254//----------------------------------------------------------------------------- 255 256template< typename Polyhedron > 257OverlapTestResult PolyhedronBoxIntersector< Polyhedron >::test( const Box3F& box ) const 258{ 259 PROFILE_SCOPE( PolyhedronBoxIntersector_test ); 260 261 // -- Bounding box tests. -- 262 263 // If the box does not intersect with the AABB of the polyhedron, 264 // it must be outside. 265 266 if( !mBounds.isOverlapped( box ) ) 267 return GeometryOutside; 268 269 // If the polyhedron's bounding box is fully contained in the given box, 270 // the box is intersecting. 271 272 if( box.isContained( mBounds ) ) 273 return GeometryIntersecting; 274 275 // -- Face-plane tests. -- 276 277 bool insideAll = true; 278 279 // Test each of the planes to see if the bounding box lies 280 // fully in the negative space of any one of them. 281 282 const U32 numPlanes = mPlanes.size(); 283 for( U32 i = 0; i < numPlanes; ++ i ) 284 { 285 const PlaneF& plane = mPlanes[ i ]; 286 287 PlaneF::Side boxSide = plane.whichSide( box ); 288 if( boxSide == PlaneF::Back ) 289 return GeometryOutside; 290 291 insideAll &= ( boxSide == PlaneF::Front ); 292 } 293 294 // If the box is on the positive space of all of the polyhedron's 295 // planes, it's inside. 296 297 if( insideAll ) 298 return GeometryInside; 299 300 // -- Edge-line tests. -- 301 302 U32 edgeLineIndex = 0; 303 for( U32 i = 0; i < 3; ++ i ) 304 { 305 // Determine the mapping of 3D to 2D for this projection. 306 307 U32 xIndex = 0; 308 U32 yIndex = 0; 309 310 switch( i ) 311 { 312 case 0: xIndex = 1; yIndex = 2; break; 313 case 1: xIndex = 0; yIndex = 2; break; 314 case 2: xIndex = 0; yIndex = 1; break; 315 } 316 317 // Go through the edge-lines for this projection and 318 // test the p-vertex for each edge line. 319 320 const U32 numEdgesForThisProj = mNumEdgeLines[ i ]; 321 for( U32 n = 0; n < numEdgesForThisProj; ++ n, edgeLineIndex ++ ) 322 { 323 const Point3F& edgeLine = mEdgeLines[ edgeLineIndex ]; 324 325 // Determine the p-vertex for the current AABB/edge combo. 326 // Need to account for the axis flipping we have applied to maintain 327 // a right-handed coordinate system. 328 329 Point2F pVertex; 330 331 switch( i ) 332 { 333 case 0: 334 pVertex.x = - ( edgeLine.x < 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ] ); 335 pVertex.y = edgeLine.y > 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ]; 336 break; 337 338 case 1: 339 pVertex.x = edgeLine.x > 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ]; 340 pVertex.y = edgeLine.y > 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ]; 341 break; 342 343 case 2: 344 pVertex.x = edgeLine.x > 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ]; 345 pVertex.y = - ( edgeLine.y < 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ] ); 346 break; 347 } 348 349 // See if the p-vertex lies inside in the negative half-space of the 350 // edge line. If so, the AABB is not intersecting the polyhedron in 351 // this projection so we can conclude our search here. 352 353 const F32 d = edgeLine.x * pVertex.x + edgeLine.y * pVertex.y + edgeLine.z; 354 if( d < 0.f ) 355 return GeometryOutside; 356 } 357 } 358 359 // Done. Determined to be intersecting. 360 361 return GeometryIntersecting; 362} 363 364 365/// Geometric intersecting testing. 366/// 367/// This class is meant to be used for testing multiple geometries against 368/// a specific geometric object. Unlike the various intersection test routines 369/// in other classes, this class might precompute and store data that is going 370/// to be used repeatedly in the tests. As such, Intersector can be faster 371/// in certain cases. 372/// 373/// Also, intersectors are required to implement *exact* intersection tests, i.e. 374/// it is not acceptable for an Intersector to produce false positives on any 375/// of the OverlapTestResult values. 376/// 377/// This class itself has no functionality. It depends on specializations. 378template< typename Tester, typename Testee > 379struct Intersector : public IntersectorBase< Tester, Testee > {}; 380 381// Specializations. 382 383template<> 384struct Intersector< AnyPolyhedron, Box3F > : public PolyhedronBoxIntersector< AnyPolyhedron > {}; 385 386#endif // !_MINTERSECTOR_H_ 387