//----------------------------------------------------------------------------- // 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 _MSILHOUETTEEXTRACTOR_H_ #define _MSILHOUETTEEXTRACTOR_H_ #ifndef _FRAMEALLOCATOR_H_ #include "core/frameAllocator.h" #endif #ifndef _TVECTOR_H_ #include "core/util/tVector.h" #endif /// @file /// Routines for extracting silhouette polygons from polyhedrons. template< typename Polyhedron > struct SilhouetteExtractorBase { typedef Polyhedron PolyhedronType; protected: /// The polyhedron from which we are extracting silhouettes. const PolyhedronType* mPolyhedron; SilhouetteExtractorBase( const PolyhedronType& polyhedron ) : mPolyhedron( &polyhedron ) {} }; /// Silhouette extraction routines for perspective projections. template< typename Polyhedron > struct SilhouetteExtractorBasePerspective : public SilhouetteExtractorBase< Polyhedron > { private: enum Orientation { FrontFacing, BackFacing }; /// @name Per-Extraction Data /// @{ /// The facing direction of each of the polygons. mutable Orientation* mPolygonOrientations; /// Frame allocator water mark to release temporary memory after silhouette extraction. mutable U32 mWaterMark; /// @} public: SilhouetteExtractorBasePerspective( const Polyhedron& polyhedron ) : SilhouetteExtractorBase< Polyhedron >( polyhedron ), mPolygonOrientations( NULL ), mWaterMark( 0 ) {} /// Initialize extraction. /// /// @param objectView View->object matrix. bool begin( const MatrixF& camView ) const { mWaterMark = FrameAllocator::getWaterMark(); // Determine orientation of each of the polygons. const U32 numPolygons = this->mPolyhedron->getNumPlanes(); mPolygonOrientations = ( Orientation* ) FrameAllocator::alloc( sizeof( Orientation ) * numPolygons ); Point3F camPos = camView.getPosition(); for( U32 i = 0; i < numPolygons; ++ i ) { if (this->mPolyhedron->getPlanes()[i].whichSide( camPos ) == PlaneF::Front) mPolygonOrientations[i] = FrontFacing; else mPolygonOrientations[i] = BackFacing; } return true; } /// End extraction. void end() const { FrameAllocator::setWaterMark( mWaterMark ); mWaterMark = 0; mPolygonOrientations = NULL; } /// Return true if the given edge is a silhouette edge with respect to the /// current perspective transform. /// /// @param edgeIndex Index of edge to test. /// @return True if the given edge is a silhouette when looked at from the given view position. /// /// @note This method depends on inward-facing normals! bool isSilhouetteEdge( U32 edgeIndex ) const { AssertFatal( edgeIndex < this->mPolyhedron->getNumEdges(), "SilhouetteExtractorBasePerspective::isSilhouetteEdge - Index out of range!" ); const typename Polyhedron::EdgeType& edge = this->mPolyhedron->getEdges()[ edgeIndex ]; const U32 face0 = edge.face[ 0 ]; const U32 face1 = edge.face[ 1 ]; return ( mPolygonOrientations[ face0 ] != mPolygonOrientations[ face1 ] ); } }; /// Silhouette extraction routines for orthographic projections. template< typename Polyhedron > struct SilhouetteExtractorBaseOrtho : public SilhouetteExtractorBase< Polyhedron > { private: /// @name Per-Extraction Data /// @{ /// Precomputed dot products between view direction and plane normals /// in the polyhedron. mutable F32* mFaceDotProducts; /// Frame allocator water mark. mutable U32 mWaterMark; /// @} public: SilhouetteExtractorBaseOrtho( const Polyhedron& polyhedron ) : SilhouetteExtractorBase< Polyhedron >( polyhedron ), mFaceDotProducts( NULL ), mWaterMark( 0 ) { } /// Initialize the extractor. void begin( const Point3F& viewDirOS ) const { const typename Polyhedron::PlaneType* planes = this->mPolyhedron->getPlanes(); const U32 numPlanes = this->mPolyhedron->getNumPlanes(); mWaterMark = FrameAllocator::getWaterMark(); mFaceDotProducts = ( F32* ) FrameAllocator::alloc( sizeof( F32 ) * numPlanes ); for( U32 i = 0; i < numPlanes; ++ i ) mFaceDotProducts[ i ] = mDot( planes[ i ], viewDirOS ); } /// Finish extraction. void end() const { FrameAllocator::setWaterMark( mWaterMark ); mFaceDotProducts = NULL; mWaterMark = 0; } /// Return true if the given edge is a silhouette edge with respect to the /// view direction. /// /// @param edgeIndex Index of edge to test. /// @return True if the given edge is a silhouette in the projection along the view direction. /// /// @note This method depends on inward-facing normals! bool isSilhouetteEdge( U32 edgeIndex ) const { AssertFatal( edgeIndex < this->mPolyhedron->getNumEdges(), "SilhouetteExtractorBaseOrtho::isSilhouetteEdge - Index out of range!" ); const typename Polyhedron::EdgeType& edge = this->mPolyhedron->getEdges()[ edgeIndex ]; const U32 face0 = edge.face[ 0 ]; const U32 face1 = edge.face[ 1 ]; // Not a silhouette if both planes are facing the same way. if( mSign( mFaceDotProducts[ face0 ] ) == mSign( mFaceDotProducts[ face1 ] ) ) return false; // 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 = mFaceDotProducts[ face0 ] > 0.f ? face0 : face1; if( mFaceDotProducts[ frontFace ] <= 0.f ) return false; // This face or other face is perpendicular to us. return true; } }; /// Common implementation parts for silhouette extraction. template< typename Base > struct SilhouetteExtractorImpl : public Base { typedef typename Base::PolyhedronType PolyhedronType; SilhouetteExtractorImpl( const PolyhedronType& polyhedron ) : Base( polyhedron ) {} U32 extractSilhouette( U32* outIndices, U32 maxOutIndices ) const { // First, find the silhouette edges. We do this with a brute-force // approach here. This can be optimized (see "Silhouette Algorithms" by Bruce Gooch, Mark // Hartner, and Nathan Beddes). U32 numSilhouetteEdges = 0; const U32 numTotalEdges = this->mPolyhedron->getNumEdges(); const typename PolyhedronType::EdgeType* edges = this->mPolyhedron->getEdges(); FrameTemp< const typename PolyhedronType::EdgeType* > silhouetteEdges( numTotalEdges ); for( U32 i = 0; i < numTotalEdges; ++ i ) if( this->isSilhouetteEdge( i ) ) silhouetteEdges[ numSilhouetteEdges ++ ] = &edges[ i ]; // Allow this to happen rather than asserting as projection-based silhouettes // may fail. if( numSilhouetteEdges < 3 ) return 0; // Now walk the edge list and find the edges that are connected // with each other. From this information, emit the silhouette // polygon. U32 idx = 0; if( idx >= maxOutIndices ) return 0; outIndices[ idx ++ ] = silhouetteEdges[ 0 ]->vertex[ 1 ]; U32 currentIndex = silhouetteEdges[ 0 ]->vertex[ 1 ]; U32 currentEdge = 0; for( U32 i = 1; i < numSilhouetteEdges; ++ i ) { // Find edge that continues on from the current vertex. for( U32 n = 0; n < numSilhouetteEdges; ++ n ) { // Skip current edge. if( n == currentEdge ) continue; if( silhouetteEdges[ n ]->vertex[ 0 ] == currentIndex ) currentIndex = silhouetteEdges[ n ]->vertex[ 1 ]; else if( silhouetteEdges[ n ]->vertex[ 1 ] == currentIndex ) currentIndex = silhouetteEdges[ n ]->vertex[ 0 ]; else continue; if( idx >= maxOutIndices ) return 0; currentEdge = n; outIndices[ idx ++ ] = currentIndex; break; } } return idx; } }; /// Silhouette edge extraction for orthographic projections. template< typename Polyhedron > struct SilhouetteExtractorOrtho { protected: typedef SilhouetteExtractorImpl< SilhouetteExtractorBaseOrtho< Polyhedron > > ExtractorType; /// The actual extractor implementation. ExtractorType mExtractor; public: SilhouetteExtractorOrtho( const Polyhedron& polyhedron ) : mExtractor( polyhedron ) {} /// Generate a silhouette polygon for the polyhedron based on the given view direction. /// /// @param viewDirOS Object-space normalized view vector. /// @param outIndices Array where the resulting vertex indices will be stored. Must have /// enough room. If you don't know the exact size that you need, just allocate one index /// for any point in the mesh. /// @param maxOutIndices The number of indices that can be stored in @a outIndices. If insufficient, /// the return value will be 0. /// /// @return Number of indices written to @a outIndices or 0 if the silhouette extraction failed. /// /// @note Be aware that silhouette polygons are in most cases non-planar! /// @note The direction of the ordering of the resulting indices is undefined meaning that /// different silhouettes extracted from the same polyhedron may have different CCW/CW ordering. /// The only guarantee is that the resulting indices are consecutive. U32 extractSilhouette( const Point3F& viewDirOS, U32* outIndices, U32 maxOutIndices ) const { U32 result = 0; mExtractor.begin( viewDirOS ); result = mExtractor.extractSilhouette( outIndices, maxOutIndices ); mExtractor.end(); return result; } }; /// Silhouette edge extraction for perspective projections. template< typename Polyhedron > struct SilhouetteExtractorPerspective { protected: typedef SilhouetteExtractorImpl< SilhouetteExtractorBasePerspective< Polyhedron > > ExtractorType; /// The actual extractor implementation. ExtractorType mExtractor; public: SilhouetteExtractorPerspective( const Polyhedron& polyhedron ) : mExtractor( polyhedron ) {} /// Generate a silhouette polygon for this polyhedron based on the transforms. /// /// @param camView View->object matrix. /// @param outIndices Array where the resulting vertex indices will be stored. Must have /// enough room. If you don't know the exact size that you need, just allocate one index /// for any point in the mesh. /// @param maxOutIndices The number of indices that can be stored in @a outIndices. If insufficient, /// the return value will be 0. /// /// @return Number of indices written to @a outIndices. /// /// @note Be aware that silhouette polygons are in most cases non-planar! /// @note The direction of the ordering of the resulting indices is undefined meaning that /// different silhouettes extracted from the same polyhedron may have different CCW/CW ordering. /// The only guarantee is that the resulting indices are consecutive. U32 extractSilhouette( const MatrixF& camView, U32* outIndices, U32 maxOutIndices ) const { U32 result = 0; if( mExtractor.begin( camView ) ) result = mExtractor.extractSilhouette( outIndices, maxOutIndices ); mExtractor.end(); return result; } }; #endif // !_MSILHOUETTEEXTRACTOR_H_