2015-03-26 17:02:51 +00:00
# include "scenemanager.hpp"
2017-03-04 20:14:29 +00:00
# include <cstdlib>
2015-03-26 17:02:51 +00:00
# include <osg/Node>
2015-03-27 23:30:49 +00:00
# include <osg/UserDataContainer>
# include <osgParticle/ParticleSystem>
2015-03-26 17:02:51 +00:00
2015-05-26 18:20:18 +00:00
# include <osgUtil/IncrementalCompileOperation>
2015-06-16 21:50:19 +00:00
# include <osgDB/SharedStateManager>
# include <osgDB/Registry>
2018-08-14 15:42:41 +00:00
# include <components/debug/debuglog.hpp>
2015-03-26 17:02:51 +00:00
# include <components/nifosg/nifloader.hpp>
# include <components/nif/niffile.hpp>
2017-02-23 17:44:50 +00:00
# include <components/misc/stringops.hpp>
2015-03-28 01:20:20 +00:00
# include <components/vfs/manager.hpp>
2015-03-26 17:02:51 +00:00
# include <components/sceneutil/clone.hpp>
2015-06-11 00:37:49 +00:00
# include <components/sceneutil/util.hpp>
2016-02-05 17:51:51 +00:00
# include <components/sceneutil/controller.hpp>
2017-02-23 18:42:12 +00:00
# include <components/sceneutil/optimizer.hpp>
2021-02-03 19:16:54 +00:00
# include <components/sceneutil/visitor.hpp>
2015-03-26 17:02:51 +00:00
2016-02-16 17:18:48 +00:00
# include <components/shader/shadervisitor.hpp>
# include <components/shader/shadermanager.hpp>
2016-02-05 22:03:53 +00:00
# include "imagemanager.hpp"
2015-12-01 22:04:02 +00:00
# include "niffilemanager.hpp"
2016-02-05 23:15:12 +00:00
# include "objectcache.hpp"
2016-02-09 17:33:02 +00:00
# include "multiobjectcache.hpp"
2015-11-16 22:26:43 +00:00
2015-03-27 23:30:49 +00:00
namespace
{
2017-02-21 17:59:38 +00:00
class InitWorldSpaceParticlesCallback : public osg : : NodeCallback
{
public :
2020-10-16 18:18:54 +00:00
void operator ( ) ( osg : : Node * node , osg : : NodeVisitor * nv ) override
2017-02-21 17:59:38 +00:00
{
osgParticle : : ParticleSystem * partsys = static_cast < osgParticle : : ParticleSystem * > ( node ) ;
// HACK: Ignore the InverseWorldMatrix transform the particle system is attached to
if ( partsys - > getNumParents ( ) & & partsys - > getParent ( 0 ) - > getNumParents ( ) )
transformInitialParticles ( partsys , partsys - > getParent ( 0 ) - > getParent ( 0 ) ) ;
node - > removeUpdateCallback ( this ) ;
}
void transformInitialParticles ( osgParticle : : ParticleSystem * partsys , osg : : Node * node )
{
osg : : NodePathList nodepaths = node - > getParentalNodePaths ( ) ;
if ( nodepaths . empty ( ) )
return ;
osg : : Matrixf worldMat = osg : : computeLocalToWorld ( nodepaths [ 0 ] ) ;
worldMat . orthoNormalize ( worldMat ) ; // scale is already applied on the particle node
for ( int i = 0 ; i < partsys - > numParticles ( ) ; + + i )
{
partsys - > getParticle ( i ) - > transformPositionVelocity ( worldMat ) ;
}
// transform initial bounds to worldspace
osg : : BoundingSphere sphere ( partsys - > getInitialBound ( ) ) ;
SceneUtil : : transformBoundingSphere ( worldMat , sphere ) ;
osg : : BoundingBox box ;
box . expandBy ( sphere ) ;
partsys - > setInitialBound ( box ) ;
}
} ;
class InitParticlesVisitor : public osg : : NodeVisitor
2015-03-27 23:30:49 +00:00
{
public :
2015-11-10 16:00:33 +00:00
/// @param mask The node mask to set on ParticleSystem nodes.
2017-02-21 17:59:38 +00:00
InitParticlesVisitor ( unsigned int mask )
2015-03-27 23:30:49 +00:00
: osg : : NodeVisitor ( TRAVERSE_ALL_CHILDREN )
2015-11-10 16:00:33 +00:00
, mMask ( mask )
2015-03-27 23:30:49 +00:00
{
}
2015-11-10 17:21:56 +00:00
bool isWorldSpaceParticleSystem ( osgParticle : : ParticleSystem * partsys )
2015-03-27 23:30:49 +00:00
{
2015-11-10 17:21:56 +00:00
// HACK: ParticleSystem has no getReferenceFrame()
return ( partsys - > getUserDataContainer ( )
& & partsys - > getUserDataContainer ( ) - > getNumDescriptions ( ) > 0
& & partsys - > getUserDataContainer ( ) - > getDescriptions ( ) [ 0 ] = = " worldspace " ) ;
}
2020-10-16 18:18:54 +00:00
void apply ( osg : : Drawable & drw ) override
2015-11-10 17:21:56 +00:00
{
if ( osgParticle : : ParticleSystem * partsys = dynamic_cast < osgParticle : : ParticleSystem * > ( & drw ) )
{
if ( isWorldSpaceParticleSystem ( partsys ) )
{
2017-02-21 17:59:38 +00:00
partsys - > addUpdateCallback ( new InitWorldSpaceParticlesCallback ) ;
2015-11-10 17:21:56 +00:00
}
partsys - > setNodeMask ( mMask ) ;
}
2015-03-27 23:30:49 +00:00
}
2015-11-10 16:00:33 +00:00
private :
unsigned int mMask ;
2015-03-27 23:30:49 +00:00
} ;
}
2015-03-26 17:02:51 +00:00
namespace Resource
{
2021-01-13 09:33:46 +00:00
void TemplateMultiRef : : addRef ( const osg : : Node * node )
{
mObjects . emplace_back ( node ) ;
}
2015-03-26 17:02:51 +00:00
2017-02-23 13:49:12 +00:00
class SharedStateManager : public osgDB : : SharedStateManager
{
public :
unsigned int getNumSharedTextures ( ) const
{
return _sharedTextureList . size ( ) ;
}
unsigned int getNumSharedStateSets ( ) const
{
return _sharedStateSetList . size ( ) ;
}
2017-08-20 23:55:16 +00:00
void clearCache ( )
{
2020-06-25 19:46:07 +00:00
std : : lock_guard < OpenThreads : : Mutex > lock ( _listMutex ) ;
2017-08-20 23:55:16 +00:00
_sharedTextureList . clear ( ) ;
_sharedStateSetList . clear ( ) ;
}
2017-02-23 13:49:12 +00:00
} ;
2016-02-05 17:51:51 +00:00
/// Set texture filtering settings on textures contained in a FlipController.
class SetFilterSettingsControllerVisitor : public SceneUtil : : ControllerVisitor
{
public :
SetFilterSettingsControllerVisitor ( osg : : Texture : : FilterMode minFilter , osg : : Texture : : FilterMode magFilter , int maxAnisotropy )
: mMinFilter ( minFilter )
, mMagFilter ( magFilter )
, mMaxAnisotropy ( maxAnisotropy )
{
}
2020-10-16 18:18:54 +00:00
void visit ( osg : : Node & node , SceneUtil : : Controller & ctrl ) override
2016-02-05 17:51:51 +00:00
{
if ( NifOsg : : FlipController * flipctrl = dynamic_cast < NifOsg : : FlipController * > ( & ctrl ) )
{
for ( std : : vector < osg : : ref_ptr < osg : : Texture2D > > : : iterator it = flipctrl - > getTextures ( ) . begin ( ) ; it ! = flipctrl - > getTextures ( ) . end ( ) ; + + it )
{
osg : : Texture * tex = * it ;
tex - > setFilter ( osg : : Texture : : MIN_FILTER , mMinFilter ) ;
tex - > setFilter ( osg : : Texture : : MAG_FILTER , mMagFilter ) ;
tex - > setMaxAnisotropy ( mMaxAnisotropy ) ;
}
}
}
private :
osg : : Texture : : FilterMode mMinFilter ;
osg : : Texture : : FilterMode mMagFilter ;
int mMaxAnisotropy ;
} ;
/// Set texture filtering settings on textures contained in StateSets.
class SetFilterSettingsVisitor : public osg : : NodeVisitor
{
public :
SetFilterSettingsVisitor ( osg : : Texture : : FilterMode minFilter , osg : : Texture : : FilterMode magFilter , int maxAnisotropy )
: osg : : NodeVisitor ( TRAVERSE_ALL_CHILDREN )
, mMinFilter ( minFilter )
, mMagFilter ( magFilter )
, mMaxAnisotropy ( maxAnisotropy )
{
}
2020-10-16 18:18:54 +00:00
void apply ( osg : : Node & node ) override
2016-02-05 17:51:51 +00:00
{
osg : : StateSet * stateset = node . getStateSet ( ) ;
if ( stateset )
2016-02-16 18:17:04 +00:00
applyStateSet ( stateset ) ;
2016-02-05 20:08:32 +00:00
2016-02-05 17:51:51 +00:00
traverse ( node ) ;
}
2016-02-16 18:17:04 +00:00
void applyStateSet ( osg : : StateSet * stateset )
2016-02-05 17:51:51 +00:00
{
const osg : : StateSet : : TextureAttributeList & texAttributes = stateset - > getTextureAttributeList ( ) ;
for ( unsigned int unit = 0 ; unit < texAttributes . size ( ) ; + + unit )
{
osg : : StateAttribute * texture = stateset - > getTextureAttribute ( unit , osg : : StateAttribute : : TEXTURE ) ;
2016-02-07 13:53:42 +00:00
if ( texture )
2016-02-16 18:17:04 +00:00
applyStateAttribute ( texture ) ;
2016-02-05 17:51:51 +00:00
}
}
2016-02-16 18:17:04 +00:00
void applyStateAttribute ( osg : : StateAttribute * attr )
2016-02-05 17:51:51 +00:00
{
osg : : Texture * tex = attr - > asTexture ( ) ;
if ( tex )
{
tex - > setFilter ( osg : : Texture : : MIN_FILTER , mMinFilter ) ;
tex - > setFilter ( osg : : Texture : : MAG_FILTER , mMagFilter ) ;
tex - > setMaxAnisotropy ( mMaxAnisotropy ) ;
}
}
private :
osg : : Texture : : FilterMode mMinFilter ;
osg : : Texture : : FilterMode mMagFilter ;
int mMaxAnisotropy ;
} ;
2016-02-05 22:10:27 +00:00
SceneManager : : SceneManager ( const VFS : : Manager * vfs , Resource : : ImageManager * imageManager , Resource : : NifFileManager * nifFileManager )
2016-02-06 15:57:54 +00:00
: ResourceManager ( vfs )
2021-03-02 07:30:54 +00:00
, mShaderManager ( new Shader : : ShaderManager )
2016-02-18 16:08:18 +00:00
, mForceShaders ( false )
2016-02-20 19:36:29 +00:00
, mClampLighting ( true )
2016-02-20 16:57:19 +00:00
, mAutoUseNormalMaps ( false )
2016-02-21 00:26:45 +00:00
, mAutoUseSpecularMaps ( false )
2020-11-09 10:53:58 +00:00
, mApplyLightingToEnvMaps ( false )
2021-03-15 04:42:34 +00:00
, mLightingMethod ( SceneUtil : : LightingMethod : : FFP )
2021-04-10 06:58:00 +00:00
, mConvertAlphaTestToAlphaToCoverage ( false )
2016-02-09 17:33:02 +00:00
, mInstanceCache ( new MultiObjectCache )
2017-02-23 13:49:12 +00:00
, mSharedStateManager ( new SharedStateManager )
2016-02-05 22:10:27 +00:00
, mImageManager ( imageManager )
2015-12-01 22:04:02 +00:00
, mNifFileManager ( nifFileManager )
2016-02-05 17:51:51 +00:00
, mMinFilter ( osg : : Texture : : LINEAR_MIPMAP_LINEAR )
, mMagFilter ( osg : : Texture : : LINEAR )
, mMaxAnisotropy ( 1 )
, mUnRefImageDataAfterApply ( false )
2015-11-10 16:00:33 +00:00
, mParticleSystemMask ( ~ 0u )
2015-03-26 17:02:51 +00:00
{
}
2016-02-18 16:08:18 +00:00
void SceneManager : : setForceShaders ( bool force )
{
mForceShaders = force ;
}
2016-02-19 00:30:15 +00:00
bool SceneManager : : getForceShaders ( ) const
{
return mForceShaders ;
}
2020-01-12 07:42:47 +00:00
void SceneManager : : recreateShaders ( osg : : ref_ptr < osg : : Node > node , const std : : string & shaderPrefix , bool translucentFramebuffer , bool forceShadersForNode )
2016-02-18 21:48:53 +00:00
{
2021-01-20 23:37:19 +00:00
osg : : ref_ptr < Shader : : ShaderVisitor > shaderVisitor ( createShaderVisitor ( shaderPrefix , translucentFramebuffer ) ) ;
2017-10-15 15:24:23 +00:00
shaderVisitor - > setAllowedToModifyStateSets ( false ) ;
2020-01-12 07:42:47 +00:00
if ( forceShadersForNode )
shaderVisitor - > setForceShaders ( true ) ;
2017-10-15 15:24:23 +00:00
node - > accept ( * shaderVisitor ) ;
2016-02-18 21:48:53 +00:00
}
2021-02-19 19:59:48 +00:00
void SceneManager : : reinstateRemovedState ( osg : : ref_ptr < osg : : Node > node )
{
osg : : ref_ptr < Shader : : ReinstateRemovedStateVisitor > reinstateRemovedStateVisitor = new Shader : : ReinstateRemovedStateVisitor ( false ) ;
node - > accept ( * reinstateRemovedStateVisitor ) ;
}
2016-02-18 16:08:18 +00:00
void SceneManager : : setClampLighting ( bool clamp )
{
mClampLighting = clamp ;
}
2016-02-19 00:30:15 +00:00
bool SceneManager : : getClampLighting ( ) const
{
return mClampLighting ;
}
2016-02-20 16:57:19 +00:00
void SceneManager : : setAutoUseNormalMaps ( bool use )
{
mAutoUseNormalMaps = use ;
}
void SceneManager : : setNormalMapPattern ( const std : : string & pattern )
{
mNormalMapPattern = pattern ;
}
2016-03-22 20:00:31 +00:00
void SceneManager : : setNormalHeightMapPattern ( const std : : string & pattern )
{
mNormalHeightMapPattern = pattern ;
}
2016-02-20 18:02:11 +00:00
void SceneManager : : setAutoUseSpecularMaps ( bool use )
{
mAutoUseSpecularMaps = use ;
}
void SceneManager : : setSpecularMapPattern ( const std : : string & pattern )
{
mSpecularMapPattern = pattern ;
}
2020-11-09 10:53:58 +00:00
void SceneManager : : setApplyLightingToEnvMaps ( bool apply )
{
mApplyLightingToEnvMaps = apply ;
}
2021-04-16 18:55:40 +00:00
void SceneManager : : setSupportedLightingMethods ( const SceneUtil : : LightManager : : SupportedMethods & supported )
{
mSupportedLightingMethods = supported ;
}
bool SceneManager : : isSupportedLightingMethod ( SceneUtil : : LightingMethod method ) const
{
return mSupportedLightingMethods [ static_cast < int > ( method ) ] ;
}
2021-03-15 04:42:34 +00:00
void SceneManager : : setLightingMethod ( SceneUtil : : LightingMethod method )
2021-02-21 18:38:15 +00:00
{
2021-03-15 04:42:34 +00:00
mLightingMethod = method ;
2021-02-21 18:38:15 +00:00
}
2021-03-15 04:42:34 +00:00
SceneUtil : : LightingMethod SceneManager : : getLightingMethod ( ) const
{
return mLightingMethod ;
}
2020-12-26 22:45:53 +00:00
void SceneManager : : setConvertAlphaTestToAlphaToCoverage ( bool convert )
2021-02-21 18:38:15 +00:00
{
2020-12-26 22:45:53 +00:00
mConvertAlphaTestToAlphaToCoverage = convert ;
2021-02-21 18:38:15 +00:00
}
2015-05-08 15:52:35 +00:00
SceneManager : : ~ SceneManager ( )
{
// this has to be defined in the .cpp file as we can't delete incomplete types
2015-11-16 22:26:43 +00:00
}
2016-02-19 00:30:15 +00:00
Shader : : ShaderManager & SceneManager : : getShaderManager ( )
{
return * mShaderManager . get ( ) ;
}
2016-02-16 17:18:48 +00:00
void SceneManager : : setShaderPath ( const std : : string & path )
{
mShaderManager - > setShaderPath ( path ) ;
}
2017-02-14 23:55:35 +00:00
bool SceneManager : : checkLoaded ( const std : : string & name , double timeStamp )
{
std : : string normalized = name ;
mVFS - > normalizeFilename ( normalized ) ;
return mCache - > checkInObjectCache ( normalized , timeStamp ) ;
}
2015-11-16 22:26:43 +00:00
/// @brief Callback to read image files from the VFS.
class ImageReadCallback : public osgDB : : ReadFileCallback
{
public :
2016-02-05 22:10:27 +00:00
ImageReadCallback ( Resource : : ImageManager * imageMgr )
: mImageManager ( imageMgr )
2015-11-16 22:26:43 +00:00
{
}
2020-10-16 18:18:54 +00:00
osgDB : : ReaderWriter : : ReadResult readImage ( const std : : string & filename , const osgDB : : Options * options ) override
2015-11-16 22:26:43 +00:00
{
try
{
2016-02-05 22:10:27 +00:00
return osgDB : : ReaderWriter : : ReadResult ( mImageManager - > getImage ( filename ) , osgDB : : ReaderWriter : : ReadResult : : FILE_LOADED ) ;
2015-11-16 22:26:43 +00:00
}
catch ( std : : exception & e )
{
return osgDB : : ReaderWriter : : ReadResult ( e . what ( ) ) ;
}
}
private :
2016-02-05 22:10:27 +00:00
Resource : : ImageManager * mImageManager ;
2015-11-16 22:26:43 +00:00
} ;
2016-02-05 22:10:27 +00:00
osg : : ref_ptr < osg : : Node > load ( Files : : IStreamPtr file , const std : : string & normalizedFilename , Resource : : ImageManager * imageManager , Resource : : NifFileManager * nifFileManager )
2015-11-16 22:26:43 +00:00
{
2020-11-28 13:03:10 +00:00
std : : string ext = Resource : : getFileExtension ( normalizedFilename ) ;
2015-11-16 22:26:43 +00:00
if ( ext = = " nif " )
2016-02-05 22:10:27 +00:00
return NifOsg : : Loader : : load ( nifFileManager - > get ( normalizedFilename ) , imageManager ) ;
2015-11-16 22:26:43 +00:00
else
{
osgDB : : ReaderWriter * reader = osgDB : : Registry : : instance ( ) - > getReaderWriterForExtension ( ext ) ;
if ( ! reader )
{
std : : stringstream errormsg ;
errormsg < < " Error loading " < < normalizedFilename < < " : no readerwriter for ' " < < ext < < " ' found " < < std : : endl ;
throw std : : runtime_error ( errormsg . str ( ) ) ;
}
osg : : ref_ptr < osgDB : : Options > options ( new osgDB : : Options ) ;
// Set a ReadFileCallback so that image files referenced in the model are read from our virtual file system instead of the osgDB.
// Note, for some formats (.obj/.mtl) that reference other (non-image) files a findFileCallback would be necessary.
// but findFileCallback does not support virtual files, so we can't implement it.
2016-02-05 22:10:27 +00:00
options - > setReadFileCallback ( new ImageReadCallback ( imageManager ) ) ;
2020-06-16 11:37:06 +00:00
if ( ext = = " dae " ) options - > setOptionString ( " daeUseSequencedTextureUnits " ) ;
2015-11-16 22:26:43 +00:00
osgDB : : ReaderWriter : : ReadResult result = reader - > readNode ( * file , options ) ;
if ( ! result . success ( ) )
{
std : : stringstream errormsg ;
errormsg < < " Error loading " < < normalizedFilename < < " : " < < result . message ( ) < < " code " < < result . status ( ) < < std : : endl ;
throw std : : runtime_error ( errormsg . str ( ) ) ;
}
2021-02-03 19:16:54 +00:00
// Recognize and hide collision node
unsigned int hiddenNodeMask = 0 ;
SceneUtil : : FindByNameVisitor nameFinder ( " Collision " ) ;
result . getNode ( ) - > accept ( nameFinder ) ;
if ( nameFinder . mFoundNode )
nameFinder . mFoundNode - > setNodeMask ( hiddenNodeMask ) ;
2015-11-16 22:26:43 +00:00
return result . getNode ( ) ;
}
2015-05-08 15:52:35 +00:00
}
2017-02-23 18:42:12 +00:00
class CanOptimizeCallback : public SceneUtil : : Optimizer : : IsOperationPermissibleForObjectCallback
2017-02-23 17:44:50 +00:00
{
public :
bool isReservedName ( const std : : string & name ) const
{
2017-05-07 21:28:56 +00:00
if ( name . empty ( ) )
return false ;
static std : : vector < std : : string > reservedNames ;
2017-02-23 17:44:50 +00:00
if ( reservedNames . empty ( ) )
{
2018-08-05 08:22:45 +00:00
const char * reserved [ ] = { " Head " , " Neck " , " Chest " , " Groin " , " Right Hand " , " Left Hand " , " Right Wrist " , " Left Wrist " , " Shield Bone " , " Right Forearm " , " Left Forearm " , " Right Upper Arm " ,
" Left Upper Arm " , " Right Foot " , " Left Foot " , " Right Ankle " , " Left Ankle " , " Right Knee " , " Left Knee " , " Right Upper Leg " , " Left Upper Leg " , " Right Clavicle " ,
2021-02-03 12:25:50 +00:00
" Left Clavicle " , " Weapon Bone " , " Tail " , " Bip01 " , " Root Bone " , " BoneOffset " , " AttachLight " , " Arrow " , " Camera " , " Collision " , " Right_Wrist " , " Left_Wrist " ,
" Shield_Bone " , " Right_Forearm " , " Left_Forearm " , " Right_Upper_Arm " , " Left_Clavicle " , " Weapon_Bone " , " Root_Bone " } ;
2018-03-03 10:16:21 +00:00
2017-05-07 21:28:56 +00:00
reservedNames = std : : vector < std : : string > ( reserved , reserved + sizeof ( reserved ) / sizeof ( reserved [ 0 ] ) ) ;
2017-02-25 20:48:07 +00:00
for ( unsigned int i = 0 ; i < sizeof ( reserved ) / sizeof ( reserved [ 0 ] ) ; + + i )
2017-05-07 21:28:56 +00:00
reservedNames . push_back ( std : : string ( " Tri " ) + reserved [ i ] ) ;
std : : sort ( reservedNames . begin ( ) , reservedNames . end ( ) , Misc : : StringUtils : : ciLess ) ;
2017-02-23 17:44:50 +00:00
}
2017-05-07 21:28:56 +00:00
std : : vector < std : : string > : : iterator it = Misc : : StringUtils : : partialBinarySearch ( reservedNames . begin ( ) , reservedNames . end ( ) , name ) ;
return it ! = reservedNames . end ( ) ;
2017-02-23 17:44:50 +00:00
}
2020-10-16 18:18:54 +00:00
bool isOperationPermissibleForObjectImplementation ( const SceneUtil : : Optimizer * optimizer , const osg : : Drawable * node , unsigned int option ) const override
2017-02-23 21:05:32 +00:00
{
if ( option & SceneUtil : : Optimizer : : FLATTEN_STATIC_TRANSFORMS )
{
if ( node - > asGeometry ( ) & & node - > className ( ) = = std : : string ( " Geometry " ) )
return true ;
else
return false ; //ParticleSystem would have to convert space of all the processors, RigGeometry would have to convert bones... theoretically possible, but very complicated
}
return ( option & optimizer - > getPermissibleOptimizationsForObject ( node ) ) ! = 0 ;
}
2020-10-16 18:18:54 +00:00
bool isOperationPermissibleForObjectImplementation ( const SceneUtil : : Optimizer * optimizer , const osg : : Node * node , unsigned int option ) const override
2017-02-23 17:44:50 +00:00
{
if ( node - > getNumDescriptions ( ) > 0 ) return false ;
if ( node - > getDataVariance ( ) = = osg : : Object : : DYNAMIC ) return false ;
if ( isReservedName ( node - > getName ( ) ) ) return false ;
return ( option & optimizer - > getPermissibleOptimizationsForObject ( node ) ) ! = 0 ;
}
} ;
bool canOptimize ( const std : : string & filename )
{
size_t slashpos = filename . find_last_of ( " \\ / " ) ;
if ( slashpos ! = std : : string : : npos & & slashpos + 1 < filename . size ( ) )
{
std : : string basename = filename . substr ( slashpos + 1 ) ;
2017-11-10 21:09:24 +00:00
// xmesh.nif can not be optimized because there are keyframes added in post
2017-02-23 17:44:50 +00:00
if ( ! basename . empty ( ) & & basename [ 0 ] = = ' x ' )
return false ;
2017-11-10 21:09:24 +00:00
// NPC skeleton files can not be optimized because of keyframes added in post
// (most of them are usually named like 'xbase_anim.nif' anyway, but not all of them :( )
if ( basename . compare ( 0 , 9 , " base_anim " ) = = 0 | | basename . compare ( 0 , 4 , " skin " ) = = 0 )
return false ;
2017-02-23 17:44:50 +00:00
}
// For spell VFX, DummyXX nodes must remain intact. Not adding those to reservedNames to avoid being overly cautious - instead, decide on filename
if ( filename . find ( " vfx_pattern " ) ! = std : : string : : npos )
return false ;
return true ;
}
2017-03-04 20:14:29 +00:00
unsigned int getOptimizationOptions ( )
{
using namespace SceneUtil ;
const char * env = getenv ( " OPENMW_OPTIMIZE " ) ;
unsigned int options = Optimizer : : FLATTEN_STATIC_TRANSFORMS | Optimizer : : REMOVE_REDUNDANT_NODES | Optimizer : : MERGE_GEOMETRY ;
if ( env )
{
std : : string str ( env ) ;
2017-07-10 21:49:42 +00:00
if ( str . find ( " OFF " ) ! = std : : string : : npos | | str . find ( " 0 " ) ! = std : : string : : npos ) options = 0 ;
2017-03-04 20:14:29 +00:00
if ( str . find ( " ~FLATTEN_STATIC_TRANSFORMS " ) ! = std : : string : : npos ) options ^ = Optimizer : : FLATTEN_STATIC_TRANSFORMS ;
else if ( str . find ( " FLATTEN_STATIC_TRANSFORMS " ) ! = std : : string : : npos ) options | = Optimizer : : FLATTEN_STATIC_TRANSFORMS ;
if ( str . find ( " ~REMOVE_REDUNDANT_NODES " ) ! = std : : string : : npos ) options ^ = Optimizer : : REMOVE_REDUNDANT_NODES ;
else if ( str . find ( " REMOVE_REDUNDANT_NODES " ) ! = std : : string : : npos ) options | = Optimizer : : REMOVE_REDUNDANT_NODES ;
if ( str . find ( " ~MERGE_GEOMETRY " ) ! = std : : string : : npos ) options ^ = Optimizer : : MERGE_GEOMETRY ;
else if ( str . find ( " MERGE_GEOMETRY " ) ! = std : : string : : npos ) options | = Optimizer : : MERGE_GEOMETRY ;
}
return options ;
}
2019-06-13 13:37:00 +00:00
osg : : ref_ptr < const osg : : Node > SceneManager : : getTemplate ( const std : : string & name , bool compile )
2015-03-26 17:02:51 +00:00
{
std : : string normalized = name ;
mVFS - > normalizeFilename ( normalized ) ;
2016-02-05 23:15:12 +00:00
osg : : ref_ptr < osg : : Object > obj = mCache - > getRefFromObjectCache ( normalized ) ;
if ( obj )
return osg : : ref_ptr < const osg : : Node > ( static_cast < osg : : Node * > ( obj . get ( ) ) ) ;
else
2015-03-26 17:02:51 +00:00
{
2015-07-27 20:59:20 +00:00
osg : : ref_ptr < osg : : Node > loaded ;
try
{
Files : : IStreamPtr file = mVFS - > get ( normalized ) ;
2015-03-26 17:02:51 +00:00
2016-02-05 22:10:27 +00:00
loaded = load ( file , normalized , mImageManager , mNifFileManager ) ;
2015-07-27 20:59:20 +00:00
}
catch ( std : : exception & e )
{
2020-11-18 23:11:56 +00:00
static const char * const sMeshTypes [ ] = { " nif " , " osg " , " osgt " , " osgb " , " osgx " , " osg2 " , " dae " } ;
2016-01-25 15:59:31 +00:00
for ( unsigned int i = 0 ; i < sizeof ( sMeshTypes ) / sizeof ( sMeshTypes [ 0 ] ) ; + + i )
{
normalized = " meshes/marker_error. " + std : : string ( sMeshTypes [ i ] ) ;
if ( mVFS - > exists ( normalized ) )
{
2018-08-14 15:42:41 +00:00
Log ( Debug : : Error ) < < " Failed to load ' " < < name < < " ': " < < e . what ( ) < < " , using marker_error. " < < sMeshTypes [ i ] < < " instead " ;
2016-01-25 15:59:31 +00:00
Files : : IStreamPtr file = mVFS - > get ( normalized ) ;
2016-02-05 22:10:27 +00:00
loaded = load ( file , normalized , mImageManager , mNifFileManager ) ;
2016-01-25 15:59:31 +00:00
break ;
}
}
if ( ! loaded )
throw ;
2015-07-27 20:59:20 +00:00
}
2015-05-26 18:20:18 +00:00
2016-02-05 17:51:51 +00:00
// set filtering settings
SetFilterSettingsVisitor setFilterSettingsVisitor ( mMinFilter , mMagFilter , mMaxAnisotropy ) ;
loaded - > accept ( setFilterSettingsVisitor ) ;
SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor ( mMinFilter , mMagFilter , mMaxAnisotropy ) ;
loaded - > accept ( setFilterSettingsControllerVisitor ) ;
2021-01-13 10:30:51 +00:00
osg : : ref_ptr < Shader : : ShaderVisitor > shaderVisitor ( createShaderVisitor ( ) ) ;
2017-10-15 15:24:23 +00:00
loaded - > accept ( * shaderVisitor ) ;
2016-02-16 17:18:48 +00:00
2016-02-05 17:51:51 +00:00
// share state
2017-02-23 17:44:50 +00:00
// do this before optimizing so the optimizer will be able to combine nodes more aggressively
// note, because StateSets will be shared at this point, StateSets can not be modified inside the optimizer
2016-02-07 15:37:35 +00:00
mSharedStateMutex . lock ( ) ;
2017-02-03 02:11:36 +00:00
mSharedStateManager - > share ( loaded . get ( ) ) ;
2016-02-07 15:37:35 +00:00
mSharedStateMutex . unlock ( ) ;
2015-06-16 21:50:19 +00:00
2017-02-23 17:44:50 +00:00
if ( canOptimize ( normalized ) )
{
2017-02-23 18:42:12 +00:00
SceneUtil : : Optimizer optimizer ;
2017-02-23 17:44:50 +00:00
optimizer . setIsOperationPermissibleForObjectCallback ( new CanOptimizeCallback ) ;
2017-03-04 20:14:29 +00:00
static const unsigned int options = getOptimizationOptions ( ) ;
optimizer . optimize ( loaded , options ) ;
2017-02-23 17:44:50 +00:00
}
2019-06-13 13:37:00 +00:00
if ( compile & & mIncrementalCompileOperation )
2015-05-26 18:20:18 +00:00
mIncrementalCompileOperation - > add ( loaded ) ;
2019-03-09 09:12:54 +00:00
else
loaded - > getBound ( ) ;
2015-03-26 17:02:51 +00:00
2016-02-05 23:15:12 +00:00
mCache - > addEntryToObjectCache ( normalized , loaded ) ;
2015-03-26 17:02:51 +00:00
return loaded ;
}
}
2016-02-09 17:33:02 +00:00
osg : : ref_ptr < osg : : Node > SceneManager : : cacheInstance ( const std : : string & name )
{
std : : string normalized = name ;
mVFS - > normalizeFilename ( normalized ) ;
osg : : ref_ptr < osg : : Node > node = createInstance ( normalized ) ;
2019-03-09 09:12:54 +00:00
// Note: osg::clone() does not calculate bound volumes.
// Do it immediately, otherwise we will need to update them for all objects
// during first update traversal, what may lead to stuttering during cell transitions
node - > getBound ( ) ;
2016-02-09 17:33:02 +00:00
mInstanceCache - > addEntryToObjectCache ( normalized , node . get ( ) ) ;
return node ;
}
osg : : ref_ptr < osg : : Node > SceneManager : : createInstance ( const std : : string & name )
2015-03-26 17:02:51 +00:00
{
osg : : ref_ptr < const osg : : Node > scene = getTemplate ( name ) ;
2017-03-01 02:00:49 +00:00
return createInstance ( scene ) ;
}
osg : : ref_ptr < osg : : Node > SceneManager : : createInstance ( const osg : : Node * base )
{
2020-05-16 13:37:00 +00:00
osg : : ref_ptr < osg : : Node > cloned = static_cast < osg : : Node * > ( base - > clone ( SceneUtil : : CopyOp ( ) ) ) ;
2016-02-07 17:56:21 +00:00
// add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache
2017-03-01 02:00:49 +00:00
cloned - > getOrCreateUserDataContainer ( ) - > addUserObject ( new TemplateRef ( base ) ) ;
2016-02-07 17:56:21 +00:00
2017-02-23 23:43:36 +00:00
// we can skip any scene graphs without update callbacks since we know that particle emitters will have an update callback set
if ( cloned - > getNumChildrenRequiringUpdateTraversal ( ) > 0 )
{
InitParticlesVisitor visitor ( mParticleSystemMask ) ;
cloned - > accept ( visitor ) ;
}
2015-03-27 23:30:49 +00:00
return cloned ;
}
2016-02-09 17:33:02 +00:00
osg : : ref_ptr < osg : : Node > SceneManager : : getInstance ( const std : : string & name )
2015-03-27 23:30:49 +00:00
{
2016-02-09 17:33:02 +00:00
std : : string normalized = name ;
mVFS - > normalizeFilename ( normalized ) ;
osg : : ref_ptr < osg : : Object > obj = mInstanceCache - > takeFromObjectCache ( normalized ) ;
if ( obj . get ( ) )
return static_cast < osg : : Node * > ( obj . get ( ) ) ;
return createInstance ( normalized ) ;
}
osg : : ref_ptr < osg : : Node > SceneManager : : getInstance ( const std : : string & name , osg : : Group * parentNode )
{
osg : : ref_ptr < osg : : Node > cloned = getInstance ( name ) ;
2015-03-27 23:30:49 +00:00
attachTo ( cloned , parentNode ) ;
return cloned ;
}
void SceneManager : : attachTo ( osg : : Node * instance , osg : : Group * parentNode ) const
{
parentNode - > addChild ( instance ) ;
2015-03-26 17:02:51 +00:00
}
2015-03-28 19:15:17 +00:00
void SceneManager : : releaseGLObjects ( osg : : State * state )
{
2016-02-05 23:15:12 +00:00
mCache - > releaseGLObjects ( state ) ;
2016-02-20 19:38:37 +00:00
mInstanceCache - > releaseGLObjects ( state ) ;
2017-08-26 19:28:23 +00:00
mShaderManager - > releaseGLObjects ( state ) ;
2020-06-25 19:46:07 +00:00
std : : lock_guard < std : : mutex > lock ( mSharedStateMutex ) ;
2017-08-26 19:28:23 +00:00
mSharedStateManager - > releaseGLObjects ( state ) ;
2015-03-28 19:15:17 +00:00
}
2015-05-26 18:20:18 +00:00
void SceneManager : : setIncrementalCompileOperation ( osgUtil : : IncrementalCompileOperation * ico )
{
mIncrementalCompileOperation = ico ;
}
2017-03-06 19:41:02 +00:00
osgUtil : : IncrementalCompileOperation * SceneManager : : getIncrementalCompileOperation ( )
{
return mIncrementalCompileOperation . get ( ) ;
}
2016-02-05 22:10:27 +00:00
Resource : : ImageManager * SceneManager : : getImageManager ( )
2015-04-14 13:55:56 +00:00
{
2016-02-05 22:10:27 +00:00
return mImageManager ;
2015-04-14 13:55:56 +00:00
}
2015-11-10 16:00:33 +00:00
void SceneManager : : setParticleSystemMask ( unsigned int mask )
{
mParticleSystemMask = mask ;
}
2016-02-05 17:51:51 +00:00
void SceneManager : : setFilterSettings ( const std : : string & magfilter , const std : : string & minfilter ,
2016-02-14 22:14:52 +00:00
const std : : string & mipmap , int maxAnisotropy )
2016-02-05 17:51:51 +00:00
{
osg : : Texture : : FilterMode min = osg : : Texture : : LINEAR ;
osg : : Texture : : FilterMode mag = osg : : Texture : : LINEAR ;
if ( magfilter = = " nearest " )
mag = osg : : Texture : : NEAREST ;
else if ( magfilter ! = " linear " )
2018-08-14 15:42:41 +00:00
Log ( Debug : : Warning ) < < " Warning: Invalid texture mag filter: " < < magfilter ;
2016-02-05 17:51:51 +00:00
if ( minfilter = = " nearest " )
min = osg : : Texture : : NEAREST ;
else if ( minfilter ! = " linear " )
2018-08-14 15:42:41 +00:00
Log ( Debug : : Warning ) < < " Warning: Invalid texture min filter: " < < minfilter ;
2016-02-05 17:51:51 +00:00
if ( mipmap = = " nearest " )
{
if ( min = = osg : : Texture : : NEAREST )
min = osg : : Texture : : NEAREST_MIPMAP_NEAREST ;
else if ( min = = osg : : Texture : : LINEAR )
min = osg : : Texture : : LINEAR_MIPMAP_NEAREST ;
}
else if ( mipmap ! = " none " )
{
if ( mipmap ! = " linear " )
2018-08-14 15:42:41 +00:00
Log ( Debug : : Warning ) < < " Warning: Invalid texture mipmap: " < < mipmap ;
2016-02-05 17:51:51 +00:00
if ( min = = osg : : Texture : : NEAREST )
min = osg : : Texture : : NEAREST_MIPMAP_LINEAR ;
else if ( min = = osg : : Texture : : LINEAR )
min = osg : : Texture : : LINEAR_MIPMAP_LINEAR ;
}
2016-02-05 18:28:52 +00:00
mMinFilter = min ;
mMagFilter = mag ;
mMaxAnisotropy = std : : max ( 1 , maxAnisotropy ) ;
SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor ( mMinFilter , mMagFilter , mMaxAnisotropy ) ;
SetFilterSettingsVisitor setFilterSettingsVisitor ( mMinFilter , mMagFilter , mMaxAnisotropy ) ;
2016-02-14 22:14:52 +00:00
mCache - > accept ( setFilterSettingsVisitor ) ;
mCache - > accept ( setFilterSettingsControllerVisitor ) ;
2016-02-05 17:51:51 +00:00
}
void SceneManager : : applyFilterSettings ( osg : : Texture * tex )
{
tex - > setFilter ( osg : : Texture : : MIN_FILTER , mMinFilter ) ;
tex - > setFilter ( osg : : Texture : : MAG_FILTER , mMagFilter ) ;
tex - > setMaxAnisotropy ( mMaxAnisotropy ) ;
}
void SceneManager : : setUnRefImageDataAfterApply ( bool unref )
{
mUnRefImageDataAfterApply = unref ;
}
2016-02-09 17:33:02 +00:00
void SceneManager : : updateCache ( double referenceTime )
{
ResourceManager : : updateCache ( referenceTime ) ;
mInstanceCache - > removeUnreferencedObjectsInCache ( ) ;
2017-03-06 15:07:03 +00:00
mSharedStateMutex . lock ( ) ;
mSharedStateManager - > prune ( ) ;
mSharedStateMutex . unlock ( ) ;
2019-06-13 13:37:00 +00:00
if ( mIncrementalCompileOperation )
{
2020-06-25 19:46:07 +00:00
std : : lock_guard < OpenThreads : : Mutex > lock ( * mIncrementalCompileOperation - > getToCompiledMutex ( ) ) ;
2020-05-03 13:37:00 +00:00
osgUtil : : IncrementalCompileOperation : : CompileSets & sets = mIncrementalCompileOperation - > getToCompile ( ) ;
for ( osgUtil : : IncrementalCompileOperation : : CompileSets : : iterator it = sets . begin ( ) ; it ! = sets . end ( ) ; )
{
2020-05-04 13:37:00 +00:00
int refcount = ( * it ) - > _subgraphToCompile - > referenceCount ( ) ;
if ( ( * it ) - > _subgraphToCompile - > asDrawable ( ) ) refcount - = 1 ; // ref by CompileList.
if ( refcount < = 2 ) // ref by ObjectCache + ref by _subgraphToCompile.
{
// no other ref = not needed anymore.
2020-05-03 13:37:00 +00:00
it = sets . erase ( it ) ;
2020-05-04 13:37:00 +00:00
}
2020-05-03 13:37:00 +00:00
else
+ + it ;
}
2019-06-13 13:37:00 +00:00
}
2016-02-09 17:33:02 +00:00
}
2017-08-20 23:55:16 +00:00
void SceneManager : : clearCache ( )
{
ResourceManager : : clearCache ( ) ;
2020-06-25 19:46:07 +00:00
std : : lock_guard < std : : mutex > lock ( mSharedStateMutex ) ;
2017-08-20 23:55:16 +00:00
mSharedStateManager - > clearCache ( ) ;
2017-08-21 22:58:38 +00:00
mInstanceCache - > clear ( ) ;
2017-08-20 23:55:16 +00:00
}
2017-03-07 03:02:06 +00:00
void SceneManager : : reportStats ( unsigned int frameNumber , osg : : Stats * stats ) const
2017-02-22 01:18:18 +00:00
{
2019-02-20 13:37:00 +00:00
if ( mIncrementalCompileOperation )
2017-02-22 01:18:18 +00:00
{
2020-06-25 19:46:07 +00:00
std : : lock_guard < OpenThreads : : Mutex > lock ( * mIncrementalCompileOperation - > getToCompiledMutex ( ) ) ;
2017-02-22 01:18:18 +00:00
stats - > setAttribute ( frameNumber , " Compiling " , mIncrementalCompileOperation - > getToCompile ( ) . size ( ) ) ;
}
2017-02-23 13:49:12 +00:00
{
2020-06-25 19:46:07 +00:00
std : : lock_guard < std : : mutex > lock ( mSharedStateMutex ) ;
2017-02-23 13:49:12 +00:00
stats - > setAttribute ( frameNumber , " Texture " , mSharedStateManager - > getNumSharedTextures ( ) ) ;
stats - > setAttribute ( frameNumber , " StateSet " , mSharedStateManager - > getNumSharedStateSets ( ) ) ;
}
2017-02-22 01:18:18 +00:00
stats - > setAttribute ( frameNumber , " Node " , mCache - > getCacheSize ( ) ) ;
stats - > setAttribute ( frameNumber , " Node Instance " , mInstanceCache - > getCacheSize ( ) ) ;
}
2021-01-20 23:37:19 +00:00
Shader : : ShaderVisitor * SceneManager : : createShaderVisitor ( const std : : string & shaderPrefix , bool translucentFramebuffer )
2017-10-15 15:24:23 +00:00
{
2020-12-16 21:46:09 +00:00
Shader : : ShaderVisitor * shaderVisitor = new Shader : : ShaderVisitor ( * mShaderManager . get ( ) , * mImageManager , shaderPrefix ) ;
2017-10-15 15:24:23 +00:00
shaderVisitor - > setForceShaders ( mForceShaders ) ;
shaderVisitor - > setAutoUseNormalMaps ( mAutoUseNormalMaps ) ;
shaderVisitor - > setNormalMapPattern ( mNormalMapPattern ) ;
shaderVisitor - > setNormalHeightMapPattern ( mNormalHeightMapPattern ) ;
shaderVisitor - > setAutoUseSpecularMaps ( mAutoUseSpecularMaps ) ;
shaderVisitor - > setSpecularMapPattern ( mSpecularMapPattern ) ;
2020-11-09 10:53:58 +00:00
shaderVisitor - > setApplyLightingToEnvMaps ( mApplyLightingToEnvMaps ) ;
2020-12-26 22:45:53 +00:00
shaderVisitor - > setConvertAlphaTestToAlphaToCoverage ( mConvertAlphaTestToAlphaToCoverage ) ;
2021-01-20 23:37:19 +00:00
shaderVisitor - > setTranslucentFramebuffer ( translucentFramebuffer ) ;
2017-10-15 15:24:23 +00:00
return shaderVisitor ;
}
2020-11-28 13:03:10 +00:00
std : : string getFileExtension ( const std : : string & file )
{
size_t extPos = file . find_last_of ( ' . ' ) ;
if ( extPos ! = std : : string : : npos & & extPos + 1 < file . size ( ) )
return file . substr ( extPos + 1 ) ;
return std : : string ( ) ;
}
2015-03-26 17:02:51 +00:00
}