diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index f347bc3506..d96bf16e4e 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include @@ -448,8 +448,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true); mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get())); - mResourceSystem->getTextureManager()->setUnRefImageDataAfterApply(true); - mResourceSystem->getTextureManager()->setFilterSettings( + mResourceSystem->getSceneManager()->setUnRefImageDataAfterApply(true); + mResourceSystem->getSceneManager()->setFilterSettings( Settings::Manager::getString("texture mag filter", "General"), Settings::Manager::getString("texture min filter", "General"), Settings::Manager::getString("texture mipmap", "General"), diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 549d0eb8e6..a7bb3f1281 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1059,7 +1059,9 @@ namespace MWRender stream << i; stream << ".dds"; - textures.push_back(mResourceSystem->getTextureManager()->getTexture2D(stream.str(), osg::Texture2D::REPEAT, osg::Texture2D::REPEAT)); + osg::ref_ptr tex = mResourceSystem->getTextureManager()->getTexture2D(stream.str(), osg::Texture2D::REPEAT, osg::Texture2D::REPEAT); + mResourceSystem->getSceneManager()->applyFilterSettings(tex); + textures.push_back(tex); } osg::ref_ptr glowupdater (new GlowUpdater(glowColor, textures)); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c8d0925b0b..755a8019b1 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -776,7 +776,7 @@ namespace MWRender void RenderingManager::updateTextureFiltering() { - mResourceSystem->getTextureManager()->setFilterSettings( + mResourceSystem->getSceneManager()->setFilterSettings( Settings::Manager::getString("texture mag filter", "General"), Settings::Manager::getString("texture min filter", "General"), Settings::Manager::getString("texture mipmap", "General"), diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index e1e969d066..0b633a1224 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -305,6 +305,8 @@ namespace NifOsg META_Object(NifOsg, FlipController) + std::vector >& getTextures() { return mTextures; } + virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv); }; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 43ece41f35..80e6f737df 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -9,6 +9,8 @@ #include +#include + #include #include @@ -19,6 +21,7 @@ #include #include +#include #include "texturemanager.hpp" #include "niffilemanager.hpp" @@ -105,10 +108,123 @@ namespace namespace Resource { + /// 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) + { + } + + virtual void visit(osg::Node& node, SceneUtil::Controller& ctrl) + { + if (NifOsg::FlipController* flipctrl = dynamic_cast(&ctrl)) + { + for (std::vector >::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); + mTexturesProcessed.insert(tex); + } + } + } + + const std::set >& getTexturesProcessed() + { + return mTexturesProcessed; + } + + private: + osg::Texture::FilterMode mMinFilter; + osg::Texture::FilterMode mMagFilter; + int mMaxAnisotropy; + std::set > mTexturesProcessed; + }; + + /// 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) + { + } + + virtual void apply(osg::Node& node) + { + osg::StateSet* stateset = node.getStateSet(); + if (stateset) + apply(stateset); + traverse(node); + } + + virtual void apply(osg::Geode& geode) + { + osg::StateSet* stateset = geode.getStateSet(); + if (stateset) + apply(stateset); + + for (unsigned int i=0; igetStateSet(); + if (stateset) + apply(stateset); + } + } + + void apply(osg::StateSet* stateset) + { + const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList(); + for(unsigned int unit=0;unitgetTextureAttribute(unit, osg::StateAttribute::TEXTURE); + apply(texture); + } + } + + void apply(osg::StateAttribute* attr) + { + osg::Texture* tex = attr->asTexture(); + if (tex) + { + tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); + tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); + tex->setMaxAnisotropy(mMaxAnisotropy); + mTexturesProcessed.insert(tex); + } + } + + const std::set >& getTexturesProcessed() + { + return mTexturesProcessed; + } + + private: + osg::Texture::FilterMode mMinFilter; + osg::Texture::FilterMode mMagFilter; + int mMaxAnisotropy; + std::set > mTexturesProcessed; + }; + + + SceneManager::SceneManager(const VFS::Manager *vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager) : mVFS(vfs) , mTextureManager(textureManager) , mNifFileManager(nifFileManager) + , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR) + , mMagFilter(osg::Texture::LINEAR) + , mMaxAnisotropy(1) + , mUnRefImageDataAfterApply(false) , mParticleSystemMask(~0u) { } @@ -219,6 +335,17 @@ namespace Resource throw; } + // set filtering settings + SetFilterSettingsVisitor setFilterSettingsVisitor(mMinFilter, mMagFilter, mMaxAnisotropy); + loaded->accept(setFilterSettingsVisitor); + SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor(mMinFilter, mMagFilter, mMaxAnisotropy); + loaded->accept(setFilterSettingsControllerVisitor); + + // remember which textures we set a filtering setting on so we can re-apply it when the setting changes + mTexturesWithFilterSetting.insert(setFilterSettingsVisitor.getTexturesProcessed().begin(), setFilterSettingsVisitor.getTexturesProcessed().end()); + mTexturesWithFilterSetting.insert(setFilterSettingsControllerVisitor.getTexturesProcessed().begin(), setFilterSettingsControllerVisitor.getTexturesProcessed().end()); + + // share state osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get()); if (mIncrementalCompileOperation) @@ -285,4 +412,84 @@ namespace Resource mParticleSystemMask = mask; } + void SceneManager::setFilterSettings(const std::string &magfilter, const std::string &minfilter, + const std::string &mipmap, int maxAnisotropy, + osgViewer::Viewer *viewer) + { + 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") + std::cerr<< "Invalid texture mag filter: "<stopThreading(); + setFilterSettings(min, mag, maxAnisotropy); + if(viewer) viewer->startThreading(); + } + + void SceneManager::applyFilterSettings(osg::Texture *tex) + { + tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); + tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); + tex->setMaxAnisotropy(mMaxAnisotropy); + mTexturesWithFilterSetting.insert(tex); + } + + void SceneManager::setUnRefImageDataAfterApply(bool unref) + { + mUnRefImageDataAfterApply = unref; + } + + void SceneManager::setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy) + { + mMinFilter = minFilter; + mMagFilter = magFilter; + mMaxAnisotropy = std::max(1, maxAnisotropy); + + for (std::set >::const_iterator it = mTexturesWithFilterSetting.begin(); it != mTexturesWithFilterSetting.end(); ++it) + { + (*it)->setFilter(osg::Texture::MIN_FILTER, mMinFilter); + (*it)->setFilter(osg::Texture::MAG_FILTER, mMagFilter); + (*it)->setMaxAnisotropy(mMaxAnisotropy); + } + } + + void SceneManager::clearCache() + { + // clear textures that are no longer referenced + for (std::set >::const_iterator it = mTexturesWithFilterSetting.begin(); it != mTexturesWithFilterSetting.end();) + { + osg::Texture* tex = *it; + if (tex->referenceCount() <= 1) + mTexturesWithFilterSetting.erase(it++); + else + ++it; + } + + } + } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 67fa2ab433..df330af5b5 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -6,6 +6,7 @@ #include #include +#include namespace Resource { @@ -23,6 +24,11 @@ namespace osgUtil class IncrementalCompileOperation; } +namespace osgViewer +{ + class Viewer; +} + namespace Resource { @@ -69,11 +75,30 @@ namespace Resource /// @param mask The node mask to apply to loaded particle system nodes. void setParticleSystemMask(unsigned int mask); + void setFilterSettings(const std::string &magfilter, const std::string &minfilter, + const std::string &mipmap, int maxAnisotropy, + osgViewer::Viewer *viewer); + + /// Apply filter settings to the given texture. Note, when loading an object through this scene manager (i.e. calling getTemplate / createInstance) + /// the filter settings are applied automatically. This method is provided for textures that were created outside of the SceneManager. + void applyFilterSettings (osg::Texture* tex); + + /// Keep a copy of the texture data around in system memory? This is needed when using multiple graphics contexts, + /// otherwise should be disabled to reduce memory usage. + void setUnRefImageDataAfterApply(bool unref); + + void clearCache(); + private: const VFS::Manager* mVFS; Resource::TextureManager* mTextureManager; Resource::NifFileManager* mNifFileManager; + osg::Texture::FilterMode mMinFilter; + osg::Texture::FilterMode mMagFilter; + int mMaxAnisotropy; + bool mUnRefImageDataAfterApply; + osg::ref_ptr mIncrementalCompileOperation; unsigned int mParticleSystemMask; @@ -82,8 +107,14 @@ namespace Resource typedef std::map > Index; Index mIndex; + std::set > mTexturesWithFilterSetting; + SceneManager(const SceneManager&); void operator = (const SceneManager&); + + /// @warning It is unsafe to call this function when a draw thread is using the textures. Call stopThreading() first! + void setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode maxFilter, int maxAnisotropy); + }; } diff --git a/components/resource/texturemanager.cpp b/components/resource/texturemanager.cpp index d7f3fc61a1..b49c6bb094 100644 --- a/components/resource/texturemanager.cpp +++ b/components/resource/texturemanager.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include @@ -47,11 +46,7 @@ namespace Resource TextureManager::TextureManager(const VFS::Manager *vfs) : mVFS(vfs) - , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR) - , mMagFilter(osg::Texture::LINEAR) - , mMaxAnisotropy(1) , mWarningTexture(createWarningTexture()) - , mUnRefImageDataAfterApply(false) { } @@ -61,88 +56,6 @@ namespace Resource } - void TextureManager::setUnRefImageDataAfterApply(bool unref) - { - mUnRefImageDataAfterApply = unref; - } - - void TextureManager::setFilterSettings(const std::string &magfilter, const std::string &minfilter, - const std::string &mipmap, int maxAnisotropy, - osgViewer::Viewer *viewer) - { - 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") - std::cerr<< "Invalid texture mag filter: "<stopThreading(); - setFilterSettings(min, mag, maxAnisotropy); - if(viewer) viewer->startThreading(); - } - - void TextureManager::setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy) - { - mMinFilter = minFilter; - mMagFilter = magFilter; - mMaxAnisotropy = std::max(1, maxAnisotropy); - - for (std::map >::iterator it = mTextures.begin(); it != mTextures.end(); ++it) - { - osg::ref_ptr tex = it->second; - - // Keep mip-mapping disabled if the texture creator explicitely requested no mipmapping. - osg::Texture::FilterMode oldMin = tex->getFilter(osg::Texture::MIN_FILTER); - if (oldMin == osg::Texture::LINEAR || oldMin == osg::Texture::NEAREST) - { - osg::Texture::FilterMode newMin = osg::Texture::LINEAR; - switch (mMinFilter) - { - case osg::Texture::LINEAR: - case osg::Texture::LINEAR_MIPMAP_LINEAR: - case osg::Texture::LINEAR_MIPMAP_NEAREST: - newMin = osg::Texture::LINEAR; - break; - case osg::Texture::NEAREST: - case osg::Texture::NEAREST_MIPMAP_LINEAR: - case osg::Texture::NEAREST_MIPMAP_NEAREST: - newMin = osg::Texture::NEAREST; - break; - } - tex->setFilter(osg::Texture::MIN_FILTER, newMin); - } - else - tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); - - tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); - tex->setMaxAnisotropy(static_cast(mMaxAnisotropy)); - } - } - /* osg::ref_ptr TextureManager::getImage(const std::string &filename) { @@ -295,11 +208,6 @@ namespace Resource texture->setImage(image); texture->setWrap(osg::Texture::WRAP_S, wrapS); texture->setWrap(osg::Texture::WRAP_T, wrapT); - texture->setFilter(osg::Texture::MIN_FILTER, mMinFilter); - texture->setFilter(osg::Texture::MAG_FILTER, mMagFilter); - texture->setMaxAnisotropy(mMaxAnisotropy); - - texture->setUnRefImageDataAfterApply(mUnRefImageDataAfterApply); mTextures.insert(std::make_pair(key, texture)); return texture; diff --git a/components/resource/texturemanager.hpp b/components/resource/texturemanager.hpp index e12dfa0900..61a727b5ee 100644 --- a/components/resource/texturemanager.hpp +++ b/components/resource/texturemanager.hpp @@ -28,14 +28,6 @@ namespace Resource TextureManager(const VFS::Manager* vfs); ~TextureManager(); - void setFilterSettings(const std::string &magfilter, const std::string &minfilter, - const std::string &mipmap, int maxAnisotropy, - osgViewer::Viewer *view); - - /// Keep a copy of the texture data around in system memory? This is needed when using multiple graphics contexts, - /// otherwise should be disabled to reduce memory usage. - void setUnRefImageDataAfterApply(bool unref); - /// Create or retrieve a Texture2D using the specified image filename, and wrap parameters. /// Returns the dummy texture if the given texture is not found. osg::ref_ptr getTexture2D(const std::string& filename, osg::Texture::WrapMode wrapS, osg::Texture::WrapMode wrapT); @@ -50,10 +42,6 @@ namespace Resource private: const VFS::Manager* mVFS; - osg::Texture::FilterMode mMinFilter; - osg::Texture::FilterMode mMagFilter; - int mMaxAnisotropy; - typedef std::pair, std::string> MapKey; std::map > mImages; @@ -62,11 +50,6 @@ namespace Resource osg::ref_ptr mWarningTexture; - bool mUnRefImageDataAfterApply; - - /// @warning It is unsafe to call this function when a draw thread is using the textures. Call stopThreading() first! - void setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode maxFilter, int maxAnisotropy); - TextureManager(const TextureManager&); void operator = (const TextureManager&); };