123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #ifndef _MINTERSECTOR_H_
- #define _MINTERSECTOR_H_
- #ifndef _MCONSTANTS_H_
- #include "math/mConstants.h"
- #endif
- #ifndef _MBOX_H_
- #include "math/mBox.h"
- #endif
- #ifndef _MORIENTEDBOX_H_
- #include "math/mOrientedBox.h"
- #endif
- #ifndef _MSPHERE_H_
- #include "math/mSphere.h"
- #endif
- #ifndef _MPLANE_H_
- #include "math/mPlane.h"
- #endif
- #ifndef _MPOLYHEDRON_H_
- #include "math/mPolyhedron.h"
- #endif
- #ifndef _MPLANETRANSFORMER_H_
- #include "math/mPlaneTransformer.h"
- #endif
- #ifndef _PROFILER_H_
- #include "platform/profiler.h"
- #endif
- /// @file
- /// Precise and fast geometric intersection testing.
- /// Base class for intersector implementations.
- template< typename Tester, typename Testee >
- struct IntersectorBase
- {
- typedef Tester TesterType;
- typedef Testee TesteeType;
- protected:
- TesterType mTester;
- public:
- IntersectorBase() {}
- IntersectorBase( const TesterType& tester )
- : mTester( tester ) {}
- };
- /// Convex polyhedron / AABB intersection.
- ///
- /// This class implements the algorithm described in "Detecting Intersection of a Rectangular
- /// Solid and a Convex Polyhedron", Graphics Gems IV, Chapter 1.7, by Ned Greene.
- ///
- /// The polyhedron is preprocessed when an object of this class is constructed.
- ///
- /// This class also assumes that the polyhedron is represented in object space and thus also
- /// stores local copies of the polyhedron's planes transformed into world space.
- ///
- /// The approach of the algorithm is simple. It uses a maximum of three successive stages
- /// to determine intersection. Each stage can early out if it can draw a conclusive result
- /// already.
- ///
- /// 1. Simple tests of the polyhedron's bounding box against the input box.
- /// 2. Standard test on plane set.
- /// 3. Plane tests reduced to 2D and done for each of the orthographic side projections.
- ///
- /// @note The intersector depends on planes facing inwards.
- template< typename Polyhedron >
- struct PolyhedronBoxIntersector : public IntersectorBase< Polyhedron, Box3F >
- {
- typedef IntersectorBase< Polyhedron, Box3F > Parent;
- typedef Polyhedron PolyhedronType;
- protected:
- /// Bounds of the polyhedron.
- Box3F mBounds;
- /// World-space planes.
- Vector< PlaneF > mPlanes;
- /// Number of silhouette edges for each of the orthographic projections.
- Point3I mNumEdgeLines;
- /// Edge line equations. X projection first, then Y, then Z.
- /// X of each equation is mapped to a, Y to b, and Z to c of the standard
- /// implicit form of line equations.
- Vector< Point3F > mEdgeLines;
- /// Run the preprocessing step on the current polyhedron.
- void _preprocess( const MatrixF& objToWorld, const Point3F& scale );
- /// Project the point orthogonally down the given axis such that
- /// the orientation of the resulting coordinate system remains
- /// right-handed.
- Point2F _project( U32 axis, const Point3F& p )
- {
- switch( axis )
- {
- case 0: return Point2F( - p.y, p.z );
- case 1: return Point2F( p.x, p.z );
- default: // silence compiler
- case 2: return Point2F( p.x, - p.y );
- }
- }
- public:
- PolyhedronBoxIntersector() {}
- PolyhedronBoxIntersector( const PolyhedronType& polyhedron,
- const MatrixF& objToWorld,
- const Point3F& scale,
- const Box3F& wsBounds )
- : Parent( polyhedron ), mBounds( wsBounds )
- {
- _preprocess( objToWorld, scale );
- }
- OverlapTestResult test( const Box3F& box ) const;
- };
- //-----------------------------------------------------------------------------
- template< typename Polyhedron >
- void PolyhedronBoxIntersector< Polyhedron >::_preprocess( const MatrixF& objToWorld, const Point3F& scale )
- {
- PROFILE_SCOPE( PolyhedronBoxIntersector_preprocess );
- // Transform the planes.
- const U32 numPlanes = this->mTester.getNumPlanes();
- const typename Polyhedron::PlaneType* planes = this->mTester.getPlanes();
-
- PlaneTransformer transformer;
- transformer.set( objToWorld, scale );
- mPlanes.setSize( numPlanes );
- for( U32 i = 0; i < numPlanes; ++ i )
- transformer.transform( planes[ i ], mPlanes[ i ] );
- // Extract the silhouettes for each of the three
- // orthographic projections.
- const U32 numEdges = this->mTester.getNumEdges();
- const typename Polyhedron::EdgeType* edges = this->mTester.getEdges();
- const typename Polyhedron::PointType* points = this->mTester.getPoints();
- for( U32 i = 0; i < 3; ++ i )
- {
- U32 numEdgesThisProj = 0;
- // Gather edge-lines for this projection.
- for( U32 n = 0; n < numEdges; ++ n )
- {
- const typename Polyhedron::EdgeType& edge = edges[ n ];
- // Compute dot product with face normals. With our projection
- // pointing straight down the current axis, this is reduced to
- // '1*normal[i]'.
- F32 dotFace[ 2 ];
- dotFace[ 0 ] = mPlanes[ edge.face[ 0 ] ][ i ];
- dotFace[ 1 ] = mPlanes[ edge.face[ 1 ] ][ i ];
- // Skip edge if not a silhouette edge in this view.
- if( mSign( dotFace[ 0 ] ) == mSign( dotFace[ 1 ] ) )
- continue;
- // Find out which face is the front facing one. Since we expect normals
- // to be pointing inwards, this means a reversal of the normal back facing
- // test and we're looking for a normal facing the *same* way as our projection.
- const U32 frontFace = dotFace[ 0 ] > 0.f ? 0 : 1;
- if( dotFace[ frontFace ] <= 0.f )
- continue; // This face or other face is perpendicular to us.
- // Now we want to find the line equation for the edge. For that, we first need
- // the normal. The direction of the normal is important so that we identify
- // the half-spaces correctly. We want it to be pointing to the inside of the
- // polyhedron.
- Point3F v1 = points[ edge.vertex[ 0 ] ];
- Point3F v2 = points[ edge.vertex[ 1 ] ];
- v1.convolve( scale );
- v2.convolve( scale );
- objToWorld.mulP( v1 );
- objToWorld.mulP( v2 );
- Point2F q = _project( i, v1 ); // First point on line.
- Point2F p = _project( i, v2 ); // Second point on line.
- if( frontFace != 0 )
- T3D::swap( p, q );
- Point2F normal( - ( p.y - q.y ), p.x - q.x );
- normal.normalize();
- // Now compute c.
- const F32 c = mDot( - q, normal );
- // Add the edge.
-
- mEdgeLines.push_back(
- Point3F( normal.x, normal.y, c )
- );
-
- numEdgesThisProj ++;
- }
- mNumEdgeLines[ i ] = numEdgesThisProj;
- }
- }
- //-----------------------------------------------------------------------------
- template< typename Polyhedron >
- OverlapTestResult PolyhedronBoxIntersector< Polyhedron >::test( const Box3F& box ) const
- {
- PROFILE_SCOPE( PolyhedronBoxIntersector_test );
- // -- Bounding box tests. --
- // If the box does not intersect with the AABB of the polyhedron,
- // it must be outside.
- if( !mBounds.isOverlapped( box ) )
- return GeometryOutside;
- // If the polyhedron's bounding box is fully contained in the given box,
- // the box is intersecting.
-
- if( box.isContained( mBounds ) )
- return GeometryIntersecting;
- // -- Face-plane tests. --
-
- bool insideAll = true;
- // Test each of the planes to see if the bounding box lies
- // fully in the negative space of any one of them.
- const U32 numPlanes = mPlanes.size();
- for( U32 i = 0; i < numPlanes; ++ i )
- {
- const PlaneF& plane = mPlanes[ i ];
- PlaneF::Side boxSide = plane.whichSide( box );
- if( boxSide == PlaneF::Back )
- return GeometryOutside;
- insideAll &= ( boxSide == PlaneF::Front );
- }
- // If the box is on the positive space of all of the polyhedron's
- // planes, it's inside.
- if( insideAll )
- return GeometryInside;
- // -- Edge-line tests. --
- U32 edgeLineIndex = 0;
- for( U32 i = 0; i < 3; ++ i )
- {
- // Determine the mapping of 3D to 2D for this projection.
- U32 xIndex = 0;
- U32 yIndex = 0;
- switch( i )
- {
- case 0: xIndex = 1; yIndex = 2; break;
- case 1: xIndex = 0; yIndex = 2; break;
- case 2: xIndex = 0; yIndex = 1; break;
- }
- // Go through the edge-lines for this projection and
- // test the p-vertex for each edge line.
- const U32 numEdgesForThisProj = mNumEdgeLines[ i ];
- for( U32 n = 0; n < numEdgesForThisProj; ++ n, edgeLineIndex ++ )
- {
- const Point3F& edgeLine = mEdgeLines[ edgeLineIndex ];
- // Determine the p-vertex for the current AABB/edge combo.
- // Need to account for the axis flipping we have applied to maintain
- // a right-handed coordinate system.
- Point2F pVertex;
- switch( i )
- {
- case 0:
- pVertex.x = - ( edgeLine.x < 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ] );
- pVertex.y = edgeLine.y > 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ];
- break;
- case 1:
- pVertex.x = edgeLine.x > 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ];
- pVertex.y = edgeLine.y > 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ];
- break;
- case 2:
- pVertex.x = edgeLine.x > 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ];
- pVertex.y = - ( edgeLine.y < 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ] );
- break;
- }
- // See if the p-vertex lies inside in the negative half-space of the
- // edge line. If so, the AABB is not intersecting the polyhedron in
- // this projection so we can conclude our search here.
- const F32 d = edgeLine.x * pVertex.x + edgeLine.y * pVertex.y + edgeLine.z;
- if( d < 0.f )
- return GeometryOutside;
- }
- }
- // Done. Determined to be intersecting.
- return GeometryIntersecting;
- }
- /// Geometric intersecting testing.
- ///
- /// This class is meant to be used for testing multiple geometries against
- /// a specific geometric object. Unlike the various intersection test routines
- /// in other classes, this class might precompute and store data that is going
- /// to be used repeatedly in the tests. As such, Intersector can be faster
- /// in certain cases.
- ///
- /// Also, intersectors are required to implement *exact* intersection tests, i.e.
- /// it is not acceptable for an Intersector to produce false positives on any
- /// of the OverlapTestResult values.
- ///
- /// This class itself has no functionality. It depends on specializations.
- template< typename Tester, typename Testee >
- struct Intersector : public IntersectorBase< Tester, Testee > {};
- // Specializations.
- template<>
- struct Intersector< AnyPolyhedron, Box3F > : public PolyhedronBoxIntersector< AnyPolyhedron > {};
- #endif // !_MINTERSECTOR_H_
|