| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 | //-----------------------------------------------------------------------------// 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 _MPOLYHEDRON_IMPL_H_#define _MPOLYHEDRON_IMPL_H_#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.   U32 indexItr = 0;   do    {      // Add the vertex for the current edge.      if( idx >= maxOutIndices )         return 0;      ++indexItr;      if (indexItr >= 3)      {         outIndices[idx++] = firstEdge->vertex[0];         indexItr = 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.mPointList;   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.mPlaneList;   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.mEdgeList[ 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 ++;   }}#endif // _MPOLYHEDRON_IMPL_H_
 |