Torque3D/Engine/source/math/mSilhouetteExtractor.h
Azaezel fbfd3ed8ed clang: constructor initialization order
while not a major issue per-se, the sheer number of times the engine has to jump back in memory and backfill data in a given class can add up. First run of... many.,
2016-10-14 18:16:55 -05:00

385 lines
13 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 _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_