mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
506 lines
15 KiB
C++
506 lines
15 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.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "math/mPlaneTransformer.h"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< typename Base >
|
|
Point3F PolyhedronImpl< Base >::getCenterPoint() const
|
|
{
|
|
const U32 numPoints = this->getNumPoints();
|
|
if( numPoints == 0 )
|
|
return Point3F( 0.f, 0.f, 0.f );
|
|
|
|
const typename Base::PointType* pointList = this->getPoints();
|
|
Point3F center( pointList[ 0 ] );
|
|
|
|
for( U32 i = 1; i < numPoints; ++ i )
|
|
center += pointList[ i ];
|
|
|
|
center /= ( F32 ) numPoints;
|
|
return center;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< typename Base >
|
|
void PolyhedronImpl< Base >::transform( const MatrixF& matrix, const Point3F& scale )
|
|
{
|
|
// Transform points.
|
|
|
|
const U32 numPoints = this->getNumPoints();
|
|
typename Base::PointType* points = this->getPoints();
|
|
|
|
for( U32 i = 0; i < numPoints; ++ i )
|
|
{
|
|
matrix.mulP( points[ i ] );
|
|
points[ i ].convolve( scale );
|
|
}
|
|
|
|
// Transform planes.
|
|
|
|
const U32 numPlanes = this->getNumPlanes();
|
|
typename Base::PlaneType* planes = this->getPlanes();
|
|
|
|
PlaneTransformer transformer;
|
|
transformer.set( matrix, scale );
|
|
|
|
for( U32 i = 0; i < numPlanes; ++ i )
|
|
{
|
|
const typename Base::PlaneType& plane = planes[ i ];
|
|
|
|
PlaneF result;
|
|
transformer.transform( plane, result );
|
|
planes[ i ] = result;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< typename Base >
|
|
U32 PolyhedronImpl< Base >::constructIntersection( const PlaneF& plane, Point3F* outPoints, U32 maxOutPoints ) const
|
|
{
|
|
// The assumption here is that the polyhedron is entirely composed
|
|
// of convex polygons implicitly described by the edges, points, and planes.
|
|
// So, any polygon can only have one of three relations to the given plane
|
|
//
|
|
// 1) None of its edges intersect with the plane, i.e. the polygon can be ignored.
|
|
// 2) One of its edges lies on the plane.
|
|
// 2) Two of its edges intersect with the plane.
|
|
//
|
|
// Conceptually, we need to find the first face with an intersecting edge, then find
|
|
// another edge on the same face that is also intersecting, and then continue with the
|
|
// face on the opposite side of the edge.
|
|
|
|
const U32 numEdges = this->getNumEdges();
|
|
const typename Base::EdgeType* edges = this->getEdges();
|
|
const typename Base::PointType* points = this->getPoints();
|
|
U32 numOutPoints = 0;
|
|
|
|
#define ADD_POINT( p ) \
|
|
if( numOutPoints >= maxOutPoints ) \
|
|
return 0; \
|
|
outPoints[ numOutPoints ++ ] = p;
|
|
|
|
Point3F intersection;
|
|
|
|
S32 firstEdge = -1;
|
|
S32 firstFace = -1;
|
|
|
|
U32 v1 = 0;
|
|
U32 v2 = 0;
|
|
|
|
PlaneF::Side v1Side = PlaneF::Front;
|
|
PlaneF::Side v2Side = PlaneF::Front;
|
|
|
|
// Find an edge to start with. This is the first edge
|
|
// in the polyhedron that intersects the plane.
|
|
|
|
for( U32 i = 0; i < numEdges; ++ i )
|
|
{
|
|
const typename Base::EdgeType& edge = edges[ i ];
|
|
|
|
v1 = edge.vertex[ 0 ];
|
|
v2 = edge.vertex[ 1 ];
|
|
|
|
const Point3F& p1 = points[ v1 ];
|
|
const Point3F& p2 = points[ v2 ];
|
|
|
|
v1Side = plane.whichSide( p1 );
|
|
v2Side = plane.whichSide( p2 );
|
|
|
|
if( v1Side == PlaneF::On || v2Side == PlaneF::On ||
|
|
plane.clipSegment( p1, p2, intersection ) )
|
|
{
|
|
firstEdge = i;
|
|
firstFace = edge.face[ 0 ];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( firstEdge == -1 )
|
|
return 0;
|
|
|
|
// Slice around the faces of the polyhedron until
|
|
// we get back to the starting face.
|
|
|
|
U32 currentEdge = firstEdge;
|
|
U32 currentFace = firstFace;
|
|
|
|
do
|
|
{
|
|
// Handle the current edge.
|
|
|
|
if( v1Side == PlaneF::On && v2Side == PlaneF::On )
|
|
{
|
|
// Both points of the edge lie on the plane. Add v2
|
|
// and then look for an edge that is also connected to v1
|
|
// *and* is connected to our current face. The other face
|
|
// of that edge is the one we need to continue with.
|
|
|
|
ADD_POINT( points[ v2 ] );
|
|
|
|
for( U32 n = 0; n < numEdges; ++ n )
|
|
{
|
|
const typename Base::EdgeType& e = edges[ n ];
|
|
|
|
// Skip current edge.
|
|
if( n == currentEdge )
|
|
continue;
|
|
|
|
// Skip edges not belonging to current face.
|
|
if( e.face[ 0 ] != currentFace && e.face[ 1 ] != currentFace )
|
|
continue;
|
|
|
|
// Skip edges not connected to the current point.
|
|
if( e.vertex[ 0 ] != edges[ currentEdge ].vertex[ 0 ] &&
|
|
e.vertex[ 1 ] != edges[ currentEdge ].vertex[ 0 ] )
|
|
continue;
|
|
|
|
// It's our edge. Continue on with the face that
|
|
// isn't our current one.
|
|
|
|
if( e.face[ 0 ] == currentFace )
|
|
currentFace = e.face[ 1 ];
|
|
else
|
|
currentFace = e.face[ 0 ];
|
|
currentEdge = n;
|
|
break;
|
|
}
|
|
}
|
|
else if( v1Side == PlaneF::On || v2Side == PlaneF::On )
|
|
{
|
|
// One of the points of the current edge is on the plane.
|
|
// Add that point.
|
|
|
|
if( v1Side == PlaneF::On )
|
|
{
|
|
ADD_POINT( points[ v1 ] );
|
|
}
|
|
else
|
|
{
|
|
ADD_POINT( points[ v2 ] );
|
|
}
|
|
|
|
// Find edge to continue with.
|
|
|
|
for( U32 n = 0; n < numEdges; ++ n )
|
|
{
|
|
const typename Base::EdgeType& e = edges[ n ];
|
|
|
|
// Skip current edge.
|
|
if( n == currentEdge )
|
|
continue;
|
|
|
|
// Skip edges not belonging to current face.
|
|
if( e.face[ 0 ] != currentFace && e.face[ 1 ] != currentFace )
|
|
continue;
|
|
|
|
// Skip edges connected to point that is on the plane.
|
|
if( v1Side == PlaneF::On )
|
|
{
|
|
if( e.vertex[ 0 ] == v1 || e.vertex[ 1 ] == v1 )
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if( e.vertex[ 0 ] == v2 || e.vertex[ 1 ] == v2 )
|
|
continue;
|
|
}
|
|
|
|
// Skip edges not intersecting the plane.
|
|
|
|
U32 v1new = e.vertex[ 0 ];
|
|
U32 v2new = e.vertex[ 1 ];
|
|
|
|
const Point3F& p1 = points[ v1new ];
|
|
const Point3F& p2 = points[ v2new ];
|
|
|
|
PlaneF::Side v1SideNew = plane.whichSide( p1 );
|
|
PlaneF::Side v2SideNew = plane.whichSide( p2 );
|
|
|
|
if( v1SideNew != PlaneF::On && v2SideNew != PlaneF::On && !plane.clipSegment( p1, p2, intersection ) )
|
|
continue;
|
|
|
|
// It's our edge. Continue with the face on the
|
|
// opposite side.
|
|
|
|
if( e.face[ 0 ] == currentFace )
|
|
currentFace = e.face[ 1 ];
|
|
else
|
|
currentFace = e.face[ 0 ];
|
|
currentEdge = n;
|
|
|
|
v1 = v1new;
|
|
v2 = v2new;
|
|
|
|
v1Side = v1SideNew;
|
|
v2Side = v2SideNew;
|
|
|
|
break;
|
|
}
|
|
|
|
// Already have computed all the data.
|
|
continue;
|
|
}
|
|
|
|
// It's a clean intersecting somewhere along the edge. Add it.
|
|
|
|
else
|
|
{
|
|
ADD_POINT( intersection );
|
|
}
|
|
|
|
// Find edge to continue with.
|
|
|
|
for( U32 n = 0; n < numEdges; ++ n )
|
|
{
|
|
const typename Base::EdgeType& e = edges[ n ];
|
|
|
|
// Skip current edge.
|
|
if( n == currentEdge )
|
|
continue;
|
|
|
|
// Skip edges not belonging to current face.
|
|
if( e.face[ 0 ] != currentFace && e.face[ 1 ] != currentFace )
|
|
continue;
|
|
|
|
// Skip edges not intersecting the plane.
|
|
|
|
v1 = e.vertex[ 0 ];
|
|
v2 = e.vertex[ 1 ];
|
|
|
|
const Point3F& p1 = points[ v1 ];
|
|
const Point3F& p2 = points[ v2 ];
|
|
|
|
PlaneF::Side v1SideNew = plane.whichSide( p1 );
|
|
PlaneF::Side v2SideNew = plane.whichSide( p2 );
|
|
|
|
if( v1SideNew != PlaneF::On && v2SideNew != PlaneF::On && !plane.clipSegment( p1, p2, intersection ) )
|
|
continue;
|
|
|
|
// It's our edge. Make it the current one.
|
|
|
|
if( e.face[ 0 ] == currentFace )
|
|
currentFace = e.face[ 1 ];
|
|
else
|
|
currentFace = e.face[ 0 ];
|
|
currentEdge = n;
|
|
|
|
break;
|
|
}
|
|
}
|
|
//TODO: I guess this is insufficient; edges with vertices on the plane may lead us to take different
|
|
// paths depending on edge order
|
|
while( currentFace != firstFace &&
|
|
currentEdge != firstEdge );
|
|
|
|
return numOutPoints;
|
|
|
|
#undef ADD_POINT
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< typename Base >
|
|
template< typename IndexType >
|
|
U32 PolyhedronImpl< Base >::extractFace( U32 plane, IndexType* outIndices, U32 maxOutIndices ) const
|
|
{
|
|
AssertFatal( plane < this->getNumPlanes(), "PolyhedronImpl::extractFace - Plane index out of range!" );
|
|
|
|
// This method relies on the fact that vertices on the edges must be CW ordered
|
|
// for face[0]. If that is not the case, it is still possible to infer the correct
|
|
// ordering by finding one edge and a point not on the edge but still on
|
|
// the polygon. By constructing a plane through that edge (simple cross product) and
|
|
// then seeing which side the point is on, we know which direction is the right one
|
|
// for the polygon. The implicit CW ordering spares us from having to do that, though.
|
|
|
|
const U32 numEdges = this->getNumEdges();
|
|
const Edge* edges = this->getEdges();
|
|
|
|
// Find first edge that belongs to the plane.
|
|
|
|
const Edge* firstEdge = 0;
|
|
|
|
for( U32 i = 0; i < numEdges; ++ i )
|
|
{
|
|
const Edge& edge = edges[ i ];
|
|
if( edge.face[ 0 ] == plane || edge.face[ 1 ] == plane )
|
|
{
|
|
firstEdge = &edge;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we have found no edge, the polyhedron is degenerate,
|
|
// so abort.
|
|
|
|
if( !firstEdge )
|
|
return 0;
|
|
|
|
// Choose vertex that begins a CCW traversal for this plane.
|
|
//
|
|
// Note that we expect the planes to be facing inwards by default so we
|
|
// go the opposite direction to yield a polygon facing the other way.
|
|
|
|
U32 idx = 0;
|
|
U32 currentVertex;
|
|
const Edge* currentEdge = firstEdge;
|
|
|
|
if( firstEdge->face[ 0 ] == plane )
|
|
currentVertex = firstEdge->vertex[ 0 ];
|
|
else
|
|
currentVertex = firstEdge->vertex[ 1 ];
|
|
|
|
// Now spider along the edges until we have gathered all indices
|
|
// for the plane in the right order.
|
|
//
|
|
// For larger edge sets, it would be more efficient to first extract
|
|
// all edges for the plane and then loop only over this subset to
|
|
// spider along the indices. However, we tend to have small sets
|
|
// so it should be sufficiently fast to just loop over the original
|
|
// set.
|
|
|
|
do
|
|
{
|
|
// Add the vertex for the current edge.
|
|
|
|
if( idx >= maxOutIndices )
|
|
return 0;
|
|
|
|
outIndices[ idx ++ ] = currentVertex;
|
|
|
|
// Look for next edge.
|
|
|
|
for( U32 i = 0; i < numEdges; ++ i )
|
|
{
|
|
const Edge& edge = edges[ i ];
|
|
|
|
// Skip if we hit the edge that we are looking to continue from.
|
|
|
|
if( &edge == currentEdge )
|
|
continue;
|
|
|
|
// Skip edge if it doesn't belong to the current plane.
|
|
|
|
if( edge.face[ 0 ] != plane && edge.face[ 1 ] != plane )
|
|
continue;
|
|
|
|
// If edge connects to vertex we are looking for, make it
|
|
// the current edge and push its other vertex.
|
|
|
|
if( edge.vertex[ 0 ] == currentVertex )
|
|
currentVertex = edge.vertex[ 1 ];
|
|
else if( edge.vertex[ 1 ] == currentVertex )
|
|
currentVertex = edge.vertex[ 0 ];
|
|
else
|
|
continue; // Skip edge.
|
|
|
|
currentEdge = &edge;
|
|
break;
|
|
}
|
|
}
|
|
while( currentEdge != firstEdge );
|
|
|
|
// Done.
|
|
|
|
return idx;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< typename Polyhedron >
|
|
void PolyhedronData::buildBoxData( Polyhedron& poly, const MatrixF& mat, const Box3F& box, bool invertNormals )
|
|
{
|
|
AssertFatal( poly.getNumPoints() == 8, "PolyhedronData::buildBox - Incorrect point count!" );
|
|
AssertFatal( poly.getNumEdges() == 12, "PolyhedronData::buildBox - Incorrect edge count!" );
|
|
AssertFatal( poly.getNumPlanes() == 6, "PolyhedronData::buildBox - Incorrect plane count!" );
|
|
|
|
// Box is assumed to be axis aligned in the source space.
|
|
// Transform into geometry space.
|
|
|
|
Point3F xvec = mat.getRightVector() * box.len_x();
|
|
Point3F yvec = mat.getForwardVector() * box.len_y();
|
|
Point3F zvec = mat.getUpVector() * box.len_z();
|
|
|
|
Point3F min;
|
|
mat.mulP( box.minExtents, &min );
|
|
|
|
// Corner points.
|
|
|
|
typename Polyhedron::PointListType& pointList = poly.pointList;
|
|
|
|
pointList[ 0 ] = min; // near left bottom
|
|
pointList[ 1 ] = min + yvec; // far left bottom
|
|
pointList[ 2 ] = min + xvec + yvec; // far right bottom
|
|
pointList[ 3 ] = min + xvec; // near right bottom
|
|
pointList[ 4 ] = pointList[ 0 ] + zvec; // near left top
|
|
pointList[ 5 ] = pointList[ 1 ] + zvec; // far left top
|
|
pointList[ 6 ] = pointList[ 2 ] + zvec; // far right top
|
|
pointList[ 7 ] = pointList[ 3 ] + zvec; // near right top
|
|
|
|
// Side planes.
|
|
|
|
typename Polyhedron::PlaneListType& planeList = poly.planeList;
|
|
|
|
const F32 pos = invertNormals ? -1.f : 1.f;
|
|
const F32 neg = - pos;
|
|
|
|
planeList[ 0 ].set( pointList[ 0 ], xvec * pos ); // left
|
|
planeList[ 1 ].set( pointList[ 2 ], yvec * neg ); // far
|
|
planeList[ 2 ].set( pointList[ 2 ], xvec * neg ); // right
|
|
planeList[ 3 ].set( pointList[ 0 ], yvec * pos ); // front
|
|
planeList[ 4 ].set( pointList[ 0 ], zvec * pos ); // bottom
|
|
planeList[ 5 ].set( pointList[ 4 ], zvec * neg ); // top
|
|
|
|
// The edges are constructed so that the vertices
|
|
// are oriented clockwise for face[0].
|
|
|
|
typename Polyhedron::EdgeType* edge = &poly.edgeList[ 0 ];
|
|
|
|
for( U32 i = 0; i < 4; ++ i )
|
|
{
|
|
S32 n = ( i == 3 ) ? 0: i + 1;
|
|
S32 p = ( i == 0 ) ? 3: i - 1;
|
|
|
|
edge->vertex[ 0 ] = !invertNormals ? n : i;
|
|
edge->vertex[ 1 ] = !invertNormals ? i : n;
|
|
edge->face[ 0 ] = i;
|
|
edge->face[ 1 ] = 4;
|
|
edge ++;
|
|
|
|
edge->vertex[ 0 ] = !invertNormals ? 4 + n : 4 + i;
|
|
edge->vertex[ 1 ] = !invertNormals ? 4 + i : 4 + n;
|
|
edge->face[ 0 ] = 5;
|
|
edge->face[ 1 ] = i;
|
|
edge ++;
|
|
|
|
edge->vertex[ 0 ] = !invertNormals ? 4 + i : i;
|
|
edge->vertex[ 1 ] = !invertNormals ? i : 4 + i;
|
|
edge->face[ 0 ] = p;
|
|
edge->face[ 1 ] = i;
|
|
edge ++;
|
|
}
|
|
}
|