From 57b585570a6e4a76716497e0eee66fc01b0ab58d Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Feb 2017 00:55:35 +0100 Subject: [PATCH] Preload magic effect visuals of the player's selected weapon/spell --- apps/openmw/mwbase/windowmanager.hpp | 2 + apps/openmw/mwbase/world.hpp | 3 ++ apps/openmw/mwgui/hud.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 15 ++++++ apps/openmw/mwgui/windowmanagerimp.hpp | 6 +++ apps/openmw/mwworld/scene.cpp | 30 ++++++++++++ apps/openmw/mwworld/scene.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 68 +++++++++++++++++++++++++- apps/openmw/mwworld/worldimp.hpp | 7 +++ components/resource/objectcache.cpp | 12 +++++ components/resource/objectcache.hpp | 3 ++ components/resource/scenemanager.cpp | 8 +++ components/resource/scenemanager.hpp | 3 ++ 13 files changed, 158 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 3a53c3253..f103ce191 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -215,7 +215,9 @@ namespace MWBase virtual std::string getSelectedSpell() = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; virtual void setSelectedEnchantItem(const MWWorld::Ptr& item) = 0; + virtual const MWWorld::Ptr& getSelectedEnchantItem() const = 0; virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0; + virtual const MWWorld::Ptr& getSelectedWeapon() const = 0; virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 59f28fcc8..86d26d3a7 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -569,6 +569,9 @@ namespace MWBase /// Export scene graph to a file and return the filename. /// \param ptr object to export scene graph for (if empty, export entire scene graph) virtual std::string exportSceneGraph(const MWWorld::Ptr& ptr) = 0; + + /// Preload VFX associated with this effect list + virtual void preloadEffects(const ESM::EffectList* effectList) = 0; }; } diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index c5ae45789..e2ef52be0 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -39,6 +39,7 @@ namespace MWGui void setSelectedSpell(const std::string& spellId, int successChancePercent); void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); + const MWWorld::Ptr& getSelectedEnchantItem(); void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); void unsetSelectedSpell(); void unsetSelectedWeapon(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 937463ecd..55c9355a1 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1282,6 +1282,7 @@ namespace MWGui void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) { mSelectedSpell = spellId; + mSelectedEnchantItem = MWWorld::Ptr(); mHud->setSelectedSpell(spellId, successChancePercent); const ESM::Spell* spell = mStore->get().find(spellId); @@ -1291,6 +1292,7 @@ namespace MWGui void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) { + mSelectedEnchantItem = item; mSelectedSpell = ""; const ESM::Enchantment* ench = mStore->get() .find(item.getClass().getEnchantment(item)); @@ -1301,17 +1303,29 @@ namespace MWGui mSpellWindow->setTitle(item.getClass().getName(item)); } + const MWWorld::Ptr &WindowManager::getSelectedEnchantItem() const + { + return mSelectedEnchantItem; + } + void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) { + mSelectedWeapon = item; int durabilityPercent = static_cast(item.getClass().getItemHealth(item) / static_cast(item.getClass().getItemMaxHealth(item)) * 100); mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(item.getClass().getName(item)); } + const MWWorld::Ptr &WindowManager::getSelectedWeapon() const + { + return mSelectedWeapon; + } + void WindowManager::unsetSelectedSpell() { mSelectedSpell = ""; + mSelectedEnchantItem = MWWorld::Ptr(); mHud->unsetSelectedSpell(); MWWorld::Player* player = &MWBase::Environment::get().getWorld()->getPlayer(); @@ -1323,6 +1337,7 @@ namespace MWGui void WindowManager::unsetSelectedWeapon() { + mSelectedWeapon = MWWorld::Ptr(); mHud->unsetSelectedWeapon(); mInventoryWindow->setTitle("#{sSkillHandtohand}"); } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 4e2cef8af..a8f6263c2 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -13,6 +13,8 @@ #include "../mwbase/windowmanager.hpp" +#include "../mwworld/ptr.hpp" + #include #include @@ -244,7 +246,9 @@ namespace MWGui virtual std::string getSelectedSpell() { return mSelectedSpell; } virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); virtual void setSelectedEnchantItem(const MWWorld::Ptr& item); + virtual const MWWorld::Ptr& getSelectedEnchantItem() const; virtual void setSelectedWeapon(const MWWorld::Ptr& item); + virtual const MWWorld::Ptr& getSelectedWeapon() const; virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); @@ -403,6 +407,8 @@ namespace MWGui void onWindowChangeCoord(MyGUI::Window* _sender); std::string mSelectedSpell; + MWWorld::Ptr mSelectedEnchantItem; + MWWorld::Ptr mSelectedWeapon; std::stack mCurrentModals; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 8a3178e6c..0e8a1fecb 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -652,6 +653,35 @@ namespace MWWorld return Ptr(); } + class PreloadMeshItem : public SceneUtil::WorkItem + { + public: + PreloadMeshItem(const std::string& mesh, Resource::SceneManager* sceneManager) + : mMesh(mesh), mSceneManager(sceneManager) + { + } + + virtual void doWork() + { + try + { + mSceneManager->getTemplate(mMesh); + } + catch (std::exception& e) + { + } + } + private: + std::string mMesh; + Resource::SceneManager* mSceneManager; + }; + + void Scene::preload(const std::string &mesh) + { + if (!mRendering.getResourceSystem()->getSceneManager()->checkLoaded(mesh, mRendering.getReferenceTime())) + mRendering.getWorkQueue()->addWorkItem(new PreloadMeshItem(mesh, mRendering.getResourceSystem()->getSceneManager())); + } + void Scene::preloadCells(float dt) { const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 328538076..cbceb14f5 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -132,6 +132,8 @@ namespace MWWorld bool isCellActive(const CellStore &cell); Ptr searchPtrViaActorId (int actorId); + + void preload(const std::string& mesh); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 399525a8e..83b2b4d54 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -152,7 +152,7 @@ namespace MWWorld mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mUserDataPath(userDataPath), mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript), mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true), - mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0) + mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), mSpellPreloadTimer(0.f) { mPhysics = new MWPhysics::PhysicsSystem(resourceSystem, rootNode); mRendering = new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, &mFallback, resourcePath); @@ -1627,6 +1627,13 @@ namespace MWWorld updateSoundListener(); updatePlayer(paused); + + mSpellPreloadTimer -= duration; + if (mSpellPreloadTimer <= 0.f) + { + mSpellPreloadTimer = 0.1f; + preloadSpells(); + } } void World::updatePlayer(bool paused) @@ -1683,7 +1690,39 @@ namespace MWWorld if (result.mHit) mRendering->getCamera()->setCameraDistance((result.mHitPos - focal).length() - radius, false, false); } + } + void World::preloadSpells() + { + std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell(); + if (!selectedSpell.empty()) + { + const ESM::Spell* spell = mStore.get().search(selectedSpell); + if (spell) + preloadEffects(&spell->mEffects); + } + const MWWorld::Ptr& selectedEnchantItem = MWBase::Environment::get().getWindowManager()->getSelectedEnchantItem(); + if (!selectedEnchantItem.isEmpty()) + { + std::string enchantId = selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem); + if (!enchantId.empty()) + { + const ESM::Enchantment* ench = mStore.get().search(selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem)); + if (ench) + preloadEffects(&ench->mEffects); + } + } + const MWWorld::Ptr& selectedWeapon = MWBase::Environment::get().getWindowManager()->getSelectedWeapon(); + if (!selectedWeapon.isEmpty()) + { + std::string enchantId = selectedWeapon.getClass().getEnchantment(selectedWeapon); + if (!enchantId.empty()) + { + const ESM::Enchantment* ench = mStore.get().search(enchantId); + if (ench && ench->mData.mType == ESM::Enchantment::WhenStrikes) + preloadEffects(&ench->mEffects); + } + } } void World::updateSoundListener() @@ -2720,7 +2759,6 @@ namespace MWWorld if (!selectedSpell.empty()) { const ESM::Spell* spell = getStore().get().find(selectedSpell); - cast.cast(spell); } else if (actor.getClass().hasInventoryStore(actor)) @@ -3356,4 +3394,30 @@ namespace MWWorld return mPhysics->getHitDistance(weaponPos, target) - halfExtents.y(); } + void preload(MWWorld::Scene* scene, const ESMStore& store, const std::string& obj) + { + if (obj.empty()) + return; + MWWorld::ManualRef ref(store, obj); + std::string model = ref.getPtr().getClass().getModel(ref.getPtr()); + if (!model.empty()) + scene->preload(model); + } + + void World::preloadEffects(const ESM::EffectList *effectList) + { + for (std::vector::const_iterator it = effectList->mList.begin(); it != effectList->mList.end(); ++it) + { + const ESM::MagicEffect *effect = mStore.get().find(it->mEffectID); + + preload(mWorldScene, mStore, effect->mCasting); + preload(mWorldScene, mStore, effect->mHit); + + if (it->mArea > 0) + preload(mWorldScene, mStore, effect->mArea); + if (it->mRange == ESM::RT_Target) + preload(mWorldScene, mStore, effect->mBolt); + } + } + } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 688ff4751..ce6e27672 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -135,6 +135,8 @@ namespace MWWorld void updateWindowManager (); void updatePlayer(bool paused); + void preloadSpells(); + MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true); public: // FIXME @@ -174,6 +176,8 @@ namespace MWWorld bool mGoToJail; int mDaysInPrison; + float mSpellPreloadTimer; + float feetToGameUnits(float feet); float getActivationDistancePlusTelekinesis(); @@ -677,6 +681,9 @@ namespace MWWorld /// Export scene graph to a file and return the filename. /// \param ptr object to export scene graph for (if empty, export entire scene graph) virtual std::string exportSceneGraph(const MWWorld::Ptr& ptr); + + /// Preload VFX associated with this effect list + virtual void preloadEffects(const ESM::EffectList* effectList); }; } diff --git a/components/resource/objectcache.cpp b/components/resource/objectcache.cpp index 7caf5366c..d80561c24 100644 --- a/components/resource/objectcache.cpp +++ b/components/resource/objectcache.cpp @@ -49,6 +49,18 @@ osg::ref_ptr ObjectCache::getRefFromObjectCache(const std::string& else return 0; } +bool ObjectCache::checkInObjectCache(const std::string &fileName, double timeStamp) +{ + OpenThreads::ScopedLock lock(_objectCacheMutex); + ObjectCacheMap::iterator itr = _objectCache.find(fileName); + if (itr!=_objectCache.end()) + { + itr->second.second = timeStamp; + return true; + } + else return false; +} + void ObjectCache::updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime) { OpenThreads::ScopedLock lock(_objectCacheMutex); diff --git a/components/resource/objectcache.hpp b/components/resource/objectcache.hpp index 82aca76f0..3d65df546 100644 --- a/components/resource/objectcache.hpp +++ b/components/resource/objectcache.hpp @@ -64,6 +64,9 @@ class ObjectCache : public osg::Referenced /** Get an ref_ptr from the object cache*/ osg::ref_ptr getRefFromObjectCache(const std::string& fileName); + /** Check if an object is in the cache, and if it is, update its usage time stamp. */ + bool checkInObjectCache(const std::string& fileName, double timeStamp); + /** call releaseGLObjects on all objects attached to the object cache.*/ void releaseGLObjects(osg::State* state); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index c975286ce..9cda7d755 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -272,6 +272,14 @@ namespace Resource mShaderManager->setShaderPath(path); } + bool SceneManager::checkLoaded(const std::string &name, double timeStamp) + { + std::string normalized = name; + mVFS->normalizeFilename(normalized); + + return mCache->checkInObjectCache(normalized, timeStamp); + } + /// @brief Callback to read image files from the VFS. class ImageReadCallback : public osgDB::ReadFileCallback { diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index ee4a453a3..97e5b43be 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -77,6 +77,9 @@ namespace Resource void setShaderPath(const std::string& path); + /// Check if a given scene is loaded and if so, update its usage timestamp to prevent it from being unloaded + bool checkLoaded(const std::string& name, double referenceTime); + /// Get a read-only copy of this scene "template" /// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead. /// If even the error marker mesh can not be found, an exception is thrown.