mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
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.,
385 lines
13 KiB
C++
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_
|