2011-12-12 04:42:39 +00:00
# include "animation.hpp"
2015-04-22 17:08:56 +00:00
# include <iomanip>
2015-04-23 18:41:31 +00:00
# include <limits>
2015-04-22 17:08:56 +00:00
2015-04-16 23:23:37 +00:00
# include <osg/TexGen>
# include <osg/TexEnvCombine>
2015-04-23 18:41:31 +00:00
# include <osg/MatrixTransform>
2015-09-16 14:15:55 +00:00
# include <osg/BlendFunc>
# include <osg/Material>
2015-04-16 23:23:37 +00:00
2015-06-19 16:38:44 +00:00
# include <osgParticle/ParticleSystem>
2016-05-19 20:34:27 +00:00
# include <osgParticle/ParticleProcessor>
2015-06-19 16:38:44 +00:00
2015-04-12 13:34:50 +00:00
# include <components/nifosg/nifloader.hpp>
2013-08-06 07:04:39 +00:00
2015-04-12 13:34:50 +00:00
# include <components/resource/resourcesystem.hpp>
# include <components/resource/scenemanager.hpp>
2015-12-12 17:52:06 +00:00
# include <components/resource/keyframemanager.hpp>
2016-02-05 22:03:53 +00:00
# include <components/resource/imagemanager.hpp>
2013-08-07 02:45:07 +00:00
2015-04-23 18:41:31 +00:00
# include <components/nifosg/nifloader.hpp> // KeyframeHolder
2015-06-14 21:13:26 +00:00
# include <components/nifosg/controller.hpp>
2015-04-23 18:41:31 +00:00
# include <components/vfs/manager.hpp>
2015-04-16 23:23:37 +00:00
# include <components/sceneutil/statesetupdater.hpp>
2015-04-18 23:57:52 +00:00
# include <components/sceneutil/visitor.hpp>
2015-04-19 13:03:08 +00:00
# include <components/sceneutil/lightmanager.hpp>
2016-01-06 12:19:15 +00:00
# include <components/sceneutil/lightutil.hpp>
2015-04-23 21:30:06 +00:00
# include <components/sceneutil/skeleton.hpp>
2015-11-20 20:57:04 +00:00
# include <components/sceneutil/positionattitudetransform.hpp>
2015-04-16 23:23:37 +00:00
2016-01-06 11:46:06 +00:00
# include <components/fallback/fallback.hpp>
2015-04-16 23:23:37 +00:00
# include "../mwbase/environment.hpp"
# include "../mwbase/world.hpp"
# include "../mwworld/esmstore.hpp"
# include "../mwworld/class.hpp"
2015-06-02 15:02:56 +00:00
# include "../mwworld/cellstore.hpp"
2015-04-16 23:23:37 +00:00
2015-05-12 15:40:42 +00:00
# include "../mwmechanics/character.hpp" // FIXME: for MWMechanics::Priority
2015-04-18 23:57:52 +00:00
# include "vismask.hpp"
2015-04-19 15:55:56 +00:00
# include "util.hpp"
2015-06-22 19:06:27 +00:00
# include "rotatecontroller.hpp"
2015-04-18 23:57:52 +00:00
2015-04-16 23:23:37 +00:00
namespace
{
2016-05-19 20:34:27 +00:00
/// Removes all particle systems and related nodes in a subgraph.
class RemoveParticlesVisitor : public osg : : NodeVisitor
{
public :
RemoveParticlesVisitor ( )
: osg : : NodeVisitor ( TRAVERSE_ALL_CHILDREN )
{ }
virtual void apply ( osg : : Node & node )
{
if ( dynamic_cast < osgParticle : : ParticleProcessor * > ( & node ) )
mToRemove . push_back ( & node ) ;
traverse ( node ) ;
}
virtual void apply ( osg : : Drawable & drw )
{
if ( osgParticle : : ParticleSystem * partsys = dynamic_cast < osgParticle : : ParticleSystem * > ( & drw ) )
mToRemove . push_back ( partsys ) ;
}
void remove ( )
{
for ( std : : vector < osg : : ref_ptr < osg : : Node > > : : iterator it = mToRemove . begin ( ) ; it ! = mToRemove . end ( ) ; + + it )
{
// FIXME: a Drawable might have more than one parent
osg : : Node * node = * it ;
if ( node - > getNumParents ( ) )
node - > getParent ( 0 ) - > removeChild ( node ) ;
}
mToRemove . clear ( ) ;
}
private :
std : : vector < osg : : ref_ptr < osg : : Node > > mToRemove ;
} ;
2015-04-16 23:23:37 +00:00
class GlowUpdater : public SceneUtil : : StateSetUpdater
{
public :
2016-08-06 18:08:46 +00:00
GlowUpdater ( int texUnit , osg : : Vec4f color , const std : : vector < osg : : ref_ptr < osg : : Texture2D > > & textures ,
osg : : ref_ptr < osg : : Node > node , float maxduration , Resource : : ResourceSystem * resourcesystem , osg : : Uniform * uniform )
2016-02-18 22:05:44 +00:00
: mTexUnit ( texUnit )
2015-04-16 23:23:37 +00:00
, mColor ( color )
, mTextures ( textures )
2016-08-06 18:08:46 +00:00
, mNode ( node )
, mMaxDuration ( maxduration )
, mStartingTime ( 0 )
, mResourceSystem ( resourcesystem )
, mDone ( false )
, mUniform ( uniform )
, mWatchedSpellGlow ( NULL )
2015-04-16 23:23:37 +00:00
{
}
virtual void setDefaults ( osg : : StateSet * stateset )
{
stateset - > setTextureMode ( mTexUnit , GL_TEXTURE_2D , osg : : StateAttribute : : ON ) ;
osg : : TexGen * texGen = new osg : : TexGen ;
texGen - > setMode ( osg : : TexGen : : SPHERE_MAP ) ;
stateset - > setTextureAttributeAndModes ( mTexUnit , texGen , osg : : StateAttribute : : ON | osg : : StateAttribute : : OVERRIDE ) ;
osg : : TexEnvCombine * texEnv = new osg : : TexEnvCombine ;
texEnv - > setSource0_RGB ( osg : : TexEnvCombine : : CONSTANT ) ;
texEnv - > setConstantColor ( mColor ) ;
texEnv - > setCombine_RGB ( osg : : TexEnvCombine : : INTERPOLATE ) ;
texEnv - > setSource2_RGB ( osg : : TexEnvCombine : : TEXTURE ) ;
texEnv - > setOperand2_RGB ( osg : : TexEnvCombine : : SRC_COLOR ) ;
stateset - > setTextureAttributeAndModes ( mTexUnit , texEnv , osg : : StateAttribute : : ON ) ;
2016-08-06 18:08:46 +00:00
// Reduce the texture list back down by one when a temporary glow finishes
// to allow FindLowestUnusedTexUnitVisitor to choose the same texunit again.
if ( mDone )
stateset - > getTextureAttributeList ( ) . resize ( stateset - > getTextureAttributeList ( ) . size ( ) - 1 ) ;
2015-04-16 23:23:37 +00:00
}
virtual void apply ( osg : : StateSet * stateset , osg : : NodeVisitor * nv )
{
2016-08-06 18:08:46 +00:00
if ( mDone )
return ;
// If there is a permanent enchantment glow already on this object, give the
// permanent glow a pointer to a temporary glow added in a nested update callback
// The permanent glow will remove the temporary glow when it finishes, allowing
// the permanent glow to display its glow texture cycling properly.
if ( mWatchedSpellGlow ! = NULL & & mWatchedSpellGlow - > isDone ( ) )
{
mNode - > removeUpdateCallback ( mWatchedSpellGlow ) ;
mWatchedSpellGlow = NULL ;
}
// Set the starting time to measure glow duration from if this is a temporary glow
if ( ( mMaxDuration > = 0 ) & & mStartingTime = = 0 )
mStartingTime = nv - > getFrameStamp ( ) - > getSimulationTime ( ) ;
2015-04-16 23:23:37 +00:00
float time = nv - > getFrameStamp ( ) - > getSimulationTime ( ) ;
int index = ( int ) ( time * 16 ) % mTextures . size ( ) ;
stateset - > setTextureAttribute ( mTexUnit , mTextures [ index ] , osg : : StateAttribute : : ON | osg : : StateAttribute : : OVERRIDE ) ;
2016-08-06 18:08:46 +00:00
if ( ( mMaxDuration > = 0 ) & & ( time - mStartingTime > mMaxDuration ) ) // If this is a temporary glow and it has finished its duration
{
osg : : ref_ptr < osg : : StateSet > writableStateSet = NULL ;
if ( ! mNode - > getStateSet ( ) )
writableStateSet = mNode - > getOrCreateStateSet ( ) ;
else
writableStateSet = osg : : clone ( mNode - > getStateSet ( ) , osg : : CopyOp : : SHALLOW_COPY ) ;
for ( size_t index = 0 ; index < mTextures . size ( ) ; index + + )
{
writableStateSet - > removeTextureAttribute ( mTexUnit , mTextures [ index ] ) ;
}
writableStateSet - > removeUniform ( mUniform ) ; // remove the uniform given to the temporary glow in addglow()
mNode - > setStateSet ( writableStateSet ) ;
this - > reset ( ) ; // without this a texture from the glow will continue to show on the object
mResourceSystem - > getSceneManager ( ) - > recreateShaders ( mNode ) ;
mDone = true ;
}
}
bool isSpellGlowUpdater ( )
{
return ( mMaxDuration > = 0 ) ;
2015-04-16 23:23:37 +00:00
}
2016-08-06 18:08:46 +00:00
bool isEnchantmentGlowUpdater ( )
2016-08-02 12:37:39 +00:00
{
2016-08-06 18:08:46 +00:00
return ( mMaxDuration < 0 ) ;
2016-08-02 12:37:39 +00:00
}
2016-08-06 18:08:46 +00:00
bool isDone ( )
2016-08-02 12:37:39 +00:00
{
2016-08-06 18:08:46 +00:00
return mDone ;
2016-08-02 12:37:39 +00:00
}
2016-08-06 18:08:46 +00:00
int getTexUnit ( )
2016-08-02 12:37:39 +00:00
{
return mTexUnit ;
}
2016-08-06 18:08:46 +00:00
void setWatchedSpellGlow ( osg : : ref_ptr < GlowUpdater > watched )
{
mWatchedSpellGlow = watched ;
}
osg : : ref_ptr < GlowUpdater > getWatchedSpellGlow ( )
{
return mWatchedSpellGlow ;
}
2015-04-16 23:23:37 +00:00
private :
int mTexUnit ;
osg : : Vec4f mColor ;
std : : vector < osg : : ref_ptr < osg : : Texture2D > > mTextures ;
2016-08-06 18:08:46 +00:00
osg : : ref_ptr < osg : : Node > mNode ;
float mMaxDuration ;
float mStartingTime ;
Resource : : ResourceSystem * mResourceSystem ;
bool mDone ;
osg : : Uniform * mUniform ;
osg : : ref_ptr < GlowUpdater > mWatchedSpellGlow ;
2015-04-16 23:23:37 +00:00
} ;
2015-04-23 18:41:31 +00:00
class NodeMapVisitor : public osg : : NodeVisitor
{
public :
2016-02-09 15:18:19 +00:00
typedef std : : map < std : : string , osg : : ref_ptr < osg : : MatrixTransform > > NodeMap ;
NodeMapVisitor ( NodeMap & map )
: osg : : NodeVisitor ( osg : : NodeVisitor : : TRAVERSE_ALL_CHILDREN )
, mMap ( map )
{ }
2015-04-23 18:41:31 +00:00
void apply ( osg : : MatrixTransform & trans )
{
mMap [ Misc : : StringUtils : : lowerCase ( trans . getName ( ) ) ] = & trans ;
traverse ( trans ) ;
}
private :
2016-02-09 15:18:19 +00:00
NodeMap & mMap ;
2015-04-23 18:41:31 +00:00
} ;
NifOsg : : TextKeyMap : : const_iterator findGroupStart ( const NifOsg : : TextKeyMap & keys , const std : : string & groupname )
{
NifOsg : : TextKeyMap : : const_iterator iter ( keys . begin ( ) ) ;
for ( ; iter ! = keys . end ( ) ; + + iter )
{
if ( iter - > second . compare ( 0 , groupname . size ( ) , groupname ) = = 0 & &
iter - > second . compare ( groupname . size ( ) , 2 , " : " ) = = 0 )
break ;
}
return iter ;
}
float calcAnimVelocity ( const std : : multimap < float , std : : string > & keys ,
NifOsg : : KeyframeController * nonaccumctrl , const osg : : Vec3f & accum , const std : : string & groupname )
{
const std : : string start = groupname + " : start " ;
const std : : string loopstart = groupname + " : loop start " ;
const std : : string loopstop = groupname + " : loop stop " ;
const std : : string stop = groupname + " : stop " ;
float starttime = std : : numeric_limits < float > : : max ( ) ;
float stoptime = 0.0f ;
// Pick the last Loop Stop key and the last Loop Start key.
// This is required because of broken text keys in AshVampire.nif.
// It has *two* WalkForward: Loop Stop keys at different times, the first one is used for stopping playback
// but the animation velocity calculation uses the second one.
// As result the animation velocity calculation is not correct, and this incorrect velocity must be replicated,
// because otherwise the Creature's Speed (dagoth uthol) would not be sufficient to move fast enough.
NifOsg : : TextKeyMap : : const_reverse_iterator keyiter ( keys . rbegin ( ) ) ;
while ( keyiter ! = keys . rend ( ) )
{
if ( keyiter - > second = = start | | keyiter - > second = = loopstart )
{
starttime = keyiter - > first ;
break ;
}
+ + keyiter ;
}
keyiter = keys . rbegin ( ) ;
while ( keyiter ! = keys . rend ( ) )
{
if ( keyiter - > second = = stop )
stoptime = keyiter - > first ;
else if ( keyiter - > second = = loopstop )
{
stoptime = keyiter - > first ;
break ;
}
+ + keyiter ;
}
if ( stoptime > starttime )
{
osg : : Vec3f startpos = osg : : componentMultiply ( nonaccumctrl - > getTranslation ( starttime ) , accum ) ;
osg : : Vec3f endpos = osg : : componentMultiply ( nonaccumctrl - > getTranslation ( stoptime ) , accum ) ;
return ( startpos - endpos ) . length ( ) / ( stoptime - starttime ) ;
}
return 0.0f ;
}
2015-05-21 21:24:22 +00:00
2015-11-11 16:04:06 +00:00
/// @brief Base class for visitors that remove nodes from a scene graph.
/// Subclasses need to fill the mToRemove vector.
/// To use, node->accept(removeVisitor); removeVisitor.remove();
class RemoveVisitor : public osg : : NodeVisitor
2015-05-21 21:24:22 +00:00
{
public :
2015-11-11 16:04:06 +00:00
RemoveVisitor ( )
2015-05-21 21:24:22 +00:00
: osg : : NodeVisitor ( TRAVERSE_ALL_CHILDREN )
{
}
2015-11-11 16:04:06 +00:00
void remove ( )
{
for ( RemoveVec : : iterator it = mToRemove . begin ( ) ; it ! = mToRemove . end ( ) ; + + it )
it - > second - > removeChild ( it - > first ) ;
}
protected :
// <node to remove, parent node to remove it from>
typedef std : : vector < std : : pair < osg : : Node * , osg : : Group * > > RemoveVec ;
std : : vector < std : : pair < osg : : Node * , osg : : Group * > > mToRemove ;
} ;
// Removes all drawables from a graph.
class RemoveDrawableVisitor : public RemoveVisitor
{
public :
2015-11-10 17:21:56 +00:00
virtual void apply ( osg : : Drawable & drw )
{
2015-11-11 16:04:06 +00:00
applyImpl ( drw ) ;
2015-11-10 17:21:56 +00:00
}
2015-11-11 16:04:06 +00:00
void applyImpl ( osg : : Node & node )
2015-05-21 21:24:22 +00:00
{
2015-11-11 16:04:06 +00:00
osg : : NodePath : : iterator parent = getNodePath ( ) . end ( ) - 2 ;
// We know that the parent is a Group because only Groups can have children.
osg : : Group * parentGroup = static_cast < osg : : Group * > ( * parent ) ;
// Try to prune nodes that would be empty after the removal
if ( parent ! = getNodePath ( ) . begin ( ) )
2015-05-21 21:24:22 +00:00
{
2015-11-11 16:04:06 +00:00
// This could be extended to remove the parent's parent, and so on if they are empty as well.
// But for NIF files, there won't be a benefit since only TriShapes can be set to STATIC dataVariance.
osg : : Group * parentParent = static_cast < osg : : Group * > ( * ( parent - 1 ) ) ;
if ( parentGroup - > getNumChildren ( ) = = 1 & & parentGroup - > getDataVariance ( ) = = osg : : Object : : STATIC )
{
mToRemove . push_back ( std : : make_pair ( parentGroup , parentParent ) ) ;
return ;
}
2015-05-21 21:24:22 +00:00
}
2015-11-11 16:04:06 +00:00
mToRemove . push_back ( std : : make_pair ( & node , parentGroup ) ) ;
}
2015-05-21 21:24:22 +00:00
} ;
2015-11-11 16:04:06 +00:00
class RemoveTriBipVisitor : public RemoveVisitor
2015-08-15 17:01:21 +00:00
{
public :
2015-11-10 17:21:56 +00:00
virtual void apply ( osg : : Drawable & drw )
{
applyImpl ( drw ) ;
}
void applyImpl ( osg : : Node & node )
2015-08-15 17:01:21 +00:00
{
const std : : string toFind = " tri bip " ;
if ( Misc : : StringUtils : : ciCompareLen ( node . getName ( ) , toFind , toFind . size ( ) ) = = 0 )
{
2015-11-11 16:04:06 +00:00
osg : : Group * parent = static_cast < osg : : Group * > ( * ( getNodePath ( ) . end ( ) - 2 ) ) ;
2015-08-15 17:01:21 +00:00
// Not safe to remove in apply(), since the visitor is still iterating the child list
2015-11-11 16:04:06 +00:00
mToRemove . push_back ( std : : make_pair ( & node , parent ) ) ;
2015-08-15 17:01:21 +00:00
}
}
} ;
2015-04-16 23:23:37 +00:00
}
2013-11-19 23:07:26 +00:00
2012-07-13 10:51:58 +00:00
namespace MWRender
{
2012-07-13 03:12:18 +00:00
2015-06-14 21:13:26 +00:00
struct Animation : : AnimSource
{
osg : : ref_ptr < const NifOsg : : KeyframeHolder > mKeyframes ;
typedef std : : map < std : : string , osg : : ref_ptr < NifOsg : : KeyframeController > > ControllerMap ;
2015-07-09 16:47:11 +00:00
ControllerMap mControllerMap [ Animation : : sNumBlendMasks ] ;
2015-06-14 21:13:26 +00:00
2016-05-19 20:30:14 +00:00
const std : : multimap < float , std : : string > & getTextKeys ( ) const ;
2015-06-14 21:13:26 +00:00
} ;
2015-04-23 20:46:07 +00:00
class ResetAccumRootCallback : public osg : : NodeCallback
{
public :
virtual void operator ( ) ( osg : : Node * node , osg : : NodeVisitor * nv )
{
osg : : MatrixTransform * transform = static_cast < osg : : MatrixTransform * > ( node ) ;
osg : : Matrix mat = transform - > getMatrix ( ) ;
osg : : Vec3f position = mat . getTrans ( ) ;
position = osg : : componentMultiply ( mResetAxes , position ) ;
mat . setTrans ( position ) ;
transform - > setMatrix ( mat ) ;
traverse ( node , nv ) ;
}
void setAccumulate ( const osg : : Vec3f & accumulate )
{
// anything that accumulates (1.f) should be reset in the callback to (0.f)
mResetAxes . x ( ) = accumulate . x ( ) ! = 0.f ? 0.f : 1.f ;
mResetAxes . y ( ) = accumulate . y ( ) ! = 0.f ? 0.f : 1.f ;
mResetAxes . z ( ) = accumulate . z ( ) ! = 0.f ? 0.f : 1.f ;
}
private :
osg : : Vec3f mResetAxes ;
} ;
2015-04-15 20:11:38 +00:00
Animation : : Animation ( const MWWorld : : Ptr & ptr , osg : : ref_ptr < osg : : Group > parentNode , Resource : : ResourceSystem * resourceSystem )
2015-05-23 20:44:00 +00:00
: mInsert ( parentNode )
2015-12-03 19:06:00 +00:00
, mSkeleton ( NULL )
2016-02-09 15:18:19 +00:00
, mNodeMapCreated ( false )
2015-05-23 20:44:00 +00:00
, mPtr ( ptr )
2015-04-12 13:34:50 +00:00
, mResourceSystem ( resourceSystem )
2015-04-23 20:46:07 +00:00
, mAccumulate ( 1.f , 1.f , 0.f )
2015-05-21 22:55:43 +00:00
, mTextKeyListener ( NULL )
2015-06-22 19:06:27 +00:00
, mHeadYawRadians ( 0.f )
, mHeadPitchRadians ( 0.f )
2015-09-16 14:15:55 +00:00
, mAlpha ( 1.f )
2013-05-07 23:59:32 +00:00
{
2015-07-09 16:47:11 +00:00
for ( size_t i = 0 ; i < sNumBlendMasks ; i + + )
2015-06-04 18:33:20 +00:00
mAnimationTimePtr [ i ] . reset ( new AnimationTime ) ;
2014-05-26 18:37:12 +00:00
}
2013-05-12 12:08:01 +00:00
2015-04-12 13:34:50 +00:00
Animation : : ~ Animation ( )
2013-05-11 01:37:44 +00:00
{
2015-07-02 15:19:30 +00:00
setLightEffect ( 0.f ) ;
2015-04-12 13:34:50 +00:00
if ( mObjectRoot )
mInsert - > removeChild ( mObjectRoot ) ;
2013-05-11 01:37:44 +00:00
}
2016-05-19 20:30:14 +00:00
MWWorld : : ConstPtr Animation : : getPtr ( ) const
2015-05-21 21:54:39 +00:00
{
return mPtr ;
}
2015-04-29 21:48:08 +00:00
void Animation : : setActive ( bool active )
{
2015-12-03 19:06:00 +00:00
if ( mSkeleton )
mSkeleton - > setActive ( active ) ;
2015-04-29 21:48:08 +00:00
}
2015-04-22 17:08:56 +00:00
void Animation : : updatePtr ( const MWWorld : : Ptr & ptr )
{
mPtr = ptr ;
}
2015-04-23 18:41:31 +00:00
void Animation : : setAccumulation ( const osg : : Vec3f & accum )
{
mAccumulate = accum ;
2015-04-23 20:46:07 +00:00
if ( mResetAccumRootCallback )
mResetAccumRootCallback - > setAccumulate ( mAccumulate ) ;
2015-04-23 18:41:31 +00:00
}
2016-05-19 20:30:14 +00:00
size_t Animation : : detectBlendMask ( const osg : : Node * node ) const
2015-04-23 18:41:31 +00:00
{
2015-07-09 16:47:11 +00:00
static const char sBlendMaskRoots [ sNumBlendMasks ] [ 32 ] = {
2015-04-23 18:41:31 +00:00
" " , /* Lower body / character root */
" Bip01 Spine1 " , /* Torso */
" Bip01 L Clavicle " , /* Left arm */
" Bip01 R Clavicle " , /* Right arm */
} ;
while ( node ! = mObjectRoot )
{
const std : : string & name = node - > getName ( ) ;
2015-07-09 16:47:11 +00:00
for ( size_t i = 1 ; i < sNumBlendMasks ; i + + )
2015-04-23 18:41:31 +00:00
{
2015-07-09 16:47:11 +00:00
if ( name = = sBlendMaskRoots [ i ] )
2015-04-23 18:41:31 +00:00
return i ;
}
assert ( node - > getNumParents ( ) > 0 ) ;
node = node - > getParent ( 0 ) ;
}
return 0 ;
}
2016-05-19 20:30:14 +00:00
const std : : multimap < float , std : : string > & Animation : : AnimSource : : getTextKeys ( ) const
2015-04-23 18:41:31 +00:00
{
return mKeyframes - > mTextKeys ;
}
2015-04-22 17:08:56 +00:00
void Animation : : addAnimSource ( const std : : string & model )
{
2015-04-23 18:41:31 +00:00
std : : string kfname = model ;
2015-12-07 21:49:15 +00:00
Misc : : StringUtils : : lowerCaseInPlace ( kfname ) ;
2015-04-23 18:41:31 +00:00
if ( kfname . size ( ) > 4 & & kfname . compare ( kfname . size ( ) - 4 , 4 , " .nif " ) = = 0 )
kfname . replace ( kfname . size ( ) - 4 , 4 , " .kf " ) ;
2015-12-03 19:11:28 +00:00
else
return ;
2015-04-23 18:41:31 +00:00
if ( ! mResourceSystem - > getVFS ( ) - > exists ( kfname ) )
return ;
boost : : shared_ptr < AnimSource > animsrc ;
animsrc . reset ( new AnimSource ) ;
2015-12-12 17:52:06 +00:00
animsrc - > mKeyframes = mResourceSystem - > getKeyframeManager ( ) - > get ( kfname ) ;
2015-04-23 18:41:31 +00:00
2015-11-16 22:26:43 +00:00
if ( ! animsrc - > mKeyframes | | animsrc - > mKeyframes - > mTextKeys . empty ( ) | | animsrc - > mKeyframes - > mKeyframeControllers . empty ( ) )
2015-04-23 18:41:31 +00:00
return ;
2016-02-09 15:18:19 +00:00
const NodeMap & nodeMap = getNodeMap ( ) ;
2015-04-23 18:41:31 +00:00
for ( NifOsg : : KeyframeHolder : : KeyframeControllerMap : : const_iterator it = animsrc - > mKeyframes - > mKeyframeControllers . begin ( ) ;
it ! = animsrc - > mKeyframes - > mKeyframeControllers . end ( ) ; + + it )
{
std : : string bonename = Misc : : StringUtils : : lowerCase ( it - > first ) ;
2016-02-09 15:18:19 +00:00
NodeMap : : const_iterator found = nodeMap . find ( bonename ) ;
if ( found = = nodeMap . end ( ) )
2015-06-15 20:42:14 +00:00
{
std : : cerr < < " addAnimSource: can't find bone ' " + bonename < < " ' in " < < model < < " (referenced by " < < kfname < < " ) " < < std : : endl ;
continue ;
}
2015-04-23 18:41:31 +00:00
osg : : Node * node = found - > second ;
2015-07-09 16:47:11 +00:00
size_t blendMask = detectBlendMask ( node ) ;
2015-04-23 18:41:31 +00:00
// clone the controller, because each Animation needs its own ControllerSource
osg : : ref_ptr < NifOsg : : KeyframeController > cloned = osg : : clone ( it - > second . get ( ) , osg : : CopyOp : : DEEP_COPY_ALL ) ;
2015-07-09 16:47:11 +00:00
cloned - > setSource ( mAnimationTimePtr [ blendMask ] ) ;
2015-04-23 18:41:31 +00:00
2015-07-09 16:47:11 +00:00
animsrc - > mControllerMap [ blendMask ] . insert ( std : : make_pair ( bonename , cloned ) ) ;
2015-04-23 18:41:31 +00:00
}
mAnimSources . push_back ( animsrc ) ;
2015-04-22 17:08:56 +00:00
2015-04-23 18:41:31 +00:00
SceneUtil : : AssignControllerSourcesVisitor assignVisitor ( mAnimationTimePtr [ 0 ] ) ;
mObjectRoot - > accept ( assignVisitor ) ;
if ( ! mAccumRoot )
{
2016-02-09 15:18:19 +00:00
NodeMap : : const_iterator found = nodeMap . find ( " root bone " ) ;
if ( found = = nodeMap . end ( ) )
found = nodeMap . find ( " bip01 " ) ;
2015-04-23 18:41:31 +00:00
2016-02-09 15:18:19 +00:00
if ( found ! = nodeMap . end ( ) )
2015-04-23 18:41:31 +00:00
mAccumRoot = found - > second ;
}
}
2015-04-23 21:30:06 +00:00
void Animation : : clearAnimSources ( )
{
mStates . clear ( ) ;
2015-07-09 16:47:11 +00:00
for ( size_t i = 0 ; i < sNumBlendMasks ; i + + )
2015-05-14 14:33:41 +00:00
mAnimationTimePtr [ i ] - > setTimePtr ( boost : : shared_ptr < float > ( ) ) ;
2015-04-23 21:30:06 +00:00
mAccumCtrl = NULL ;
mAnimSources . clear ( ) ;
}
2016-05-19 20:30:14 +00:00
bool Animation : : hasAnimation ( const std : : string & anim ) const
2015-04-23 18:41:31 +00:00
{
AnimSourceList : : const_iterator iter ( mAnimSources . begin ( ) ) ;
for ( ; iter ! = mAnimSources . end ( ) ; + + iter )
{
const NifOsg : : TextKeyMap & keys = ( * iter ) - > getTextKeys ( ) ;
if ( findGroupStart ( keys , anim ) ! = keys . end ( ) )
return true ;
}
return false ;
}
float Animation : : getStartTime ( const std : : string & groupname ) const
{
for ( AnimSourceList : : const_iterator iter ( mAnimSources . begin ( ) ) ; iter ! = mAnimSources . end ( ) ; + + iter )
{
const NifOsg : : TextKeyMap & keys = ( * iter ) - > getTextKeys ( ) ;
NifOsg : : TextKeyMap : : const_iterator found = findGroupStart ( keys , groupname ) ;
if ( found ! = keys . end ( ) )
return found - > first ;
}
return - 1.f ;
}
float Animation : : getTextKeyTime ( const std : : string & textKey ) const
{
for ( AnimSourceList : : const_iterator iter ( mAnimSources . begin ( ) ) ; iter ! = mAnimSources . end ( ) ; + + iter )
{
const NifOsg : : TextKeyMap & keys = ( * iter ) - > getTextKeys ( ) ;
for ( NifOsg : : TextKeyMap : : const_iterator iterKey ( keys . begin ( ) ) ; iterKey ! = keys . end ( ) ; + + iterKey )
{
if ( iterKey - > second . compare ( 0 , textKey . size ( ) , textKey ) = = 0 )
return iterKey - > first ;
}
}
return - 1.f ;
}
void Animation : : handleTextKey ( AnimState & state , const std : : string & groupname , const std : : multimap < float , std : : string > : : const_iterator & key ,
const std : : multimap < float , std : : string > & map )
{
2015-04-23 20:46:07 +00:00
const std : : string & evt = key - > second ;
size_t off = groupname . size ( ) + 2 ;
size_t len = evt . size ( ) - off ;
2015-05-29 21:38:31 +00:00
if ( evt . compare ( 0 , groupname . size ( ) , groupname ) = = 0 & &
evt . compare ( groupname . size ( ) , 2 , " : " ) = = 0 )
{
if ( evt . compare ( off , len , " loop start " ) = = 0 )
state . mLoopStartTime = key - > first ;
else if ( evt . compare ( off , len , " loop stop " ) = = 0 )
state . mLoopStopTime = key - > first ;
}
2015-04-23 20:46:07 +00:00
2015-05-21 22:55:43 +00:00
if ( mTextKeyListener )
2015-12-18 01:36:34 +00:00
{
try
{
mTextKeyListener - > handleTextKey ( groupname , key , map ) ;
}
catch ( std : : exception & e )
{
std : : cerr < < " Error handling text key " < < evt < < " : " < < e . what ( ) < < std : : endl ;
}
}
2015-04-23 18:41:31 +00:00
}
2015-07-18 01:23:41 +00:00
void Animation : : play ( const std : : string & groupname , const AnimPriority & priority , int blendMask , bool autodisable , float speedmult ,
2015-04-23 18:41:31 +00:00
const std : : string & start , const std : : string & stop , float startpoint , size_t loops , bool loopfallback )
{
if ( ! mObjectRoot | | mAnimSources . empty ( ) )
return ;
if ( groupname . empty ( ) )
{
resetActiveGroups ( ) ;
return ;
}
AnimStateMap : : iterator stateiter = mStates . begin ( ) ;
while ( stateiter ! = mStates . end ( ) )
{
if ( stateiter - > second . mPriority = = priority )
mStates . erase ( stateiter + + ) ;
else
+ + stateiter ;
}
stateiter = mStates . find ( groupname ) ;
if ( stateiter ! = mStates . end ( ) )
{
stateiter - > second . mPriority = priority ;
resetActiveGroups ( ) ;
return ;
}
/* Look in reverse; last-inserted source has priority. */
AnimState state ;
AnimSourceList : : reverse_iterator iter ( mAnimSources . rbegin ( ) ) ;
for ( ; iter ! = mAnimSources . rend ( ) ; + + iter )
{
const NifOsg : : TextKeyMap & textkeys = ( * iter ) - > getTextKeys ( ) ;
if ( reset ( state , textkeys , groupname , start , stop , startpoint , loopfallback ) )
{
state . mSource = * iter ;
state . mSpeedMult = speedmult ;
state . mLoopCount = loops ;
2015-05-14 14:33:41 +00:00
state . mPlaying = ( state . getTime ( ) < state . mStopTime ) ;
2015-04-23 18:41:31 +00:00
state . mPriority = priority ;
2015-07-09 16:47:11 +00:00
state . mBlendMask = blendMask ;
2015-04-23 18:41:31 +00:00
state . mAutoDisable = autodisable ;
mStates [ groupname ] = state ;
2015-05-14 14:33:41 +00:00
NifOsg : : TextKeyMap : : const_iterator textkey ( textkeys . lower_bound ( state . getTime ( ) ) ) ;
2015-04-23 18:41:31 +00:00
if ( state . mPlaying )
{
2015-05-14 14:33:41 +00:00
while ( textkey ! = textkeys . end ( ) & & textkey - > first < = state . getTime ( ) )
2015-04-23 18:41:31 +00:00
{
handleTextKey ( state , groupname , textkey , textkeys ) ;
+ + textkey ;
}
}
2015-05-14 14:33:41 +00:00
if ( state . getTime ( ) > = state . mLoopStopTime & & state . mLoopCount > 0 )
2015-04-23 18:41:31 +00:00
{
state . mLoopCount - - ;
2015-05-14 14:33:41 +00:00
state . setTime ( state . mLoopStartTime ) ;
2015-04-23 18:41:31 +00:00
state . mPlaying = true ;
2015-05-14 14:33:41 +00:00
if ( state . getTime ( ) > = state . mLoopStopTime )
2015-04-23 18:41:31 +00:00
break ;
2015-05-14 14:33:41 +00:00
NifOsg : : TextKeyMap : : const_iterator textkey ( textkeys . lower_bound ( state . getTime ( ) ) ) ;
while ( textkey ! = textkeys . end ( ) & & textkey - > first < = state . getTime ( ) )
2015-04-23 18:41:31 +00:00
{
handleTextKey ( state , groupname , textkey , textkeys ) ;
+ + textkey ;
}
}
break ;
}
}
if ( iter = = mAnimSources . rend ( ) )
std : : cerr < < " Failed to find animation " < < groupname < < " for " < < mPtr . getCellRef ( ) . getRefId ( ) < < std : : endl ;
resetActiveGroups ( ) ;
}
bool Animation : : reset ( AnimState & state , const NifOsg : : TextKeyMap & keys , const std : : string & groupname , const std : : string & start , const std : : string & stop , float startpoint , bool loopfallback )
{
// Look for text keys in reverse. This normally wouldn't matter, but for some reason undeadwolf_2.nif has two
// separate walkforward keys, and the last one is supposed to be used.
NifOsg : : TextKeyMap : : const_reverse_iterator groupend ( keys . rbegin ( ) ) ;
for ( ; groupend ! = keys . rend ( ) ; + + groupend )
{
if ( groupend - > second . compare ( 0 , groupname . size ( ) , groupname ) = = 0 & &
groupend - > second . compare ( groupname . size ( ) , 2 , " : " ) = = 0 )
break ;
}
std : : string starttag = groupname + " : " + start ;
NifOsg : : TextKeyMap : : const_reverse_iterator startkey ( groupend ) ;
while ( startkey ! = keys . rend ( ) & & startkey - > second ! = starttag )
+ + startkey ;
if ( startkey = = keys . rend ( ) & & start = = " loop start " )
{
starttag = groupname + " : start " ;
startkey = groupend ;
while ( startkey ! = keys . rend ( ) & & startkey - > second ! = starttag )
+ + startkey ;
}
if ( startkey = = keys . rend ( ) )
return false ;
const std : : string stoptag = groupname + " : " + stop ;
NifOsg : : TextKeyMap : : const_reverse_iterator stopkey ( groupend ) ;
while ( stopkey ! = keys . rend ( )
// We have to ignore extra garbage at the end.
// The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop".
// Why, just why? :(
& & ( stopkey - > second . size ( ) < stoptag . size ( ) | | stopkey - > second . substr ( 0 , stoptag . size ( ) ) ! = stoptag ) )
+ + stopkey ;
if ( stopkey = = keys . rend ( ) )
return false ;
if ( startkey - > first > stopkey - > first )
return false ;
state . mStartTime = startkey - > first ;
if ( loopfallback )
{
state . mLoopStartTime = startkey - > first ;
state . mLoopStopTime = stopkey - > first ;
}
else
{
state . mLoopStartTime = startkey - > first ;
state . mLoopStopTime = std : : numeric_limits < float > : : max ( ) ;
}
state . mStopTime = stopkey - > first ;
2015-05-14 14:33:41 +00:00
state . setTime ( state . mStartTime + ( ( state . mStopTime - state . mStartTime ) * startpoint ) ) ;
2015-04-23 18:41:31 +00:00
// mLoopStartTime and mLoopStopTime normally get assigned when encountering these keys while playing the animation
2015-07-16 17:55:05 +00:00
// (see handleTextKey). But if startpoint is already past these keys, or start time is == stop time, we need to assign them now.
const std : : string loopstarttag = groupname + " : loop start " ;
const std : : string loopstoptag = groupname + " : loop stop " ;
2015-04-23 18:41:31 +00:00
2015-07-16 17:55:05 +00:00
NifOsg : : TextKeyMap : : const_reverse_iterator key ( groupend ) ;
for ( ; key ! = startkey & & key ! = keys . rend ( ) ; + + key )
{
if ( key - > first > state . getTime ( ) )
continue ;
2015-04-23 18:41:31 +00:00
2015-07-16 17:55:05 +00:00
if ( key - > second = = loopstarttag )
state . mLoopStartTime = key - > first ;
else if ( key - > second = = loopstoptag )
state . mLoopStopTime = key - > first ;
2015-04-23 18:41:31 +00:00
}
return true ;
}
2015-05-21 22:55:43 +00:00
void Animation : : setTextKeyListener ( Animation : : TextKeyListener * listener )
{
mTextKeyListener = listener ;
}
2016-02-09 15:18:19 +00:00
const Animation : : NodeMap & Animation : : getNodeMap ( ) const
{
if ( ! mNodeMapCreated & & mObjectRoot )
{
NodeMapVisitor visitor ( mNodeMap ) ;
mObjectRoot - > accept ( visitor ) ;
mNodeMapCreated = true ;
}
return mNodeMap ;
}
2015-04-23 18:41:31 +00:00
void Animation : : resetActiveGroups ( )
{
// remove all previous external controllers from the scene graph
2015-05-31 00:26:31 +00:00
for ( ControllerMap : : iterator it = mActiveControllers . begin ( ) ; it ! = mActiveControllers . end ( ) ; + + it )
2015-04-23 18:41:31 +00:00
{
osg : : Node * node = it - > first ;
node - > removeUpdateCallback ( it - > second ) ;
2015-04-25 13:10:37 +00:00
// Should be no longer needed with OSG 3.4
it - > second - > setNestedCallback ( NULL ) ;
2015-04-23 18:41:31 +00:00
}
2015-05-31 00:26:31 +00:00
mActiveControllers . clear ( ) ;
2015-04-23 18:41:31 +00:00
mAccumCtrl = NULL ;
2015-07-09 16:47:11 +00:00
for ( size_t blendMask = 0 ; blendMask < sNumBlendMasks ; blendMask + + )
2015-04-23 18:41:31 +00:00
{
AnimStateMap : : const_iterator active = mStates . end ( ) ;
AnimStateMap : : const_iterator state = mStates . begin ( ) ;
for ( ; state ! = mStates . end ( ) ; + + state )
{
2015-07-09 16:47:11 +00:00
if ( ! ( state - > second . mBlendMask & ( 1 < < blendMask ) ) )
2015-04-23 18:41:31 +00:00
continue ;
2015-09-16 14:14:17 +00:00
if ( active = = mStates . end ( ) | | active - > second . mPriority [ ( BoneGroup ) blendMask ] < state - > second . mPriority [ ( BoneGroup ) blendMask ] )
2015-04-23 18:41:31 +00:00
active = state ;
}
2015-07-09 16:47:11 +00:00
mAnimationTimePtr [ blendMask ] - > setTimePtr ( active = = mStates . end ( ) ? boost : : shared_ptr < float > ( ) : active - > second . mTime ) ;
2015-04-23 18:41:31 +00:00
2015-07-09 16:47:11 +00:00
// add external controllers for the AnimSource active in this blend mask
2015-04-23 18:41:31 +00:00
if ( active ! = mStates . end ( ) )
{
boost : : shared_ptr < AnimSource > animsrc = active - > second . mSource ;
2015-07-09 16:47:11 +00:00
for ( AnimSource : : ControllerMap : : iterator it = animsrc - > mControllerMap [ blendMask ] . begin ( ) ; it ! = animsrc - > mControllerMap [ blendMask ] . end ( ) ; + + it )
2015-04-23 18:41:31 +00:00
{
2016-02-09 15:18:19 +00:00
osg : : ref_ptr < osg : : Node > node = getNodeMap ( ) . at ( it - > first ) ; // this should not throw, we already checked for the node existing in addAnimSource
2015-04-23 18:41:31 +00:00
node - > addUpdateCallback ( it - > second ) ;
2015-05-31 00:26:31 +00:00
mActiveControllers . insert ( std : : make_pair ( node , it - > second ) ) ;
2015-04-23 18:41:31 +00:00
2015-07-09 16:47:11 +00:00
if ( blendMask = = 0 & & node = = mAccumRoot )
2015-04-23 20:46:07 +00:00
{
2015-04-23 18:41:31 +00:00
mAccumCtrl = it - > second ;
2015-04-23 20:46:07 +00:00
2015-04-25 13:10:37 +00:00
// make sure reset is last in the chain of callbacks
2015-04-23 20:46:07 +00:00
if ( ! mResetAccumRootCallback )
{
mResetAccumRootCallback = new ResetAccumRootCallback ;
mResetAccumRootCallback - > setAccumulate ( mAccumulate ) ;
}
mAccumRoot - > addUpdateCallback ( mResetAccumRootCallback ) ;
2015-05-31 00:26:31 +00:00
mActiveControllers . insert ( std : : make_pair ( mAccumRoot , mResetAccumRootCallback ) ) ;
2015-04-23 20:46:07 +00:00
}
2015-04-23 18:41:31 +00:00
}
}
}
2015-05-31 00:26:31 +00:00
addControllers ( ) ;
2015-04-23 18:41:31 +00:00
}
void Animation : : stopLooping ( const std : : string & groupname )
{
AnimStateMap : : iterator stateiter = mStates . find ( groupname ) ;
if ( stateiter ! = mStates . end ( ) )
{
stateiter - > second . mLoopCount = 0 ;
return ;
}
}
void Animation : : adjustSpeedMult ( const std : : string & groupname , float speedmult )
{
AnimStateMap : : iterator state ( mStates . find ( groupname ) ) ;
if ( state ! = mStates . end ( ) )
state - > second . mSpeedMult = speedmult ;
}
bool Animation : : isPlaying ( const std : : string & groupname ) const
{
AnimStateMap : : const_iterator state ( mStates . find ( groupname ) ) ;
if ( state ! = mStates . end ( ) )
return state - > second . mPlaying ;
return false ;
}
bool Animation : : getInfo ( const std : : string & groupname , float * complete , float * speedmult ) const
{
AnimStateMap : : const_iterator iter = mStates . find ( groupname ) ;
if ( iter = = mStates . end ( ) )
{
if ( complete ) * complete = 0.0f ;
if ( speedmult ) * speedmult = 0.0f ;
return false ;
}
if ( complete )
{
if ( iter - > second . mStopTime > iter - > second . mStartTime )
2015-05-14 14:33:41 +00:00
* complete = ( iter - > second . getTime ( ) - iter - > second . mStartTime ) /
2015-04-23 18:41:31 +00:00
( iter - > second . mStopTime - iter - > second . mStartTime ) ;
else
* complete = ( iter - > second . mPlaying ? 0.0f : 1.0f ) ;
}
if ( speedmult ) * speedmult = iter - > second . mSpeedMult ;
return true ;
}
float Animation : : getCurrentTime ( const std : : string & groupname ) const
{
AnimStateMap : : const_iterator iter = mStates . find ( groupname ) ;
if ( iter = = mStates . end ( ) )
return - 1.f ;
2015-05-14 14:33:41 +00:00
return iter - > second . getTime ( ) ;
2015-04-23 18:41:31 +00:00
}
2016-07-30 17:24:03 +00:00
size_t Animation : : getCurrentLoopCount ( const std : : string & groupname ) const
{
AnimStateMap : : const_iterator iter = mStates . find ( groupname ) ;
if ( iter = = mStates . end ( ) )
return 0 ;
return iter - > second . mLoopCount ;
}
2015-04-23 18:41:31 +00:00
void Animation : : disable ( const std : : string & groupname )
{
AnimStateMap : : iterator iter = mStates . find ( groupname ) ;
if ( iter ! = mStates . end ( ) )
mStates . erase ( iter ) ;
resetActiveGroups ( ) ;
}
float Animation : : getVelocity ( const std : : string & groupname ) const
{
2015-04-23 21:30:06 +00:00
if ( ! mAccumRoot )
return 0.0f ;
2015-04-23 18:41:31 +00:00
// Look in reverse; last-inserted source has priority.
AnimSourceList : : const_reverse_iterator animsrc ( mAnimSources . rbegin ( ) ) ;
for ( ; animsrc ! = mAnimSources . rend ( ) ; + + animsrc )
{
2015-04-23 21:30:06 +00:00
const NifOsg : : TextKeyMap & keys = ( * animsrc ) - > getTextKeys ( ) ;
2015-04-23 18:41:31 +00:00
if ( findGroupStart ( keys , groupname ) ! = keys . end ( ) )
break ;
}
if ( animsrc = = mAnimSources . rend ( ) )
return 0.0f ;
float velocity = 0.0f ;
2015-04-23 21:30:06 +00:00
const NifOsg : : TextKeyMap & keys = ( * animsrc ) - > getTextKeys ( ) ;
const AnimSource : : ControllerMap & ctrls = ( * animsrc ) - > mControllerMap [ 0 ] ;
for ( AnimSource : : ControllerMap : : const_iterator it = ctrls . begin ( ) ; it ! = ctrls . end ( ) ; + + it )
2015-04-23 18:41:31 +00:00
{
2015-04-23 21:30:06 +00:00
if ( Misc : : StringUtils : : ciEqual ( it - > first , mAccumRoot - > getName ( ) ) )
2015-04-23 18:41:31 +00:00
{
2015-04-23 21:30:06 +00:00
velocity = calcAnimVelocity ( keys , it - > second , mAccumulate , groupname ) ;
2015-04-23 18:41:31 +00:00
break ;
}
}
// If there's no velocity, keep looking
if ( ! ( velocity > 1.0f ) )
{
AnimSourceList : : const_reverse_iterator animiter = mAnimSources . rbegin ( ) ;
while ( * animiter ! = * animsrc )
+ + animiter ;
while ( ! ( velocity > 1.0f ) & & + + animiter ! = mAnimSources . rend ( ) )
{
2015-04-23 21:30:06 +00:00
const NifOsg : : TextKeyMap & keys = ( * animiter ) - > getTextKeys ( ) ;
const AnimSource : : ControllerMap & ctrls = ( * animiter ) - > mControllerMap [ 0 ] ;
for ( AnimSource : : ControllerMap : : const_iterator it = ctrls . begin ( ) ; it ! = ctrls . end ( ) ; + + it )
2015-04-23 18:41:31 +00:00
{
2015-04-23 21:30:06 +00:00
if ( Misc : : StringUtils : : ciEqual ( it - > first , mAccumRoot - > getName ( ) ) )
2015-04-23 18:41:31 +00:00
{
2015-04-23 21:30:06 +00:00
velocity = calcAnimVelocity ( keys , it - > second , mAccumulate , groupname ) ;
2015-04-23 18:41:31 +00:00
break ;
}
}
}
}
return velocity ;
2015-04-22 17:08:56 +00:00
}
2015-04-23 20:46:07 +00:00
void Animation : : updatePosition ( float oldtime , float newtime , osg : : Vec3f & position )
{
// Get the difference from the last update, and move the position
osg : : Vec3f off = osg : : componentMultiply ( mAccumCtrl - > getTranslation ( newtime ) , mAccumulate ) ;
position + = off - osg : : componentMultiply ( mAccumCtrl - > getTranslation ( oldtime ) , mAccumulate ) ;
}
2015-04-12 13:34:50 +00:00
osg : : Vec3f Animation : : runAnimation ( float duration )
2015-01-02 01:27:05 +00:00
{
2015-04-23 18:41:31 +00:00
osg : : Vec3f movement ( 0.f , 0.f , 0.f ) ;
AnimStateMap : : iterator stateiter = mStates . begin ( ) ;
while ( stateiter ! = mStates . end ( ) )
{
AnimState & state = stateiter - > second ;
const NifOsg : : TextKeyMap & textkeys = state . mSource - > getTextKeys ( ) ;
2015-05-14 14:33:41 +00:00
NifOsg : : TextKeyMap : : const_iterator textkey ( textkeys . upper_bound ( state . getTime ( ) ) ) ;
2015-04-23 18:41:31 +00:00
float timepassed = duration * state . mSpeedMult ;
while ( state . mPlaying )
{
float targetTime ;
2015-05-14 14:33:41 +00:00
if ( state . getTime ( ) > = state . mLoopStopTime & & state . mLoopCount > 0 )
2015-04-23 18:41:31 +00:00
goto handle_loop ;
2015-05-14 14:33:41 +00:00
targetTime = state . getTime ( ) + timepassed ;
2015-04-23 18:41:31 +00:00
if ( textkey = = textkeys . end ( ) | | textkey - > first > targetTime )
{
2015-05-14 14:33:41 +00:00
if ( mAccumCtrl & & state . mTime = = mAnimationTimePtr [ 0 ] - > getTimePtr ( ) )
updatePosition ( state . getTime ( ) , targetTime , movement ) ;
state . setTime ( std : : min ( targetTime , state . mStopTime ) ) ;
2015-04-23 18:41:31 +00:00
}
else
{
2015-05-14 14:33:41 +00:00
if ( mAccumCtrl & & state . mTime = = mAnimationTimePtr [ 0 ] - > getTimePtr ( ) )
updatePosition ( state . getTime ( ) , textkey - > first , movement ) ;
state . setTime ( textkey - > first ) ;
2015-04-23 18:41:31 +00:00
}
2015-05-14 14:33:41 +00:00
state . mPlaying = ( state . getTime ( ) < state . mStopTime ) ;
timepassed = targetTime - state . getTime ( ) ;
2015-04-23 18:41:31 +00:00
2015-05-14 14:33:41 +00:00
while ( textkey ! = textkeys . end ( ) & & textkey - > first < = state . getTime ( ) )
2015-04-23 18:41:31 +00:00
{
handleTextKey ( state , stateiter - > first , textkey , textkeys ) ;
+ + textkey ;
}
2015-05-14 14:33:41 +00:00
if ( state . getTime ( ) > = state . mLoopStopTime & & state . mLoopCount > 0 )
2015-04-23 18:41:31 +00:00
{
handle_loop :
state . mLoopCount - - ;
2015-05-14 14:33:41 +00:00
state . setTime ( state . mLoopStartTime ) ;
2015-04-23 18:41:31 +00:00
state . mPlaying = true ;
2015-05-14 14:33:41 +00:00
textkey = textkeys . lower_bound ( state . getTime ( ) ) ;
while ( textkey ! = textkeys . end ( ) & & textkey - > first < = state . getTime ( ) )
2015-04-23 18:41:31 +00:00
{
handleTextKey ( state , stateiter - > first , textkey , textkeys ) ;
+ + textkey ;
}
2015-05-14 14:33:41 +00:00
if ( state . getTime ( ) > = state . mLoopStopTime )
2015-04-23 18:41:31 +00:00
break ;
}
if ( timepassed < = 0.0f )
break ;
}
if ( ! state . mPlaying & & state . mAutoDisable )
{
mStates . erase ( stateiter + + ) ;
resetActiveGroups ( ) ;
}
else
+ + stateiter ;
}
2015-04-18 23:57:52 +00:00
updateEffects ( duration ) ;
2015-06-22 19:06:27 +00:00
if ( mHeadController )
{
const float epsilon = 0.001f ;
bool enable = ( std : : abs ( mHeadPitchRadians ) > epsilon | | std : : abs ( mHeadYawRadians ) > epsilon ) ;
mHeadController - > setEnabled ( enable ) ;
if ( enable )
mHeadController - > setRotate ( osg : : Quat ( mHeadPitchRadians , osg : : Vec3f ( 1 , 0 , 0 ) ) * osg : : Quat ( mHeadYawRadians , osg : : Vec3f ( 0 , 0 , 1 ) ) ) ;
}
2015-04-23 18:41:31 +00:00
return movement ;
2015-01-02 01:27:05 +00:00
}
2013-05-11 01:37:44 +00:00
2015-08-15 17:01:21 +00:00
void Animation : : setObjectRoot ( const std : : string & model , bool forceskeleton , bool baseonly , bool isCreature )
2013-05-11 01:37:44 +00:00
{
2015-09-18 19:29:32 +00:00
osg : : ref_ptr < osg : : StateSet > previousStateset ;
2015-04-12 13:34:50 +00:00
if ( mObjectRoot )
2013-04-25 05:45:43 +00:00
{
2015-09-18 19:29:32 +00:00
previousStateset = mObjectRoot - > getStateSet ( ) ;
2015-04-12 13:34:50 +00:00
mObjectRoot - > getParent ( 0 ) - > removeChild ( mObjectRoot ) ;
2013-04-25 05:45:43 +00:00
}
2015-04-23 20:46:07 +00:00
mObjectRoot = NULL ;
2015-12-03 19:06:00 +00:00
mSkeleton = NULL ;
2013-04-25 07:03:27 +00:00
2015-04-23 18:41:31 +00:00
mNodeMap . clear ( ) ;
2016-02-09 15:18:19 +00:00
mNodeMapCreated = false ;
2015-05-31 00:26:31 +00:00
mActiveControllers . clear ( ) ;
2015-04-23 20:46:07 +00:00
mAccumRoot = NULL ;
mAccumCtrl = NULL ;
2015-04-23 18:41:31 +00:00
2015-04-23 21:30:06 +00:00
if ( ! forceskeleton )
2015-12-17 02:48:11 +00:00
{
2016-02-09 17:33:02 +00:00
osg : : ref_ptr < osg : : Node > created = mResourceSystem - > getSceneManager ( ) - > getInstance ( model , mInsert ) ;
2015-12-17 02:48:11 +00:00
mObjectRoot = created - > asGroup ( ) ;
if ( ! mObjectRoot )
{
2015-12-17 02:49:18 +00:00
mInsert - > removeChild ( created ) ;
2015-12-17 02:48:11 +00:00
mObjectRoot = new osg : : Group ;
mObjectRoot - > addChild ( created ) ;
2015-12-17 02:49:18 +00:00
mInsert - > addChild ( mObjectRoot ) ;
2015-12-17 02:48:11 +00:00
}
}
2015-04-23 21:30:06 +00:00
else
{
2016-02-09 17:33:02 +00:00
osg : : ref_ptr < osg : : Node > created = mResourceSystem - > getSceneManager ( ) - > getInstance ( model ) ;
2015-12-17 02:48:11 +00:00
osg : : ref_ptr < SceneUtil : : Skeleton > skel = dynamic_cast < SceneUtil : : Skeleton * > ( created . get ( ) ) ;
if ( ! skel )
2015-04-23 21:30:06 +00:00
{
2015-12-17 02:48:11 +00:00
skel = new SceneUtil : : Skeleton ;
skel - > addChild ( created ) ;
2015-04-23 21:30:06 +00:00
}
2015-12-17 02:48:11 +00:00
mSkeleton = skel . get ( ) ;
mObjectRoot = skel ;
mInsert - > addChild ( mObjectRoot ) ;
2015-04-23 21:30:06 +00:00
}
2015-04-23 18:41:31 +00:00
2015-09-18 19:29:32 +00:00
if ( previousStateset )
mObjectRoot - > setStateSet ( previousStateset ) ;
2015-05-21 21:24:22 +00:00
if ( baseonly )
{
RemoveDrawableVisitor removeDrawableVisitor ;
mObjectRoot - > accept ( removeDrawableVisitor ) ;
removeDrawableVisitor . remove ( ) ;
}
2015-08-15 17:01:21 +00:00
if ( isCreature )
{
RemoveTriBipVisitor removeTriBipVisitor ;
mObjectRoot - > accept ( removeTriBipVisitor ) ;
removeTriBipVisitor . remove ( ) ;
}
2015-05-01 16:21:50 +00:00
mObjectRoot - > addCullCallback ( new SceneUtil : : LightListCallback ) ;
2013-07-23 12:30:37 +00:00
}
2013-04-25 07:03:27 +00:00
2015-04-12 13:34:50 +00:00
osg : : Group * Animation : : getObjectRoot ( )
2014-02-04 02:46:15 +00:00
{
2015-12-17 02:48:11 +00:00
return mObjectRoot . get ( ) ;
2014-06-10 20:20:46 +00:00
}
2015-04-12 13:34:50 +00:00
osg : : Group * Animation : : getOrCreateObjectRoot ( )
2014-06-10 20:20:46 +00:00
{
2015-04-12 13:34:50 +00:00
if ( mObjectRoot )
2015-12-17 02:48:11 +00:00
return mObjectRoot . get ( ) ;
2014-06-10 20:20:46 +00:00
2015-04-12 13:34:50 +00:00
mObjectRoot = new osg : : Group ;
mInsert - > addChild ( mObjectRoot ) ;
2015-12-17 02:48:11 +00:00
return mObjectRoot . get ( ) ;
2014-02-04 02:46:15 +00:00
}
2014-06-10 20:20:46 +00:00
2016-02-18 22:10:58 +00:00
class FindLowestUnusedTexUnitVisitor : public osg : : NodeVisitor
{
public :
FindLowestUnusedTexUnitVisitor ( )
: osg : : NodeVisitor ( TRAVERSE_ALL_CHILDREN )
, mLowestUnusedTexUnit ( 0 )
{
}
virtual void apply ( osg : : Node & node )
{
if ( osg : : StateSet * stateset = node . getStateSet ( ) )
mLowestUnusedTexUnit = std : : max ( mLowestUnusedTexUnit , int ( stateset - > getTextureAttributeList ( ) . size ( ) ) ) ;
traverse ( node ) ;
}
int mLowestUnusedTexUnit ;
} ;
2016-08-02 12:37:39 +00:00
void Animation : : addSpellCastGlow ( osg : : Vec4f glowColor ) {
2016-08-06 18:08:46 +00:00
if ( ! mObjectRoot - > getUpdateCallback ( ) ) // If there is no glow on object
addGlow ( mObjectRoot , glowColor , 1.5 ) ; // Glow length measured from in-game as about 1.5 seconds
2016-08-02 12:37:39 +00:00
2016-08-06 18:08:46 +00:00
else if ( dynamic_cast < GlowUpdater * > ( mObjectRoot - > getUpdateCallback ( ) ) - > isDone ( ) = = true ) // If there was a temporary glow on object and it finished
addGlow ( mObjectRoot , glowColor , 1.5 ) ;
2016-08-02 12:37:39 +00:00
2016-08-06 18:08:46 +00:00
else if ( dynamic_cast < GlowUpdater * > ( mObjectRoot - > getUpdateCallback ( ) ) - > isEnchantmentGlowUpdater ( ) = = true
& & dynamic_cast < GlowUpdater * > ( mObjectRoot - > getUpdateCallback ( ) ) - > getWatchedSpellGlow ( ) = = NULL ) // If there is a permanent enchantment glow on object and no temporary glow is running
addGlow ( mObjectRoot , glowColor , 1.5 ) ;
2016-08-02 12:37:39 +00:00
}
2016-08-06 18:08:46 +00:00
void Animation : : addGlow ( osg : : ref_ptr < osg : : Node > node , osg : : Vec4f glowColor , float glowDuration )
2015-04-16 23:23:37 +00:00
{
std : : vector < osg : : ref_ptr < osg : : Texture2D > > textures ;
for ( int i = 0 ; i < 32 ; + + i )
{
std : : stringstream stream ;
stream < < " textures/magicitem/caust " ;
stream < < std : : setw ( 2 ) ;
stream < < std : : setfill ( ' 0 ' ) ;
stream < < i ;
stream < < " .dds " ;
2016-02-05 22:10:27 +00:00
osg : : ref_ptr < osg : : Image > image = mResourceSystem - > getImageManager ( ) - > getImage ( stream . str ( ) ) ;
2016-02-05 21:58:02 +00:00
osg : : ref_ptr < osg : : Texture2D > tex ( new osg : : Texture2D ( image ) ) ;
2016-02-18 22:05:44 +00:00
tex - > setName ( " envMap " ) ;
2016-02-05 21:58:02 +00:00
tex - > setWrap ( osg : : Texture : : WRAP_S , osg : : Texture2D : : REPEAT ) ;
tex - > setWrap ( osg : : Texture : : WRAP_T , osg : : Texture2D : : REPEAT ) ;
2016-02-05 17:51:51 +00:00
mResourceSystem - > getSceneManager ( ) - > applyFilterSettings ( tex ) ;
textures . push_back ( tex ) ;
2015-04-16 23:23:37 +00:00
}
2016-08-06 18:08:46 +00:00
int texUnit ;
// If we have a spell glow updater left over from this object prevously casting a spell,
// and there was no permanent glow updater on the object to watch it and remove it, we
// remove it here.
if ( node - > getUpdateCallback ( ) & &
dynamic_cast < GlowUpdater * > ( node - > getUpdateCallback ( ) ) - > isSpellGlowUpdater ( ) = = true )
node - > removeUpdateCallback ( node - > getUpdateCallback ( ) ) ;
2016-02-18 22:10:58 +00:00
FindLowestUnusedTexUnitVisitor findLowestUnusedTexUnitVisitor ;
node - > accept ( findLowestUnusedTexUnitVisitor ) ;
2016-08-06 18:08:46 +00:00
texUnit = findLowestUnusedTexUnitVisitor . mLowestUnusedTexUnit ;
osg : : Uniform * uniform = new osg : : Uniform ( " envMapColor " , glowColor ) ;
2016-08-02 13:15:48 +00:00
2016-08-06 18:08:46 +00:00
osg : : ref_ptr < GlowUpdater > glowUpdater = new GlowUpdater ( texUnit , glowColor , textures , node , glowDuration , mResourceSystem , uniform ) ;
2016-08-02 13:15:48 +00:00
node - > addUpdateCallback ( glowUpdater ) ;
2016-08-06 18:08:46 +00:00
if ( node - > getUpdateCallback ( ) & &
dynamic_cast < GlowUpdater * > ( node - > getUpdateCallback ( ) ) - > isEnchantmentGlowUpdater ( ) = = true )
if ( glowDuration > = 0 )
dynamic_cast < GlowUpdater * > ( node - > getUpdateCallback ( ) ) - > setWatchedSpellGlow ( glowUpdater ) ;
2016-02-16 22:30:23 +00:00
2016-02-18 22:05:44 +00:00
// set a texture now so that the ShaderVisitor can find it
osg : : ref_ptr < osg : : StateSet > writableStateSet = NULL ;
if ( ! node - > getStateSet ( ) )
writableStateSet = node - > getOrCreateStateSet ( ) ;
else
2016-08-08 23:12:54 +00:00
{
2016-02-18 22:05:44 +00:00
writableStateSet = osg : : clone ( node - > getStateSet ( ) , osg : : CopyOp : : SHALLOW_COPY ) ;
2016-08-08 23:12:54 +00:00
node - > setStateSet ( writableStateSet ) ;
}
2016-02-18 22:05:44 +00:00
writableStateSet - > setTextureAttributeAndModes ( texUnit , textures . front ( ) , osg : : StateAttribute : : ON ) ;
2016-08-06 18:08:46 +00:00
writableStateSet - > addUniform ( uniform ) ;
2016-02-18 22:05:44 +00:00
mResourceSystem - > getSceneManager ( ) - > recreateShaders ( node ) ;
2015-04-16 23:23:37 +00:00
}
// TODO: Should not be here
2016-05-19 20:30:14 +00:00
osg : : Vec4f Animation : : getEnchantmentColor ( const MWWorld : : ConstPtr & item ) const
2015-04-16 23:23:37 +00:00
{
osg : : Vec4f result ( 1 , 1 , 1 , 1 ) ;
std : : string enchantmentName = item . getClass ( ) . getEnchantment ( item ) ;
if ( enchantmentName . empty ( ) )
return result ;
2016-08-01 01:16:42 +00:00
2016-08-01 23:43:41 +00:00
const ESM : : Enchantment * enchantment = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : Enchantment > ( ) . search ( enchantmentName ) ;
if ( ! enchantment )
2016-08-01 01:16:42 +00:00
return result ;
2016-08-01 23:43:41 +00:00
2015-04-16 23:23:37 +00:00
assert ( enchantment - > mEffects . mList . size ( ) ) ;
2016-08-01 01:16:42 +00:00
2016-08-01 23:43:41 +00:00
const ESM : : MagicEffect * magicEffect = MWBase : : Environment : : get ( ) . getWorld ( ) - > getStore ( ) . get < ESM : : MagicEffect > ( ) . search (
2015-04-16 23:23:37 +00:00
enchantment - > mEffects . mList . front ( ) . mEffectID ) ;
2016-08-01 23:43:41 +00:00
if ( ! magicEffect )
2016-08-01 01:16:42 +00:00
return result ;
2016-08-01 23:43:41 +00:00
2015-04-16 23:23:37 +00:00
result . x ( ) = magicEffect - > mData . mRed / 255.f ;
result . y ( ) = magicEffect - > mData . mGreen / 255.f ;
result . z ( ) = magicEffect - > mData . mBlue / 255.f ;
return result ;
}
2015-04-19 13:03:08 +00:00
void Animation : : addExtraLight ( osg : : ref_ptr < osg : : Group > parent , const ESM : : Light * esmLight )
{
2016-01-06 11:46:06 +00:00
const Fallback : : Map * fallback = MWBase : : Environment : : get ( ) . getWorld ( ) - > getFallback ( ) ;
2015-06-02 15:02:56 +00:00
static bool outQuadInLin = fallback - > getFallbackBool ( " LightAttenuation_OutQuadInLin " ) ;
static bool useQuadratic = fallback - > getFallbackBool ( " LightAttenuation_UseQuadratic " ) ;
static float quadraticValue = fallback - > getFallbackFloat ( " LightAttenuation_QuadraticValue " ) ;
static float quadraticRadiusMult = fallback - > getFallbackFloat ( " LightAttenuation_QuadraticRadiusMult " ) ;
static bool useLinear = fallback - > getFallbackBool ( " LightAttenuation_UseLinear " ) ;
static float linearRadiusMult = fallback - > getFallbackFloat ( " LightAttenuation_LinearRadiusMult " ) ;
static float linearValue = fallback - > getFallbackFloat ( " LightAttenuation_LinearValue " ) ;
bool exterior = mPtr . isInCell ( ) & & mPtr . getCell ( ) - > getCell ( ) - > isExterior ( ) ;
2016-01-06 12:37:55 +00:00
SceneUtil : : addLight ( parent , esmLight , Mask_ParticleSystem , Mask_Lighting , exterior , outQuadInLin ,
useQuadratic , quadraticValue , quadraticRadiusMult , useLinear , linearRadiusMult , linearValue ) ;
2015-04-19 13:03:08 +00:00
}
2015-04-18 23:57:52 +00:00
void Animation : : addEffect ( const std : : string & model , int effectId , bool loop , const std : : string & bonename , std : : string texture )
{
2015-06-07 15:09:37 +00:00
if ( ! mObjectRoot . get ( ) )
return ;
2015-04-18 23:57:52 +00:00
// Early out if we already have this effect
for ( std : : vector < EffectParams > : : iterator it = mEffects . begin ( ) ; it ! = mEffects . end ( ) ; + + it )
if ( it - > mLoop & & loop & & it - > mEffectId = = effectId & & it - > mBoneName = = bonename )
return ;
EffectParams params ;
params . mModelName = model ;
osg : : ref_ptr < osg : : Group > parentNode ;
if ( bonename . empty ( ) )
2015-06-11 14:22:09 +00:00
parentNode = mInsert ;
2015-04-18 23:57:52 +00:00
else
{
2016-02-09 15:18:19 +00:00
NodeMap : : const_iterator found = getNodeMap ( ) . find ( Misc : : StringUtils : : lowerCase ( bonename ) ) ;
if ( found = = getNodeMap ( ) . end ( ) )
2015-04-18 23:57:52 +00:00
throw std : : runtime_error ( " Can't find bone " + bonename ) ;
2015-05-29 18:45:27 +00:00
parentNode = found - > second ;
2015-04-18 23:57:52 +00:00
}
2016-02-09 17:33:02 +00:00
osg : : ref_ptr < osg : : Node > node = mResourceSystem - > getSceneManager ( ) - > getInstance ( model , parentNode ) ;
2015-06-07 02:41:55 +00:00
node - > getOrCreateStateSet ( ) - > setMode ( GL_LIGHTING , osg : : StateAttribute : : OFF ) ;
2015-04-18 23:57:52 +00:00
params . mObjects = PartHolderPtr ( new PartHolder ( node ) ) ;
2015-04-19 15:55:56 +00:00
SceneUtil : : FindMaxControllerLengthVisitor findMaxLengthVisitor ;
2015-04-18 23:57:52 +00:00
node - > accept ( findMaxLengthVisitor ) ;
2015-06-19 16:38:44 +00:00
// FreezeOnCull doesn't work so well with effect particles, that tend to have moving emitters
2015-11-02 22:49:22 +00:00
SceneUtil : : DisableFreezeOnCullVisitor disableFreezeOnCullVisitor ;
2015-06-19 16:38:44 +00:00
node - > accept ( disableFreezeOnCullVisitor ) ;
2015-04-18 23:57:52 +00:00
params . mMaxControllerLength = findMaxLengthVisitor . getMaxLength ( ) ;
node - > setNodeMask ( Mask_Effect ) ;
params . mLoop = loop ;
params . mEffectId = effectId ;
params . mBoneName = bonename ;
params . mAnimTime = boost : : shared_ptr < EffectAnimationTime > ( new EffectAnimationTime ) ;
SceneUtil : : AssignControllerSourcesVisitor assignVisitor ( boost : : shared_ptr < SceneUtil : : ControllerSource > ( params . mAnimTime ) ) ;
node - > accept ( assignVisitor ) ;
2015-04-19 15:55:56 +00:00
overrideTexture ( texture , mResourceSystem , node ) ;
2015-04-18 23:57:52 +00:00
// TODO: in vanilla morrowind the effect is scaled based on the host object's bounding box.
mEffects . push_back ( params ) ;
}
void Animation : : removeEffect ( int effectId )
{
for ( std : : vector < EffectParams > : : iterator it = mEffects . begin ( ) ; it ! = mEffects . end ( ) ; + + it )
{
if ( it - > mEffectId = = effectId )
{
mEffects . erase ( it ) ;
return ;
}
}
}
2016-05-19 20:30:14 +00:00
void Animation : : getLoopingEffects ( std : : vector < int > & out ) const
2015-04-18 23:57:52 +00:00
{
2016-05-19 20:30:14 +00:00
for ( std : : vector < EffectParams > : : const_iterator it = mEffects . begin ( ) ; it ! = mEffects . end ( ) ; + + it )
2015-04-18 23:57:52 +00:00
{
if ( it - > mLoop )
out . push_back ( it - > mEffectId ) ;
}
}
void Animation : : updateEffects ( float duration )
{
for ( std : : vector < EffectParams > : : iterator it = mEffects . begin ( ) ; it ! = mEffects . end ( ) ; )
{
it - > mAnimTime - > addTime ( duration ) ;
if ( it - > mAnimTime - > getTime ( ) > = it - > mMaxControllerLength )
{
if ( it - > mLoop )
{
// Start from the beginning again; carry over the remainder
// Not sure if this is actually needed, the controller function might already handle loops
float remainder = it - > mAnimTime - > getTime ( ) - it - > mMaxControllerLength ;
it - > mAnimTime - > resetTime ( remainder ) ;
}
else
{
it = mEffects . erase ( it ) ;
continue ;
}
}
+ + it ;
}
}
2015-05-12 15:40:42 +00:00
bool Animation : : upperBodyReady ( ) const
{
for ( AnimStateMap : : const_iterator stateiter = mStates . begin ( ) ; stateiter ! = mStates . end ( ) ; + + stateiter )
{
2015-07-15 12:18:31 +00:00
if ( stateiter - > second . mPriority . contains ( int ( MWMechanics : : Priority_Hit ) )
| | stateiter - > second . mPriority . contains ( int ( MWMechanics : : Priority_Weapon ) )
| | stateiter - > second . mPriority . contains ( int ( MWMechanics : : Priority_Knockdown ) )
| | stateiter - > second . mPriority . contains ( int ( MWMechanics : : Priority_Death ) ) )
2015-05-12 15:40:42 +00:00
return false ;
}
return true ;
}
2015-05-20 00:18:20 +00:00
const osg : : Node * Animation : : getNode ( const std : : string & name ) const
{
std : : string lowerName = Misc : : StringUtils : : lowerCase ( name ) ;
2016-02-09 15:18:19 +00:00
NodeMap : : const_iterator found = getNodeMap ( ) . find ( lowerName ) ;
if ( found = = getNodeMap ( ) . end ( ) )
2015-05-29 23:41:38 +00:00
return NULL ;
else
return found - > second ;
2015-05-20 00:18:20 +00:00
}
2015-09-16 14:15:55 +00:00
void Animation : : setAlpha ( float alpha )
{
if ( alpha = = mAlpha )
return ;
mAlpha = alpha ;
if ( alpha ! = 1.f )
{
osg : : StateSet * stateset ( new osg : : StateSet ) ;
osg : : BlendFunc * blendfunc ( new osg : : BlendFunc ) ;
stateset - > setAttributeAndModes ( blendfunc , osg : : StateAttribute : : ON | osg : : StateAttribute : : OVERRIDE ) ;
// FIXME: overriding diffuse/ambient/emissive colors
osg : : Material * material ( new osg : : Material ) ;
material - > setColorMode ( osg : : Material : : OFF ) ;
material - > setDiffuse ( osg : : Material : : FRONT_AND_BACK , osg : : Vec4f ( 1 , 1 , 1 , alpha ) ) ;
material - > setAmbient ( osg : : Material : : FRONT_AND_BACK , osg : : Vec4f ( 1 , 1 , 1 , 1 ) ) ;
stateset - > setAttributeAndModes ( material , osg : : StateAttribute : : ON | osg : : StateAttribute : : OVERRIDE ) ;
mObjectRoot - > setStateSet ( stateset ) ;
2016-02-18 21:48:53 +00:00
mResourceSystem - > getSceneManager ( ) - > recreateShaders ( mObjectRoot ) ;
2015-09-16 14:15:55 +00:00
}
else
{
mObjectRoot - > setStateSet ( NULL ) ;
2016-02-18 21:48:53 +00:00
mResourceSystem - > getSceneManager ( ) - > recreateShaders ( mObjectRoot ) ;
2015-09-16 14:15:55 +00:00
}
2015-10-19 20:17:04 +00:00
setRenderBin ( ) ;
}
void Animation : : setRenderBin ( )
{
if ( mAlpha ! = 1.f )
{
osg : : StateSet * stateset = mObjectRoot - > getOrCreateStateSet ( ) ;
stateset - > setRenderingHint ( osg : : StateSet : : TRANSPARENT_BIN ) ;
stateset - > setRenderBinMode ( osg : : StateSet : : OVERRIDE_RENDERBIN_DETAILS ) ;
}
else if ( osg : : StateSet * stateset = mObjectRoot - > getStateSet ( ) )
stateset - > setRenderBinToInherit ( ) ;
2015-09-16 14:15:55 +00:00
}
2015-07-02 15:19:30 +00:00
void Animation : : setLightEffect ( float effect )
{
if ( effect = = 0 )
{
if ( mGlowLight )
{
mInsert - > removeChild ( mGlowLight ) ;
mGlowLight = NULL ;
}
}
else
{
2015-12-03 23:06:22 +00:00
effect + = 3 ;
float radius = effect * 66.f ;
float linearAttenuation = 0.5f / effect ;
if ( ! mGlowLight | | linearAttenuation ! = mGlowLight - > getLight ( 0 ) - > getLinearAttenuation ( ) )
2015-07-02 15:19:30 +00:00
{
2015-12-03 23:06:22 +00:00
if ( mGlowLight )
{
mInsert - > removeChild ( mGlowLight ) ;
mGlowLight = NULL ;
}
osg : : ref_ptr < osg : : Light > light ( new osg : : Light ) ;
2015-07-02 15:19:30 +00:00
light - > setDiffuse ( osg : : Vec4f ( 0 , 0 , 0 , 0 ) ) ;
light - > setSpecular ( osg : : Vec4f ( 0 , 0 , 0 , 0 ) ) ;
light - > setAmbient ( osg : : Vec4f ( 1.5f , 1.5f , 1.5f , 1.f ) ) ;
2015-12-03 23:06:22 +00:00
light - > setLinearAttenuation ( linearAttenuation ) ;
mGlowLight = new SceneUtil : : LightSource ;
mGlowLight - > setNodeMask ( Mask_Lighting ) ;
2015-07-02 15:19:30 +00:00
mInsert - > addChild ( mGlowLight ) ;
2015-12-03 23:06:22 +00:00
mGlowLight - > setLight ( light ) ;
2015-07-02 15:19:30 +00:00
}
2015-12-03 23:06:22 +00:00
mGlowLight - > setRadius ( radius ) ;
2015-07-02 15:19:30 +00:00
}
}
2015-06-22 19:06:27 +00:00
void Animation : : addControllers ( )
{
mHeadController = NULL ;
2015-10-13 15:55:57 +00:00
if ( mPtr . getClass ( ) . isBipedal ( mPtr ) )
2015-06-22 19:06:27 +00:00
{
2016-02-09 15:18:19 +00:00
NodeMap : : const_iterator found = getNodeMap ( ) . find ( " bip01 head " ) ;
2016-02-09 15:20:17 +00:00
if ( found ! = getNodeMap ( ) . end ( ) )
2015-10-13 15:55:57 +00:00
{
2016-02-09 15:20:17 +00:00
osg : : MatrixTransform * node = found - > second ;
2016-02-29 19:23:19 +00:00
bool foundKeyframeCtrl = false ;
osg : : Callback * cb = node - > getUpdateCallback ( ) ;
while ( cb )
{
if ( dynamic_cast < NifOsg : : KeyframeController * > ( cb ) )
{
foundKeyframeCtrl = true ;
break ;
}
2016-03-02 12:20:23 +00:00
cb = cb - > getNestedCallback ( ) ;
2016-02-29 19:23:19 +00:00
}
if ( foundKeyframeCtrl )
{
mHeadController = new RotateController ( mObjectRoot . get ( ) ) ;
node - > addUpdateCallback ( mHeadController ) ;
mActiveControllers . insert ( std : : make_pair ( node , mHeadController ) ) ;
}
2015-10-13 15:55:57 +00:00
}
2015-06-22 19:06:27 +00:00
}
}
void Animation : : setHeadPitch ( float pitchRadians )
{
mHeadPitchRadians = pitchRadians ;
}
void Animation : : setHeadYaw ( float yawRadians )
{
mHeadYawRadians = yawRadians ;
}
float Animation : : getHeadPitch ( ) const
{
return mHeadPitchRadians ;
}
float Animation : : getHeadYaw ( ) const
{
return mHeadYawRadians ;
}
// ------------------------------------------------------
2015-04-23 18:41:31 +00:00
float Animation : : AnimationTime : : getValue ( osg : : NodeVisitor * )
{
2015-05-14 14:33:41 +00:00
if ( mTimePtr )
return * mTimePtr ;
return 0.f ;
2015-04-23 18:41:31 +00:00
}
2015-04-19 15:55:56 +00:00
float EffectAnimationTime : : getValue ( osg : : NodeVisitor * )
2015-04-18 23:57:52 +00:00
{
return mTime ;
}
2015-04-19 15:55:56 +00:00
void EffectAnimationTime : : addTime ( float duration )
2015-04-18 23:57:52 +00:00
{
mTime + = duration ;
}
2015-04-19 15:55:56 +00:00
void EffectAnimationTime : : resetTime ( float time )
2015-04-18 23:57:52 +00:00
{
mTime = time ;
}
2015-04-19 15:55:56 +00:00
float EffectAnimationTime : : getTime ( ) const
2015-04-18 23:57:52 +00:00
{
return mTime ;
}
2015-04-12 13:34:50 +00:00
// --------------------------------------------------------------------------------
2013-05-11 05:22:39 +00:00
2015-05-28 13:44:58 +00:00
ObjectAnimation : : ObjectAnimation ( const MWWorld : : Ptr & ptr , const std : : string & model , Resource : : ResourceSystem * resourceSystem , bool animated , bool allowLight )
2015-04-12 13:34:50 +00:00
: Animation ( ptr , osg : : ref_ptr < osg : : Group > ( ptr . getRefData ( ) . getBaseNode ( ) ) , resourceSystem )
2012-07-21 21:41:26 +00:00
{
2015-04-12 13:34:50 +00:00
if ( ! model . empty ( ) )
2013-05-12 11:34:37 +00:00
{
2015-08-15 17:01:21 +00:00
setObjectRoot ( model , false , false , false ) ;
2015-05-28 13:44:58 +00:00
if ( animated )
addAnimSource ( model ) ;
2015-04-16 23:23:37 +00:00
if ( ! ptr . getClass ( ) . getEnchantment ( ptr ) . empty ( ) )
addGlow ( mObjectRoot , getEnchantmentColor ( ptr ) ) ;
2013-05-12 11:34:37 +00:00
}
2015-06-02 15:06:55 +00:00
if ( ptr . getTypeName ( ) = = typeid ( ESM : : Light ) . name ( ) & & allowLight )
addExtraLight ( getOrCreateObjectRoot ( ) , ptr . get < ESM : : Light > ( ) - > mBase ) ;
2016-05-19 20:34:27 +00:00
2016-05-19 20:35:25 +00:00
if ( ! allowLight & & mObjectRoot )
2016-05-19 20:34:27 +00:00
{
RemoveParticlesVisitor visitor ;
mObjectRoot - > accept ( visitor ) ;
visitor . remove ( ) ;
}
2013-08-07 02:45:07 +00:00
}
2015-06-14 21:13:26 +00:00
Animation : : AnimState : : ~ AnimState ( )
{
}
// ------------------------------
PartHolder : : PartHolder ( osg : : ref_ptr < osg : : Node > node )
: mNode ( node )
{
}
PartHolder : : ~ PartHolder ( )
{
2016-03-06 10:34:15 +00:00
if ( mNode . get ( ) & & ! mNode - > getNumParents ( ) )
std : : cerr < < " Warning: part has no parents " < < std : : endl ;
2016-02-09 15:10:42 +00:00
if ( mNode . get ( ) & & mNode - > getNumParents ( ) )
2016-03-06 10:33:07 +00:00
{
if ( mNode - > getNumParents ( ) > 1 )
std : : cerr < < " Warning: part has multiple parents " < < mNode - > getNumParents ( ) < < " " < < mNode . get ( ) < < std : : endl ;
2015-06-14 21:13:26 +00:00
mNode - > getParent ( 0 ) - > removeChild ( mNode ) ;
2016-03-06 10:33:07 +00:00
}
2015-06-14 21:13:26 +00:00
}
2016-02-09 15:10:42 +00:00
void PartHolder : : unlink ( )
{
mNode = NULL ;
}
2012-02-20 13:02:24 +00:00
}