2017-09-01 20:56:09 +00:00
# include "morphgeometry.hpp"
# include <cassert>
2021-10-23 08:31:46 +00:00
# include <components/resource/scenemanager.hpp>
2017-09-01 20:56:09 +00:00
namespace SceneUtil
{
MorphGeometry : : MorphGeometry ( )
: mLastFrameNumber ( 0 )
, mDirty ( true )
, mMorphedBoundingBox ( false )
{
}
MorphGeometry : : MorphGeometry ( const MorphGeometry & copy , const osg : : CopyOp & copyop )
: osg : : Drawable ( copy , copyop )
, mMorphTargets ( copy . mMorphTargets )
, mLastFrameNumber ( 0 )
, mDirty ( true )
, mMorphedBoundingBox ( false )
{
setSourceGeometry ( copy . getSourceGeometry ( ) ) ;
}
void MorphGeometry : : setSourceGeometry ( osg : : ref_ptr < osg : : Geometry > sourceGeom )
{
2021-10-23 08:31:46 +00:00
for ( unsigned int i = 0 ; i < 2 ; + + i )
mGeometry [ i ] = nullptr ;
2017-09-01 20:56:09 +00:00
mSourceGeometry = sourceGeom ;
for ( unsigned int i = 0 ; i < 2 ; + + i )
{
2021-10-23 08:31:46 +00:00
// DO NOT COPY AND PASTE THIS CODE. Cloning osg::Geometry without also cloning its contained Arrays is generally unsafe.
// In this specific case the operation is safe under the following two assumptions:
// - When Arrays are removed or replaced in the cloned geometry, the original Arrays in their place must outlive the cloned geometry regardless. (ensured by TemplateRef)
// - Arrays that we add or replace in the cloned geometry must be explicitely forbidden from reusing BufferObjects of the original geometry. (ensured by vbo below)
2017-09-01 20:56:09 +00:00
mGeometry [ i ] = new osg : : Geometry ( * mSourceGeometry , osg : : CopyOp : : SHALLOW_COPY ) ;
2021-10-23 08:31:46 +00:00
mGeometry [ i ] - > getOrCreateUserDataContainer ( ) - > addUserObject ( new Resource : : TemplateRef ( mSourceGeometry ) ) ;
2017-09-01 20:56:09 +00:00
2017-09-01 21:02:19 +00:00
const osg : : Geometry & from = * mSourceGeometry ;
2017-09-01 20:56:09 +00:00
osg : : Geometry & to = * mGeometry [ i ] ;
to . setSupportsDisplayList ( false ) ;
to . setUseVertexBufferObjects ( true ) ;
to . setCullingActive ( false ) ; // make sure to disable culling since that's handled by this class
// vertices are modified every frame, so we need to deep copy them.
// assign a dedicated VBO to make sure that modifications don't interfere with source geometry's VBO.
osg : : ref_ptr < osg : : VertexBufferObject > vbo ( new osg : : VertexBufferObject ) ;
vbo - > setUsage ( GL_DYNAMIC_DRAW_ARB ) ;
2020-05-16 13:37:00 +00:00
osg : : ref_ptr < osg : : Array > vertexArray = static_cast < osg : : Array * > ( from . getVertexArray ( ) - > clone ( osg : : CopyOp : : DEEP_COPY_ALL ) ) ;
2017-09-01 20:56:09 +00:00
if ( vertexArray )
{
vertexArray - > setVertexBufferObject ( vbo ) ;
to . setVertexArray ( vertexArray ) ;
}
}
}
void MorphGeometry : : addMorphTarget ( osg : : Vec3Array * offsets , float weight )
{
mMorphTargets . push_back ( MorphTarget ( offsets , weight ) ) ;
mMorphedBoundingBox = false ;
dirty ( ) ;
}
void MorphGeometry : : dirty ( )
{
mDirty = true ;
if ( ! mMorphedBoundingBox )
dirtyBound ( ) ;
}
osg : : ref_ptr < osg : : Geometry > MorphGeometry : : getSourceGeometry ( ) const
{
return mSourceGeometry ;
}
void MorphGeometry : : accept ( osg : : NodeVisitor & nv )
{
if ( ! nv . validNodeMask ( * this ) )
return ;
nv . pushOntoNodePath ( this ) ;
if ( nv . getVisitorType ( ) = = osg : : NodeVisitor : : CULL_VISITOR )
cull ( & nv ) ;
else
nv . apply ( * this ) ;
nv . popFromNodePath ( ) ;
}
2017-09-03 14:02:40 +00:00
void MorphGeometry : : accept ( osg : : PrimitiveFunctor & func ) const
{
getGeometry ( mLastFrameNumber ) - > accept ( func ) ;
}
2017-09-01 20:56:09 +00:00
osg : : BoundingBox MorphGeometry : : computeBoundingBox ( ) const
{
bool anyMorphTarget = false ;
2021-11-13 11:11:37 +00:00
for ( unsigned int i = 1 ; i < mMorphTargets . size ( ) ; + + i )
if ( mMorphTargets [ i ] . getWeight ( ) ! = 0 )
2017-09-01 20:56:09 +00:00
{
anyMorphTarget = true ;
break ;
}
// before the MorphGeometry has started animating, we will use a regular bounding box (this is required
// for correct object placements, which uses the bounding box)
if ( ! mMorphedBoundingBox & & ! anyMorphTarget )
{
return mSourceGeometry - > getBoundingBox ( ) ;
}
// once it animates, use a bounding box that encompasses all possible animations so as to avoid recalculating
else
{
mMorphedBoundingBox = true ;
2021-11-13 11:11:37 +00:00
const osg : : Vec3Array * sourceVerts = static_cast < const osg : : Vec3Array * > ( mSourceGeometry - > getVertexArray ( ) ) ;
if ( mMorphTargets . size ( ) ! = 0 )
sourceVerts = mMorphTargets [ 0 ] . getOffsets ( ) ;
std : : vector < osg : : BoundingBox > vertBounds ( sourceVerts - > size ( ) ) ;
2017-09-01 20:56:09 +00:00
// Since we don't know what combinations of morphs are being applied we need to keep track of a bounding box for each vertex.
// The minimum/maximum of the box is the minimum/maximum offset the vertex can have from its starting position.
// Start with zero offsets which will happen when no morphs are applied.
for ( unsigned int i = 0 ; i < vertBounds . size ( ) ; + + i )
vertBounds [ i ] . set ( osg : : Vec3f ( 0 , 0 , 0 ) , osg : : Vec3f ( 0 , 0 , 0 ) ) ;
2021-11-13 11:11:37 +00:00
for ( unsigned int i = 1 ; i < mMorphTargets . size ( ) ; + + i )
2017-09-01 20:56:09 +00:00
{
const osg : : Vec3Array & offsets = * mMorphTargets [ i ] . getOffsets ( ) ;
for ( unsigned int j = 0 ; j < offsets . size ( ) & & j < vertBounds . size ( ) ; + + j )
{
osg : : BoundingBox & bounds = vertBounds [ j ] ;
bounds . expandBy ( bounds . _max + offsets [ j ] ) ;
bounds . expandBy ( bounds . _min + offsets [ j ] ) ;
}
}
osg : : BoundingBox box ;
for ( unsigned int i = 0 ; i < vertBounds . size ( ) ; + + i )
{
2021-11-13 11:11:37 +00:00
vertBounds [ i ] . _max + = ( * sourceVerts ) [ i ] ;
vertBounds [ i ] . _min + = ( * sourceVerts ) [ i ] ;
2017-09-01 20:56:09 +00:00
box . expandBy ( vertBounds [ i ] ) ;
}
return box ;
}
}
void MorphGeometry : : cull ( osg : : NodeVisitor * nv )
{
2021-11-13 11:11:37 +00:00
if ( mLastFrameNumber = = nv - > getTraversalNumber ( ) | | ! mDirty | | mMorphTargets . size ( ) = = 0 )
2017-09-01 20:56:09 +00:00
{
2017-09-01 22:12:40 +00:00
osg : : Geometry & geom = * getGeometry ( mLastFrameNumber ) ;
nv - > pushOntoNodePath ( & geom ) ;
nv - > apply ( geom ) ;
nv - > popFromNodePath ( ) ;
2017-09-01 20:56:09 +00:00
return ;
}
mDirty = false ;
mLastFrameNumber = nv - > getTraversalNumber ( ) ;
osg : : Geometry & geom = * getGeometry ( mLastFrameNumber ) ;
2021-11-13 11:11:37 +00:00
const osg : : Vec3Array * positionSrc = mMorphTargets [ 0 ] . getOffsets ( ) ;
2017-09-01 20:56:09 +00:00
osg : : Vec3Array * positionDst = static_cast < osg : : Vec3Array * > ( geom . getVertexArray ( ) ) ;
assert ( positionSrc - > size ( ) = = positionDst - > size ( ) ) ;
for ( unsigned int vertex = 0 ; vertex < positionSrc - > size ( ) ; + + vertex )
( * positionDst ) [ vertex ] = ( * positionSrc ) [ vertex ] ;
2021-11-13 11:11:37 +00:00
for ( unsigned int i = 1 ; i < mMorphTargets . size ( ) ; + + i )
2017-09-01 20:56:09 +00:00
{
float weight = mMorphTargets [ i ] . getWeight ( ) ;
if ( weight = = 0.f )
continue ;
const osg : : Vec3Array * offsets = mMorphTargets [ i ] . getOffsets ( ) ;
for ( unsigned int vertex = 0 ; vertex < positionSrc - > size ( ) ; + + vertex )
( * positionDst ) [ vertex ] + = ( * offsets ) [ vertex ] * weight ;
}
positionDst - > dirty ( ) ;
2021-10-12 11:47:42 +00:00
geom . osg : : Drawable : : dirtyGLObjects ( ) ;
2018-12-18 19:44:30 +00:00
2017-09-01 20:56:09 +00:00
nv - > pushOntoNodePath ( & geom ) ;
nv - > apply ( geom ) ;
nv - > popFromNodePath ( ) ;
}
osg : : Geometry * MorphGeometry : : getGeometry ( unsigned int frame ) const
{
return mGeometry [ frame % 2 ] ;
}
}