From 99e1720980cac3d692ea7ccaf2a0df198c529a24 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Mar 2015 18:02:51 +0100 Subject: [PATCH] Add SceneManager and clone utility --- apps/opencs/model/world/data.cpp | 7 ++- apps/opencs/model/world/data.hpp | 6 ++ apps/opencs/view/render/object.cpp | 15 +++-- apps/opencs/view/render/object.hpp | 8 +-- components/CMakeLists.txt | 8 +++ components/resource/resourcesystem.cpp | 24 ++++++++ components/resource/resourcesystem.hpp | 34 +++++++++++ components/resource/scenemanager.cpp | 49 +++++++++++++++ components/resource/scenemanager.hpp | 40 ++++++++++++ components/sceneutil/clone.cpp | 85 ++++++++++++++++++++++++++ components/sceneutil/clone.hpp | 45 ++++++++++++++ components/vfs/manager.cpp | 14 ++++- components/vfs/manager.hpp | 7 +++ 13 files changed, 327 insertions(+), 15 deletions(-) create mode 100644 components/resource/resourcesystem.cpp create mode 100644 components/resource/resourcesystem.hpp create mode 100644 components/resource/scenemanager.cpp create mode 100644 components/resource/scenemanager.hpp create mode 100644 components/sceneutil/clone.cpp create mode 100644 components/sceneutil/clone.hpp diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 07b18cc23..77724c997 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -60,7 +60,7 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager) : mEncoder (encoding), mPathgrids (mCells), mRefs (mCells), - mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0) + mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0), mResourceSystem(resourcesManager.getVFS()) { mGlobals.addColumn (new StringIdColumn); mGlobals.addColumn (new RecordStateColumn); @@ -354,6 +354,11 @@ CSMWorld::Data::~Data() delete mReader; } +Resource::ResourceSystem* CSMWorld::Data::getResourceSystem() +{ + return &mResourceSystem; +} + const CSMWorld::IdCollection& CSMWorld::Data::getGlobals() const { return mGlobals; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index bb4c9a4a8..ce8776104 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -28,6 +28,8 @@ #include #include +#include + #include #include "../doc/stage.hpp" @@ -105,6 +107,8 @@ namespace CSMWorld std::map > mRefLoadCache; int mReaderIndex; + Resource::ResourceSystem mResourceSystem; + std::vector > mReaders; // not implemented @@ -128,6 +132,8 @@ namespace CSMWorld const VFS::Manager* getVFS() const; + Resource::ResourceSystem* getResourceSystem(); + const IdCollection& getGlobals() const; IdCollection& getGlobals(); diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index f8e91e463..afed837f8 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -9,10 +9,12 @@ #include "../../model/world/ref.hpp" #include "../../model/world/refidcollection.hpp" -#include +#include +#include #include "elements.hpp" + void CSVRender::Object::clear() { } @@ -56,15 +58,12 @@ void CSVRender::Object::update() { try { - NifOsg::Loader loader; - loader.resourceManager = mVFS; - std::string path = "meshes\\" + model; - Nif::NIFFilePtr file(new Nif::NIFFile(mVFS->get(path), path)); + osg::ref_ptr loaded = mResourceSystem->getSceneManager()->getInstance(path); mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); - mBaseNode->addChild(loader.load(file)); + mBaseNode->addChild(loaded); } catch (std::exception& e) { @@ -101,9 +100,9 @@ const CSMWorld::CellRef& CSVRender::Object::getReference() const return mData.getReferences().getRecord (mReferenceId).get(); } -CSVRender::Object::Object (const CSMWorld::Data& data, osg::Group* parentNode, +CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode, const std::string& id, bool referenceable, bool forceBaseToZero) -: mVFS(data.getVFS()), mData (data), mBaseNode(0), mParentNode(parentNode), mForceBaseToZero (forceBaseToZero) +: mResourceSystem(data.getResourceSystem()), mData (data), mBaseNode(0), mParentNode(parentNode), mForceBaseToZero (forceBaseToZero) { mBaseNode = new osg::PositionAttitudeTransform; parentNode->addChild(mBaseNode); diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index 9efbcf5dc..23a652792 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -14,9 +14,9 @@ namespace osg class Group; } -namespace VFS +namespace Resource { - class Manager; + class ResourceSystem; } namespace CSMWorld @@ -39,7 +39,7 @@ namespace CSVRender std::string mReferenceableId; osg::ref_ptr mBaseNode; osg::Group* mParentNode; - const VFS::Manager* mVFS; + Resource::ResourceSystem* mResourceSystem; bool mForceBaseToZero; /// Not implemented @@ -62,7 +62,7 @@ namespace CSVRender public: - Object (const CSMWorld::Data& data, osg::Group *cellNode, + Object (CSMWorld::Data& data, osg::Group *cellNode, const std::string& id, bool referenceable, bool forceBaseToZero = false); /// \param forceBaseToZero If this is a reference ignore the coordinates and place diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 48de580bc..70c9daa8b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -38,6 +38,14 @@ add_component_dir (vfs manager archive bsaarchive filesystemarchive registerarchives ) +add_component_dir (resource + scenemanager resourcesystem + ) + +add_component_dir (sceneutil + clone + ) + add_component_dir (nif controlled effect niftypes record controller extra node record_ptr data niffile property nifkey data node base nifstream ) diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp new file mode 100644 index 000000000..e87ef6f10 --- /dev/null +++ b/components/resource/resourcesystem.cpp @@ -0,0 +1,24 @@ +#include "resourcesystem.hpp" + +#include "scenemanager.hpp" + +namespace Resource +{ + + ResourceSystem::ResourceSystem(const VFS::Manager *vfs) + : mVFS(vfs) + { + mSceneManager.reset(new SceneManager(vfs)); + } + + SceneManager* ResourceSystem::getSceneManager() + { + return mSceneManager.get(); + } + + const VFS::Manager* ResourceSystem::getVFS() const + { + return mVFS; + } + +} diff --git a/components/resource/resourcesystem.hpp b/components/resource/resourcesystem.hpp new file mode 100644 index 000000000..b696a2376 --- /dev/null +++ b/components/resource/resourcesystem.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H +#define OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H + +#include + +namespace VFS +{ + class Manager; +} + +namespace Resource +{ + + class SceneManager; + + /// @brief Wrapper class that constructs and provides access to the various resource subsystems. + class ResourceSystem + { + public: + ResourceSystem(const VFS::Manager* vfs); + + SceneManager* getSceneManager(); + + const VFS::Manager* getVFS() const; + + private: + std::auto_ptr mSceneManager; + + const VFS::Manager* mVFS; + }; + +} + +#endif diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp new file mode 100644 index 000000000..b6a09f3a7 --- /dev/null +++ b/components/resource/scenemanager.cpp @@ -0,0 +1,49 @@ +#include "scenemanager.hpp" + +#include + +#include +#include + +#include + +namespace Resource +{ + + SceneManager::SceneManager(const VFS::Manager *vfs) + : mVFS(vfs) + { + } + + osg::ref_ptr SceneManager::getTemplate(const std::string &name) + { + std::string normalized = name; + mVFS->normalizeFilename(normalized); + + Index::iterator it = mIndex.find(normalized); + if (it == mIndex.end()) + { + Files::IStreamPtr file = mVFS->get(normalized); + + // TODO: add support for non-NIF formats + + NifOsg::Loader loader; + loader.resourceManager = mVFS; + osg::ref_ptr loaded = loader.load(Nif::NIFFilePtr(new Nif::NIFFile(file, normalized))); + + // TODO: provide way for the user to get textKeys (attach to the node?) + + mIndex[normalized] = loaded; + return loaded; + } + else + return it->second; + } + + osg::ref_ptr SceneManager::getInstance(const std::string &name) + { + osg::ref_ptr scene = getTemplate(name); + return osg::clone(scene.get(), SceneUtil::CopyOp()); + } + +} diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp new file mode 100644 index 000000000..7922f3a49 --- /dev/null +++ b/components/resource/scenemanager.hpp @@ -0,0 +1,40 @@ +#ifndef OPENMW_COMPONENTS_RESOURCE_SCENEMANAGER_H +#define OPENMW_COMPONENTS_RESOURCE_SCENEMANAGER_H + +#include +#include + +#include +#include + +namespace VFS +{ + class Manager; +} + +namespace Resource +{ + + /// @brief Handles loading and caching of scenes, e.g. NIF files + class SceneManager + { + public: + SceneManager(const VFS::Manager* vfs); + + /// Get a read-only copy of this scene "template" + osg::ref_ptr getTemplate(const std::string& name); + + /// Create an instance of the given scene template + osg::ref_ptr getInstance(const std::string& name); + + private: + const VFS::Manager* mVFS; + + // observer_ptr? + typedef std::map > Index; + Index mIndex; + }; + +} + +#endif diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp new file mode 100644 index 000000000..f124d7de7 --- /dev/null +++ b/components/sceneutil/clone.cpp @@ -0,0 +1,85 @@ +#include "clone.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +namespace SceneUtil +{ + + CopyOp::CopyOp() + { + setCopyFlags(osg::CopyOp::DEEP_COPY_NODES + // Controller might need different inputs per scene instance + | osg::CopyOp::DEEP_COPY_CALLBACKS); + } + + osg::StateSet* CopyOp::operator ()(const osg::StateSet* stateset) const + { + if (!stateset) + return NULL; + if (stateset->getDataVariance() == osg::StateSet::DYNAMIC) + return osg::clone(stateset, osg::CopyOp::DEEP_COPY_STATESETS); + return const_cast(stateset); + } + + osg::Node* CopyOp::operator ()(const osg::Node* node) const + { + if (const osgParticle::ParticleProcessor* processor = dynamic_cast(node)) + return operator()(processor); + if (const osgParticle::ParticleSystemUpdater* updater = dynamic_cast(node)) + { + osgParticle::ParticleSystemUpdater* cloned = osg::clone(updater, osg::CopyOp::DEEP_COPY_NODES); + mMap2[cloned] = updater->getParticleSystem(0); + return cloned; + } + return osg::CopyOp::operator()(node); + } + + osg::Drawable* CopyOp::operator ()(const osg::Drawable* drawable) const + { + if (const osgParticle::ParticleSystem* partsys = dynamic_cast(drawable)) + return operator()(partsys); + if (dynamic_cast(drawable) + || dynamic_cast(drawable)) + return osg::clone(drawable, osg::CopyOp::DEEP_COPY_DRAWABLES); + + return osg::CopyOp::operator()(drawable); + } + + osgParticle::ParticleProcessor* CopyOp::operator() (const osgParticle::ParticleProcessor* processor) const + { + osgParticle::ParticleProcessor* cloned = osg::clone(processor, osg::CopyOp::DEEP_COPY_NODES); + mMap[cloned] = processor->getParticleSystem(); + return cloned; + } + + osgParticle::ParticleSystem* CopyOp::operator ()(const osgParticle::ParticleSystem* partsys) const + { + osgParticle::ParticleSystem* cloned = osg::clone(partsys, osg::CopyOp::DEEP_COPY_DRAWABLES); + + for (std::map::const_iterator it = mMap.begin(); it != mMap.end(); ++it) + { + if (it->second == partsys) + { + it->first->setParticleSystem(cloned); + } + } + for (std::map::const_iterator it = mMap2.begin(); it != mMap2.end(); ++it) + { + if (it->second == partsys) + { + osgParticle::ParticleSystemUpdater* updater = it->first; + updater->removeParticleSystem(updater->getParticleSystem(0)); + updater->addParticleSystem(cloned); + } + } + return cloned; + } + +} diff --git a/components/sceneutil/clone.hpp b/components/sceneutil/clone.hpp new file mode 100644 index 000000000..662dad543 --- /dev/null +++ b/components/sceneutil/clone.hpp @@ -0,0 +1,45 @@ +#ifndef OPENMW_COMPONENTS_SCENEUTIL_CLONE_H +#define OPENMW_COMPONENTS_SCENEUTIL_CLONE_H + +#include + +#include + +namespace osgParticle +{ + class ParticleProcessor; + class ParticleSystem; + class ParticleSystemUpdater; +} + +namespace SceneUtil +{ + + /// @par Defines the cloning behaviour we need: + /// * Assigns updated ParticleSystem pointers on cloned emitters and programs. + /// * Creates deep copy of StateSets if they have a DYNAMIC data variance. + /// * Deep copies RigGeometry and MorphGeometry so they can animate without affecting clones. + /// @warning Do not use an object of this class for more than one copy operation. + class CopyOp : public osg::CopyOp + { + public: + CopyOp(); + + virtual osgParticle::ParticleSystem* operator() (const osgParticle::ParticleSystem* partsys) const; + virtual osgParticle::ParticleProcessor* operator() (const osgParticle::ParticleProcessor* processor) const; + + virtual osg::Node* operator() (const osg::Node* node) const; + virtual osg::Drawable* operator() (const osg::Drawable* drawable) const; + + virtual osg::StateSet* operator() (const osg::StateSet* stateset) const; + + private: + // maps new ParticleProcessor to their old ParticleSystem pointer + // a little messy, but I think this should be the most efficient way + mutable std::map mMap; + mutable std::map mMap2; + }; + +} + +#endif diff --git a/components/vfs/manager.cpp b/components/vfs/manager.cpp index d0e0cf586..829e08978 100644 --- a/components/vfs/manager.cpp +++ b/components/vfs/manager.cpp @@ -59,9 +59,14 @@ namespace VFS std::string normalized = name; normalize_path(normalized, mStrict); - std::map::const_iterator found = mIndex.find(normalized); + return getNormalized(normalized); + } + + Files::IStreamPtr Manager::getNormalized(const std::string &normalizedName) const + { + std::map::const_iterator found = mIndex.find(normalizedName); if (found == mIndex.end()) - throw std::runtime_error("Resource '" + name + "' not found"); + throw std::runtime_error("Resource '" + normalizedName + "' not found"); return found->second->open(); } @@ -78,4 +83,9 @@ namespace VFS return mIndex; } + void Manager::normalizeFilename(std::string &name) const + { + normalize_path(name, mStrict); + } + } diff --git a/components/vfs/manager.hpp b/components/vfs/manager.hpp index ebbec7d15..f74914977 100644 --- a/components/vfs/manager.hpp +++ b/components/vfs/manager.hpp @@ -38,10 +38,17 @@ namespace VFS /// Get a complete list of files from all archives const std::map& getIndex() const; + /// Normalize the given filename, making slashes/backslashes consistent, and lower-casing if mStrict is false. + void normalizeFilename(std::string& name) const; + /// Retrieve a file by name. /// @note Throws an exception if the file can not be found. Files::IStreamPtr get(const std::string& name) const; + /// Retrieve a file by name (name is already normalized). + /// @note Throws an exception if the file can not be found. + Files::IStreamPtr getNormalized(const std::string& normalizedName) const; + private: bool mStrict;