mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
386 lines
12 KiB
C++
386 lines
12 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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_
|