#include "scenemanager.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "imagemanager.hpp" #include "niffilemanager.hpp" #include "objectcache.hpp" #include "multiobjectcache.hpp" namespace { /// @todo Do this in updateCallback so that animations are accounted for. class InitWorldSpaceParticlesVisitor : public osg::NodeVisitor { public: /// @param mask The node mask to set on ParticleSystem nodes. InitWorldSpaceParticlesVisitor(unsigned int mask) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mMask(mask) { } bool isWorldSpaceParticleSystem(osgParticle::ParticleSystem* partsys) { // HACK: ParticleSystem has no getReferenceFrame() return (partsys->getUserDataContainer() && partsys->getUserDataContainer()->getNumDescriptions() > 0 && partsys->getUserDataContainer()->getDescriptions()[0] == "worldspace"); } void apply(osg::Drawable& drw) { if (osgParticle::ParticleSystem* partsys = dynamic_cast(&drw)) { if (isWorldSpaceParticleSystem(partsys)) { // 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)); } partsys->setNodeMask(mMask); } } 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; inumParticles(); ++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); } private: unsigned int mMask; }; } 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); } } } 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) { } virtual void apply(osg::Node& node) { osg::StateSet* stateset = node.getStateSet(); if (stateset) applyStateSet(stateset); traverse(node); } void applyStateSet(osg::StateSet* stateset) { const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList(); for(unsigned int unit=0;unitgetTextureAttribute(unit, osg::StateAttribute::TEXTURE); if (texture) applyStateAttribute(texture); } } void applyStateAttribute(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); } } private: osg::Texture::FilterMode mMinFilter; osg::Texture::FilterMode mMagFilter; int mMaxAnisotropy; }; SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) : ResourceManager(vfs) , mShaderManager(new Shader::ShaderManager) , mForceShaders(false) , mClampLighting(true) , mForcePerPixelLighting(false) , mAutoUseNormalMaps(false) , mAutoUseSpecularMaps(false) , mInstanceCache(new MultiObjectCache) , mImageManager(imageManager) , mNifFileManager(nifFileManager) , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR) , mMagFilter(osg::Texture::LINEAR) , mMaxAnisotropy(1) , mUnRefImageDataAfterApply(false) , mParticleSystemMask(~0u) { } void SceneManager::setForceShaders(bool force) { mForceShaders = force; } bool SceneManager::getForceShaders() const { return mForceShaders; } void SceneManager::recreateShaders(osg::ref_ptr node) { Shader::ShaderVisitor shaderVisitor(*mShaderManager.get(), *mImageManager, "objects_vertex.glsl", "objects_fragment.glsl"); shaderVisitor.setForceShaders(mForceShaders); shaderVisitor.setClampLighting(mClampLighting); shaderVisitor.setForcePerPixelLighting(mForcePerPixelLighting); shaderVisitor.setAllowedToModifyStateSets(false); node->accept(shaderVisitor); } void SceneManager::setClampLighting(bool clamp) { mClampLighting = clamp; } bool SceneManager::getClampLighting() const { return mClampLighting; } void SceneManager::setForcePerPixelLighting(bool force) { mForcePerPixelLighting = force; } bool SceneManager::getForcePerPixelLighting() const { return mForcePerPixelLighting; } void SceneManager::setAutoUseNormalMaps(bool use) { mAutoUseNormalMaps = use; } void SceneManager::setNormalMapPattern(const std::string &pattern) { mNormalMapPattern = pattern; } void SceneManager::setNormalHeightMapPattern(const std::string &pattern) { mNormalHeightMapPattern = pattern; } void SceneManager::setAutoUseSpecularMaps(bool use) { mAutoUseSpecularMaps = use; } void SceneManager::setSpecularMapPattern(const std::string &pattern) { mSpecularMapPattern = pattern; } SceneManager::~SceneManager() { // this has to be defined in the .cpp file as we can't delete incomplete types } Shader::ShaderManager &SceneManager::getShaderManager() { return *mShaderManager.get(); } void SceneManager::setShaderPath(const std::string &path) { mShaderManager->setShaderPath(path); } /// @brief Callback to read image files from the VFS. class ImageReadCallback : public osgDB::ReadFileCallback { public: ImageReadCallback(Resource::ImageManager* imageMgr) : mImageManager(imageMgr) { } virtual osgDB::ReaderWriter::ReadResult readImage(const std::string& filename, const osgDB::Options* options) { try { return osgDB::ReaderWriter::ReadResult(mImageManager->getImage(filename), osgDB::ReaderWriter::ReadResult::FILE_LOADED); } catch (std::exception& e) { return osgDB::ReaderWriter::ReadResult(e.what()); } } private: Resource::ImageManager* mImageManager; }; 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(); } osg::ref_ptr load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) { std::string ext = getFileExtension(normalizedFilename); if (ext == "nif") return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), imageManager); 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 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. options->setReadFileCallback(new ImageReadCallback(imageManager)); 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()); } return result.getNode(); } } osg::ref_ptr SceneManager::getTemplate(const std::string &name) { std::string normalized = name; mVFS->normalizeFilename(normalized); osg::ref_ptr obj = mCache->getRefFromObjectCache(normalized); if (obj) return osg::ref_ptr(static_cast(obj.get())); else { osg::ref_ptr loaded; try { Files::IStreamPtr file = mVFS->get(normalized); loaded = load(file, normalized, mImageManager, mNifFileManager); } catch (std::exception& e) { static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2" }; for (unsigned int i=0; iexists(normalized)) { std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error." << sMeshTypes[i] << " instead" << std::endl; Files::IStreamPtr file = mVFS->get(normalized); loaded = load(file, normalized, mImageManager, mNifFileManager); break; } } if (!loaded) throw; } // set filtering settings SetFilterSettingsVisitor setFilterSettingsVisitor(mMinFilter, mMagFilter, mMaxAnisotropy); loaded->accept(setFilterSettingsVisitor); SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor(mMinFilter, mMagFilter, mMaxAnisotropy); loaded->accept(setFilterSettingsControllerVisitor); Shader::ShaderVisitor shaderVisitor(*mShaderManager.get(), *mImageManager, "objects_vertex.glsl", "objects_fragment.glsl"); shaderVisitor.setForceShaders(mForceShaders); shaderVisitor.setClampLighting(mClampLighting); shaderVisitor.setForcePerPixelLighting(mForcePerPixelLighting); shaderVisitor.setAutoUseNormalMaps(mAutoUseNormalMaps); shaderVisitor.setNormalMapPattern(mNormalMapPattern); shaderVisitor.setNormalHeightMapPattern(mNormalHeightMapPattern); shaderVisitor.setAutoUseSpecularMaps(mAutoUseSpecularMaps); shaderVisitor.setSpecularMapPattern(mSpecularMapPattern); loaded->accept(shaderVisitor); // share state mSharedStateMutex.lock(); osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get()); mSharedStateMutex.unlock(); if (mIncrementalCompileOperation) mIncrementalCompileOperation->add(loaded); mCache->addEntryToObjectCache(normalized, loaded); return loaded; } } osg::ref_ptr SceneManager::cacheInstance(const std::string &name) { std::string normalized = name; mVFS->normalizeFilename(normalized); osg::ref_ptr node = createInstance(normalized); mInstanceCache->addEntryToObjectCache(normalized, node.get()); return node; } osg::ref_ptr SceneManager::createInstance(const std::string& name) { osg::ref_ptr scene = getTemplate(name); osg::ref_ptr cloned = osg::clone(scene.get(), SceneUtil::CopyOp()); // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache cloned->getOrCreateUserDataContainer()->addUserObject(const_cast(scene.get())); return cloned; } osg::ref_ptr SceneManager::getInstance(const std::string &name) { std::string normalized = name; mVFS->normalizeFilename(normalized); osg::ref_ptr obj = mInstanceCache->takeFromObjectCache(normalized); if (obj.get()) return static_cast(obj.get()); return createInstance(normalized); } osg::ref_ptr SceneManager::getInstance(const std::string &name, osg::Group* parentNode) { osg::ref_ptr cloned = getInstance(name); attachTo(cloned, parentNode); return cloned; } void SceneManager::attachTo(osg::Node *instance, osg::Group *parentNode) const { parentNode->addChild(instance); notifyAttached(instance); } void SceneManager::releaseGLObjects(osg::State *state) { mCache->releaseGLObjects(state); mInstanceCache->releaseGLObjects(state); } void SceneManager::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation *ico) { mIncrementalCompileOperation = ico; } void SceneManager::notifyAttached(osg::Node *node) const { InitWorldSpaceParticlesVisitor visitor (mParticleSystemMask); node->accept(visitor); } Resource::ImageManager* SceneManager::getImageManager() { return mImageManager; } void SceneManager::setParticleSystemMask(unsigned int mask) { mParticleSystemMask = mask; } void SceneManager::setFilterSettings(const std::string &magfilter, const std::string &minfilter, const std::string &mipmap, int maxAnisotropy) { 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: "<accept(setFilterSettingsVisitor); mCache->accept(setFilterSettingsControllerVisitor); } 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; } void SceneManager::updateCache(double referenceTime) { ResourceManager::updateCache(referenceTime); mInstanceCache->removeUnreferencedObjectsInCache(); } }