From b7adbe79b9a0c58952e98dfe1024b992495600e0 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 9 Feb 2019 14:06:05 +0400 Subject: [PATCH 001/106] Allow localization to use implicit keywords (bug #4841) --- CHANGELOG.md | 1 + apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 --- apps/openmw/mwdialogue/hypertextparser.hpp | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a71222989..eaa44cc67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Bug #4820: Spell absorption is broken Bug #4827: NiUVController is handled incorrectly Bug #4828: Potion looping effects VFX are not shown for NPCs + Bug #4841: Russian localization ignores implicit keywords Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Feature #3610: Option to invert X axis diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 17f69d69b..9d2e3e288 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -97,9 +97,6 @@ namespace MWDialogue topicId = mTranslationDataStorage.topicStandardForm(topicId); } - if (tok->isImplicitKeyword() && mTranslationDataStorage.hasTranslation()) - continue; - if (mActorKnownTopics.count( topicId )) mKnownTopics.insert( topicId ); } diff --git a/apps/openmw/mwdialogue/hypertextparser.hpp b/apps/openmw/mwdialogue/hypertextparser.hpp index 13e135f3c..4ae0474c4 100644 --- a/apps/openmw/mwdialogue/hypertextparser.hpp +++ b/apps/openmw/mwdialogue/hypertextparser.hpp @@ -19,7 +19,6 @@ namespace MWDialogue Token(const std::string & text, Type type) : mText(text), mType(type) {} bool isExplicitLink() { return mType == ExplicitLink; } - bool isImplicitKeyword() { return mType == ImplicitKeyword; } std::string mText; Type mType; From fcdb0c16bf24e1312685160099b37229b3bdd468 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 25 Jan 2019 20:04:35 +0400 Subject: [PATCH 002/106] Update jail state once instead of for every single hour --- CHANGELOG.md | 1 + apps/openmw/mwbase/mechanicsmanager.hpp | 4 ++-- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwgui/jailscreen.cpp | 3 +-- apps/openmw/mwgui/trainingwindow.cpp | 3 +-- apps/openmw/mwgui/travelwindow.cpp | 5 +---- apps/openmw/mwgui/waitdialog.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 14 +++++++------- apps/openmw/mwmechanics/actors.hpp | 6 +++--- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 10 +++++----- apps/openmw/mwmechanics/mechanicsmanagerimp.hpp | 4 ++-- apps/openmw/mwworld/cells.cpp | 6 +++--- apps/openmw/mwworld/cells.hpp | 2 +- apps/openmw/mwworld/cellstore.cpp | 6 +++--- apps/openmw/mwworld/cellstore.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 4 ++-- apps/openmw/mwworld/worldimp.hpp | 2 +- 17 files changed, 36 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6013ce55..30585f9d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Bug #4813: Creatures with known file but no "Sound Gen Creature" assigned use default sounds Bug #4815: "Finished" journal entry with lower index doesn't close journal, SetJournalIndex closes journal Bug #4820: Spell absorption is broken + Bug #4823: Jail progress bar works incorrectly Bug #4827: NiUVController is handled incorrectly Bug #4828: Potion looping effects VFX are not shown for NPCs Bug #4837: CTD when a mesh with NiLODNode root node with particles is loaded diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 1bdd8f8b5..5c5821bca 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -90,9 +90,9 @@ namespace MWBase virtual void setPlayerClass (const ESM::Class& class_) = 0; ///< Set player class to custom class. - virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep) = 0; + virtual void restoreDynamicStats(MWWorld::Ptr actor, double hours, bool sleep) = 0; - virtual void rest(bool sleep) = 0; + virtual void rest(double hours, bool sleep) = 0; ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 217e6d367..cbbd76c2f 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -587,7 +587,7 @@ namespace MWBase virtual bool isPlayerInJail() const = 0; - virtual void rest() = 0; + virtual void rest(double hours) = 0; virtual void setPlayerTraveling(bool traveling) = 0; virtual bool isPlayerTraveling() const = 0; diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 1761e1346..ee0c893ba 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -78,8 +78,7 @@ namespace MWGui MWWorld::Ptr player = MWMechanics::getPlayer(); - for (int i=0; irest(true); + MWBase::Environment::get().getMechanicsManager()->rest(mDays * 24, true); MWBase::Environment::get().getWorld()->advanceTime(mDays * 24); std::set skills; diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 60e6584c7..acc2ef72a 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -169,8 +169,7 @@ namespace MWGui npcStats.setGoldPool(npcStats.getGoldPool() + price); // advance time - MWBase::Environment::get().getMechanicsManager()->rest(false); - MWBase::Environment::get().getMechanicsManager()->rest(false); + MWBase::Environment::get().getMechanicsManager()->rest(2, false); MWBase::Environment::get().getWorld ()->advanceTime (2); setVisible(false); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 1bcbc2d12..0ce864556 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -174,10 +174,7 @@ namespace MWGui ESM::Position playerPos = player.getRefData().getPosition(); float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length(); int hours = static_cast(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->mValue.getFloat()); - for(int i = 0;i < hours;i++) - { - MWBase::Environment::get().getMechanicsManager ()->rest (true); - } + MWBase::Environment::get().getMechanicsManager ()->rest (hours, true); MWBase::Environment::get().getWorld()->advanceTime(hours); } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 037fcb19b..48384d4b5 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -232,7 +232,7 @@ namespace MWGui void WaitDialog::onWaitingProgressChanged(int cur, int total) { mProgressBar.setProgress(cur, total); - MWBase::Environment::get().getMechanicsManager()->rest(mSleeping); + MWBase::Environment::get().getMechanicsManager()->rest(1, mSleeping); MWBase::Environment::get().getWorld()->advanceTime(1); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 427bf9e7d..31d4ef42c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -568,7 +568,7 @@ namespace MWMechanics creatureStats.setMagicka(magicka); } - void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, bool sleep) + void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, double hours, bool sleep) { MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); if (stats.isDead()) @@ -582,11 +582,11 @@ namespace MWMechanics getRestorationPerHourOfSleep(ptr, health, magicka); DynamicStat stat = stats.getHealth(); - stat.setCurrent(stat.getCurrent() + health); + stat.setCurrent(stat.getCurrent() + health * hours); stats.setHealth(stat); stat = stats.getMagicka(); - stat.setCurrent(stat.getCurrent() + magicka); + stat.setCurrent(stat.getCurrent() + magicka * hours); stats.setMagicka(stat); } @@ -610,7 +610,7 @@ namespace MWMechanics float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; - fatigue.setCurrent (fatigue.getCurrent() + 3600 * x); + fatigue.setCurrent (fatigue.getCurrent() + 3600 * x * hours); stats.setFatigue (fatigue); } @@ -1685,9 +1685,9 @@ namespace MWMechanics } } - void Actors::rest(bool sleep) + void Actors::rest(double hours, bool sleep) { - float duration = 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + float duration = hours * 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor(); const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); @@ -1697,7 +1697,7 @@ namespace MWMechanics continue; if (!sleep || iter->first == player) - restoreDynamicStats(iter->first, sleep); + restoreDynamicStats(iter->first, hours, sleep); if ((!iter->first.getRefData().getBaseNode()) || (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > mActorsProcessingRange*mActorsProcessingRange) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 4ed0a50b1..07e60e1d1 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -121,13 +121,13 @@ namespace MWMechanics void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance); - void rest(bool sleep); - ///< Update actors while the player is waiting or sleeping. This should be called every hour. + void rest(double hours, bool sleep); + ///< Update actors while the player is waiting or sleeping. void updateSneaking(CharacterController* ctrl, float duration); ///< Update the sneaking indicator state according to the given player character controller. - void restoreDynamicStats(const MWWorld::Ptr& actor, bool sleep); + void restoreDynamicStats(const MWWorld::Ptr& actor, double hours, bool sleep); int getHoursToRest(const MWWorld::Ptr& ptr) const; ///< Calculate how many hours the given actor needs to rest in order to be fully healed diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index b909d7df1..e6c7796d7 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -479,17 +479,17 @@ namespace MWMechanics return mActors.isSneaking(ptr); } - void MechanicsManager::rest(bool sleep) + void MechanicsManager::rest(double hours, bool sleep) { if (sleep) - MWBase::Environment::get().getWorld()->rest(); + MWBase::Environment::get().getWorld()->rest(hours); - mActors.rest(sleep); + mActors.rest(hours, sleep); } - void MechanicsManager::restoreDynamicStats(MWWorld::Ptr actor, bool sleep) + void MechanicsManager::restoreDynamicStats(MWWorld::Ptr actor, double hours, bool sleep) { - mActors.restoreDynamicStats(actor, sleep); + mActors.restoreDynamicStats(actor, hours, sleep); } int MechanicsManager::getHoursToRest() const diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 2dda376f5..a81a6bd75 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -95,9 +95,9 @@ namespace MWMechanics virtual void setPlayerClass (const ESM::Class& class_) override; ///< Set player class to custom class. - virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep) override; + virtual void restoreDynamicStats(MWWorld::Ptr actor, double hours, bool sleep) override; - virtual void rest(bool sleep) override; + virtual void rest(double hours, bool sleep) override; ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 2b032e1d7..20e3c60d9 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -151,16 +151,16 @@ MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name) return &result->second; } -void MWWorld::Cells::rest () +void MWWorld::Cells::rest (double hours) { for (auto &interior : mInteriors) { - interior.second.rest(); + interior.second.rest(hours); } for (auto &exterior : mExteriors) { - exterior.second.rest(); + exterior.second.rest(hours); } } diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index bd730329b..dce42d996 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -61,7 +61,7 @@ namespace MWWorld /// @note name must be lower case Ptr getPtr (const std::string& name); - void rest (); + void rest (double hours); /// Get all Ptrs referencing \a name in exterior cells /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index e291951ae..12430ebde 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -966,7 +966,7 @@ namespace MWWorld } } - void CellStore::rest() + void CellStore::rest(double hours) { if (mState == State_Loaded) { @@ -975,7 +975,7 @@ namespace MWWorld Ptr ptr = getCurrentPtr(&*it); if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) { - MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, true); + MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, hours, true); } } for (CellRefList::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) @@ -983,7 +983,7 @@ namespace MWWorld Ptr ptr = getCurrentPtr(&*it); if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) { - MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, true); + MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, hours, true); } } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index ce8bb3da4..baecfb5ea 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -183,7 +183,7 @@ namespace MWWorld /// @return updated MWWorld::Ptr with the new CellStore pointer set. MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); - void rest(); + void rest(double hours); /// Make a copy of the given object and insert it into this cell. /// @note If you get a linker error here, this means the given type can not be inserted into a cell. diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d7963fae1..2c3cc59d7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3266,9 +3266,9 @@ namespace MWWorld return closestMarker; } - void World::rest() + void World::rest(double hours) { - mCells.rest(); + mCells.rest(hours); } void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 98e82bb86..af1ed46a5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -574,7 +574,7 @@ namespace MWWorld RestPermitted canRest() const override; ///< check if the player is allowed to rest - void rest() override; + void rest(double hours) override; /// \todo Probably shouldn't be here MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override; From f5eacfcf63e412eb9c6133528f3f258f8d9c8ed3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 6 Feb 2019 22:40:50 +0400 Subject: [PATCH 003/106] Support for temporary stunted magicka effects --- apps/openmw/mwmechanics/actors.cpp | 71 +++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 31d4ef42c..b627bdafa 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -130,23 +130,45 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); - bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).getMagnitude() > 0; int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); - health = 0.1f * endurance; - magicka = 0; - if (!stunted) - { - float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat (); - magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); - } + float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat (); + magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); } } namespace MWMechanics { + class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor + { + public: + float mRemainingTime; + + GetStuntedMagickaDuration(const MWWorld::Ptr& actor) + : mRemainingTime(0.f){} + + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, const std::string& sourceId, int casterActorId, + float magnitude, float remainingTime = -1, float totalTime = -1) + { + if (mRemainingTime == -1) return; + + if (key.mId == ESM::MagicEffect::StuntedMagicka) + { + if (totalTime == -1) + { + mRemainingTime = -1; + return; + } + + if (remainingTime > mRemainingTime) + mRemainingTime = remainingTime; + } + } + }; + class SoulTrap : public MWMechanics::EffectSourceVisitor { MWWorld::Ptr mCreature; @@ -585,9 +607,33 @@ namespace MWMechanics stat.setCurrent(stat.getCurrent() + health * hours); stats.setHealth(stat); - stat = stats.getMagicka(); - stat.setCurrent(stat.getCurrent() + magicka * hours); - stats.setMagicka(stat); + double restoreHours = hours; + bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).getMagnitude() > 0; + if (stunted) + { + // Stunted Magicka effect should be taken into account. + GetStuntedMagickaDuration visitor(ptr); + stats.getActiveSpells().visitEffectSources(visitor); + stats.getSpells().visitEffectSources(visitor); + if (ptr.getClass().hasInventoryStore(ptr)) + ptr.getClass().getInventoryStore(ptr).visitEffectSources(visitor); + + // Take a maximum remaining duration of Stunted Magicka effects (-1 is a constant one) in game hours. + if (visitor.mRemainingTime > 0) + { + double timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + restoreHours = std::max(0.0, hours - visitor.mRemainingTime * timeScale / 3600.f); + } + else if (visitor.mRemainingTime == -1) + restoreHours = 0; + } + + if (restoreHours > 0) + { + stat = stats.getMagicka(); + stat.setCurrent(stat.getCurrent() + magicka * restoreHours); + stats.setMagicka(stat); + } } // Current fatigue can be above base value due to a fortify effect. @@ -1809,11 +1855,12 @@ namespace MWMechanics getRestorationPerHourOfSleep(ptr, healthPerHour, magickaPerHour); CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).getMagnitude() > 0; float healthHours = healthPerHour > 0 ? (stats.getHealth().getModified() - stats.getHealth().getCurrent()) / healthPerHour : 1.0f; - float magickaHours = magickaPerHour > 0 + float magickaHours = magickaPerHour > 0 && !stunted ? (stats.getMagicka().getModified() - stats.getMagicka().getCurrent()) / magickaPerHour : 1.0f; From 619a111a117a753fa1f02e61845774fe9713114a Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 23 Feb 2019 22:39:13 +0300 Subject: [PATCH 004/106] Run startup script once at engine start when game is running (bug #4877) --- CHANGELOG.md | 1 + apps/openmw/engine.cpp | 7 ++++++- apps/openmw/mwworld/worldimp.cpp | 9 +++------ apps/openmw/mwworld/worldimp.hpp | 4 +--- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fac0b4ac0..da3b2b0cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ Bug #4837: CTD when a mesh with NiLODNode root node with particles is loaded Bug #4860: Actors outside of processing range visible for one frame after spawning Bug #4876: AI ratings handling inconsistencies + Bug #4877: Startup script executes only on a new game start Bug #4888: Global variable stray explicit reference calls break script compilation Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2a4145c98..a68bf4c36 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -546,7 +546,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // Create the world mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(), mFileCollections, mContentFiles, mEncoder, mFallbackMap, - mActivationDistanceOverride, mCellName, mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string())); + mActivationDistanceOverride, mCellName, mResDir.string(), mCfgMgr.getUserDataPath().string())); mEnvironment.getWorld()->setupPlayer(); input->setPlayer(&mEnvironment.getWorld()->getPlayer()); @@ -719,6 +719,11 @@ void OMW::Engine::go() mEnvironment.getStateManager()->newGame (!mNewGame); } + if (!mStartupScript.empty() && mEnvironment.getStateManager()->getState() == MWState::StateManager::State_Running) + { + mEnvironment.getWindowManager()->executeInConsole(mStartupScript); + } + // Start the main rendering loop osg::Timer frameTimer; double simulationTime = 0.0; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e3658285d..1109c9865 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -157,12 +157,12 @@ namespace MWWorld const Files::Collections& fileCollections, const std::vector& contentFiles, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, - int activationDistanceOverride, const std::string& startCell, const std::string& startupScript, - const std::string& resourcePath, const std::string& userDataPath) + int activationDistanceOverride, const std::string& startCell, + const std::string& resourcePath, const std::string& userDataPath) : mResourceSystem(resourceSystem), mFallback(fallbackMap), mLocalScripts (mStore), mSky (true), mCells (mStore, mEsm), mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mUserDataPath(userDataPath), - mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript), + mActivationDistanceOverride (activationDistanceOverride), mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true), mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f) @@ -307,9 +307,6 @@ namespace MWWorld if (!mPhysics->toggleCollisionMode()) mPhysics->toggleCollisionMode(); - if (!mStartupScript.empty()) - MWBase::Environment::get().getWindowManager()->executeInConsole(mStartupScript); - MWBase::Environment::get().getWindowManager()->updatePlayer(); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 98e82bb86..88d77eed3 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -117,8 +117,6 @@ namespace MWWorld int mActivationDistanceOverride; - std::string mStartupScript; - std::map mDoorStates; ///< only holds doors that are currently moving. 1 = opening, 2 = closing @@ -198,7 +196,7 @@ namespace MWWorld const Files::Collections& fileCollections, const std::vector& contentFiles, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, - int activationDistanceOverride, const std::string& startCell, const std::string& startupScript, const std::string& resourcePath, const std::string& userDataPath); + int activationDistanceOverride, const std::string& startCell, const std::string& resourcePath, const std::string& userDataPath); virtual ~World(); From 46e1ed660c6bb37517ed40e3425944aafd27f162 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 28 Feb 2019 22:10:13 +0400 Subject: [PATCH 005/106] Revert "Render default land texture for Wilderness cells with distant terrain" This reverts commit 888c2d9a33c27b764e55793ecc5cee7a4364d446. --- components/terrain/quadtreeworld.cpp | 37 +++++++++++++++++++--------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 8d54f62ce..a97aba68c 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -201,22 +201,35 @@ public: node->setLodCallback(parent->getLodCallback()); node->setViewDataMap(mViewDataMap); - if (node->getSize() > mMinSize) + if (center.x() - size > mMaxX + || center.x() + size < mMinX + || center.y() - size > mMaxY + || center.y() + size < mMinY ) + // Out of bounds of the actual terrain - this will happen because + // we rounded the size up to the next power of two { - addChildren(node); + // Still create and return an empty node so as to not break the assumption that each QuadTreeNode has either 4 or 0 children. return node; } - // We arrived at a leaf - float minZ, maxZ; - mStorage->getMinMaxHeights(size, center, minZ, maxZ); - - float cellWorldSize = mStorage->getCellWorldSize(); - osg::BoundingBox boundingBox(osg::Vec3f((center.x()-halfSize)*cellWorldSize, (center.y()-halfSize)*cellWorldSize, minZ), - osg::Vec3f((center.x()+halfSize)*cellWorldSize, (center.y()+halfSize)*cellWorldSize, maxZ)); - node->setBoundingBox(boundingBox); - - return node; + if (node->getSize() <= mMinSize) + { + // We arrived at a leaf + float minZ,maxZ; + if (mStorage->getMinMaxHeights(size, center, minZ, maxZ)) + { + float cellWorldSize = mStorage->getCellWorldSize(); + osg::BoundingBox boundingBox(osg::Vec3f((center.x()-size)*cellWorldSize, (center.y()-size)*cellWorldSize, minZ), + osg::Vec3f((center.x()+size)*cellWorldSize, (center.y()+size)*cellWorldSize, maxZ)); + node->setBoundingBox(boundingBox); + } + return node; + } + else + { + addChildren(node); + return node; + } } osg::ref_ptr getRootNode() From a6fd077537bb8c114ae2b1ad5a1254ef6af1a87c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 28 Feb 2019 22:19:48 +0400 Subject: [PATCH 006/106] Render nearby default cells if the Distant Terrain is disabled --- components/terrain/quadtreeworld.cpp | 22 +++++++++++++++++++++- components/terrain/quadtreeworld.hpp | 7 ++++++- components/terrain/terraingrid.cpp | 5 +++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index a97aba68c..6a4ad6703 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -249,7 +249,7 @@ private: }; QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask, int borderMask, int compMapResolution, float compMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize) - : World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask) + : TerrainGrid(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask) , mViewDataMap(new ViewDataMap) , mQuadTreeBuilt(false) , mLodFactor(lodFactor) @@ -515,5 +515,25 @@ void QuadTreeWorld::setDefaultViewer(osg::Object *obj) mViewDataMap->setDefaultViewer(obj); } +void QuadTreeWorld::loadCell(int x, int y) +{ + // fallback behavior only for undefined cells (every other is already handled in quadtree) + float dummy; + if (!mStorage->getMinMaxHeights(1, osg::Vec2f(x+0.5, y+0.5), dummy, dummy)) + TerrainGrid::loadCell(x,y); + else + World::loadCell(x,y); +} + +void QuadTreeWorld::unloadCell(int x, int y) +{ + // fallback behavior only for undefined cells (every other is already handled in quadtree) + float dummy; + if (!mStorage->getMinMaxHeights(1, osg::Vec2f(x+0.5, y+0.5), dummy, dummy)) + TerrainGrid::unloadCell(x,y); + else + World::unloadCell(x,y); +} + } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index f724c44b1..f02f5df4a 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -2,6 +2,7 @@ #define COMPONENTS_TERRAIN_QUADTREEWORLD_H #include "world.hpp" +#include "terraingrid.hpp" #include @@ -16,7 +17,7 @@ namespace Terrain class ViewDataMap; /// @brief Terrain implementation that loads cells into a Quad Tree, with geometry LOD and texture LOD. The entire world is displayed at all times. - class QuadTreeWorld : public Terrain::World + class QuadTreeWorld : public TerrainGrid // note: derived from TerrainGrid is only to render default cells (see loadCell) { public: QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask, int compMapResolution, float comMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize); @@ -28,6 +29,10 @@ namespace Terrain virtual void enable(bool enabled); void cacheCell(View *view, int x, int y); + /// @note Not thread safe. + virtual void loadCell(int x, int y); + /// @note Not thread safe. + virtual void unloadCell(int x, int y); View* createView(); void preload(View* view, const osg::Vec3f& eyePoint); diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 1888a02b3..a7d43f673 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -5,6 +5,7 @@ #include #include "chunkmanager.hpp" +#include "compositemaprenderer.hpp" namespace Terrain { @@ -61,6 +62,10 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu if (parent) parent->addChild(node); + osg::UserDataContainer* udc = node->getUserDataContainer(); + if (udc && udc->getUserData()) + mCompositeMapRenderer->setImmediate(static_cast(udc->getUserData())); + return node; } } From e0cf460ba3288cb3cc0c43386da6453f6f63da9c Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 007/106] Do not load terrain beyond the viewing distance --- apps/openmw/mwrender/renderingmanager.cpp | 4 ++ components/terrain/quadtreenode.cpp | 26 ++++++++++++- components/terrain/quadtreenode.hpp | 4 +- components/terrain/quadtreeworld.cpp | 45 ++++++----------------- components/terrain/quadtreeworld.hpp | 3 ++ components/terrain/world.hpp | 2 + 6 files changed, 48 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f54e423f1..42bd0c69b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1198,6 +1198,10 @@ namespace MWRender mUniformNear->set(mNearClip); mUniformFar->set(mViewDistance); + + // Since our fog is not radial yet, we should take FOV in account, otherwise terrain near viewing distance may disappear. + float distanceMult = std::cos(osg::DegreesToRadians(mFieldOfView)/2.f); + mTerrain->setViewDistance(mViewDistance * (distanceMult ? 1.f/distanceMult : 1.f)); } void RenderingManager::updateTextureFiltering() diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index f8237306e..0c157cd48 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -76,6 +76,30 @@ QuadTreeNode *QuadTreeNode::getNeighbour(Direction dir) return mNeighbours[dir]; } +float QuadTreeNode::distance(const osg::Vec3f& v) const +{ + const osg::BoundingBox& box = getBoundingBox(); + if (box.contains(v)) + return 0; + else + { + osg::Vec3f maxDist(0,0,0); + if (v.x() < box.xMin()) + maxDist.x() = box.xMin() - v.x(); + else if (v.x() > box.xMax()) + maxDist.x() = v.x() - box.xMax(); + if (v.y() < box.yMin()) + maxDist.y() = box.yMin() - v.y(); + else if (v.y() > box.yMax()) + maxDist.y() = v.y() - box.yMax(); + if (v.z() < box.zMin()) + maxDist.z() = box.zMin() - v.z(); + else if (v.z() > box.zMax()) + maxDist.z() = v.z() - box.zMax(); + return maxDist.length(); + } +} + void QuadTreeNode::initNeighbours() { for (int i=0; i<4; ++i) @@ -92,7 +116,7 @@ void QuadTreeNode::traverse(osg::NodeVisitor &nv) ViewData* vd = getView(nv); - if ((mLodCallback && mLodCallback->isSufficientDetail(this, vd->getEyePoint())) || !getNumChildren()) + if ((mLodCallback && mLodCallback->isSufficientDetail(this, distance(vd->getEyePoint()))) || !getNumChildren()) vd->add(this, true); else osg::Group::traverse(nv); diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 618429c5c..336a257fb 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -23,7 +23,7 @@ namespace Terrain public: virtual ~LodCallback() {} - virtual bool isSufficientDetail(QuadTreeNode *node, const osg::Vec3f& eyePoint) = 0; + virtual bool isSufficientDetail(QuadTreeNode *node, float dist) = 0; }; class ViewDataMap; @@ -49,6 +49,8 @@ namespace Terrain child->addParent(this); }; + float distance(const osg::Vec3f& v) const; + /// Returns our direction relative to the parent node, or Root if we are the root node. ChildDirection getDirection() { return mDirection; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 6a4ad6703..597073229 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -40,33 +40,6 @@ namespace return targetlevel; } - float distanceToBox(const osg::BoundingBox& box, const osg::Vec3f& v) - { - if (box.contains(v)) - return 0; - else - { - osg::Vec3f maxDist(0,0,0); - - if (v.x() < box.xMin()) - maxDist.x() = box.xMin() - v.x(); - else if (v.x() > box.xMax()) - maxDist.x() = v.x() - box.xMax(); - - if (v.y() < box.yMin()) - maxDist.y() = box.yMin() - v.y(); - else if (v.y() > box.yMax()) - maxDist.y() = v.y() - box.yMax(); - - if (v.z() < box.zMin()) - maxDist.z() = box.zMin() - v.z(); - else if (v.z() > box.zMax()) - maxDist.z() = v.z() - box.zMax(); - - return maxDist.length(); - } - } - } namespace Terrain @@ -81,9 +54,8 @@ public: { } - virtual bool isSufficientDetail(QuadTreeNode* node, const osg::Vec3f& eyePoint) + virtual bool isSufficientDetail(QuadTreeNode* node, float dist) { - float dist = distanceToBox(node->getBoundingBox(), eyePoint); int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); @@ -254,6 +226,7 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour , mQuadTreeBuilt(false) , mLodFactor(lodFactor) , mVertexLodMod(vertexLodMod) + , mViewDistance(std::numeric_limits::max()) { // No need for culling on the Drawable / Transform level as the quad tree performs the culling already. mChunkManager->setCullingActive(false); @@ -269,7 +242,7 @@ QuadTreeWorld::~QuadTreeWorld() } -void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& eyePoint, bool visible) +void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& eyePoint, bool visible, float maxDist) { if (!node->hasValidBounds()) return; @@ -277,14 +250,18 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) visible = visible && !static_cast(nv)->isCulled(node->getBoundingBox()); - bool stopTraversal = (lodCallback && lodCallback->isSufficientDetail(node, eyePoint)) || !node->getNumChildren(); + float dist = node->distance(eyePoint); + if (dist > maxDist) + return; + + bool stopTraversal = (lodCallback && lodCallback->isSufficientDetail(node, dist)) || !node->getNumChildren(); if (stopTraversal) vd->add(node, visible); else { for (unsigned int i=0; igetNumChildren(); ++i) - traverse(node->getChild(i), vd, nv, lodCallback, eyePoint, visible); + traverse(node->getChild(i), vd, nv, lodCallback, eyePoint, visible, maxDist); } } @@ -416,7 +393,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) traverseToCell(mRootNode.get(), vd, x,y); } else - traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true); + traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true, mViewDistance); } else mRootNode->traverse(nv); @@ -496,7 +473,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint) ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); - traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), eyePoint, false); + traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), eyePoint, false, mViewDistance); for (unsigned int i=0; igetNumEntries(); ++i) { diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index f02f5df4a..bb09048c2 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -28,6 +28,8 @@ namespace Terrain virtual void enable(bool enabled); + virtual void setViewDistance(float distance) { mViewDistance = distance; } + void cacheCell(View *view, int x, int y); /// @note Not thread safe. virtual void loadCell(int x, int y); @@ -52,6 +54,7 @@ namespace Terrain bool mQuadTreeBuilt; float mLodFactor; int mVertexLodMod; + float mViewDistance; }; } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 4fb724ebb..83a2655fd 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -100,6 +100,8 @@ namespace Terrain /// Set the default viewer (usually a Camera), used as viewpoint for any viewers that don't use their own viewpoint. virtual void setDefaultViewer(osg::Object* obj) {} + virtual void setViewDistance(float distance) {} + Storage* getStorage() { return mStorage; } protected: From b214c54b3a9f9ee5a45ec13004f99732283d553f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 9 Mar 2019 13:12:54 +0400 Subject: [PATCH 008/106] Calculate bounding volumes when preloading model instance --- components/resource/scenemanager.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index d371a8ce4..61a40ee4b 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -531,6 +531,8 @@ namespace Resource if (mIncrementalCompileOperation) mIncrementalCompileOperation->add(loaded); + else + loaded->getBound(); mCache->addEntryToObjectCache(normalized, loaded); return loaded; @@ -543,6 +545,12 @@ namespace Resource mVFS->normalizeFilename(normalized); osg::ref_ptr node = createInstance(normalized); + + // 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(); + mInstanceCache->addEntryToObjectCache(normalized, node.get()); return node; } From 12f9184d003c1180cd2b75574dc5fc3319d224ab Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 9 Mar 2019 13:13:39 +0400 Subject: [PATCH 009/106] Allow to interrupt terrain preloading --- apps/openmw/mwworld/cellpreloader.cpp | 2 +- components/terrain/quadtreeworld.cpp | 4 ++-- components/terrain/quadtreeworld.hpp | 2 +- components/terrain/world.hpp | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 65ba19b4a..98e9c7368 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -381,7 +381,7 @@ namespace MWWorld { for (unsigned int i=0; ipreload(mTerrainViews[i], mPreloadPositions[i]); + mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort); mTerrainViews[i]->reset(0); } } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 8d54f62ce..fa3972a34 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -478,14 +478,14 @@ View* QuadTreeWorld::createView() return new ViewData; } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint, std::atomic &abort) { ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), eyePoint, false); - for (unsigned int i=0; igetNumEntries(); ++i) + for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index f724c44b1..b2c0b4f14 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -30,7 +30,7 @@ namespace Terrain void cacheCell(View *view, int x, int y); View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint); + void preload(View* view, const osg::Vec3f& eyePoint, std::atomic& abort); void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 4fb724ebb..d5d4e245c 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -93,7 +94,7 @@ namespace Terrain virtual View* createView() { return nullptr; } /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. - virtual void preload(View* view, const osg::Vec3f& eyePoint) {} + virtual void preload(View* view, const osg::Vec3f& eyePoint, std::atomic& abort) {} virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} From bacaa1f7894ab87ffdf8de8ac68a7ea55be0fc92 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 9 Mar 2019 13:52:03 +0400 Subject: [PATCH 010/106] Get rid of C-style limits in the shadows code --- components/sceneutil/mwshadowtechnique.cpp | 62 +++++++++++----------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 4125ebe7d..b9d8e4bd3 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -30,6 +30,8 @@ namespace { using namespace osgShadow; using namespace SceneUtil; +#define dbl_max std::numeric_limits::max() + ////////////////////////////////////////////////////////////////// // fragment shader // @@ -928,7 +930,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv) viewProjectionMatrix(2,3)==0.0; double minZNear = 0.0; - double maxZFar = DBL_MAX; + double maxZFar = dbl_max; if (cachedNearFarMode==osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { @@ -1088,8 +1090,8 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv) double yRange = (clsb._bb.yMax()-clsb._bb.yMin()); osg::Matrixd cornerConverter = osg::Matrixd::inverse(projectionMatrix) * osg::Matrixd::inverse(viewMatrix) * *cv.getModelViewMatrix(); - double minZ = DBL_MAX; - double maxZ = -DBL_MAX; + double minZ = dbl_max; + double maxZ = -dbl_max; clsb._bb._max[2] = 1.0; for (unsigned int i = 0; i < 8; i++) { @@ -1777,7 +1779,7 @@ bool MWShadowTechnique::computeShadowCameraSettings(Frustum& frustum, LightData& } else { - double zMax=-DBL_MAX; + double zMax=-dbl_max; OSG_INFO<<"lightDir = "<getValue(), mSettings.get().mNavMeshPathPrefix, navMeshRevision); + writeToFile(job.mNavMeshCacheItem->lockConst()->getValue(), mSettings.get().mNavMeshPathPrefix, navMeshRevision); } std::chrono::steady_clock::time_point AsyncNavMeshUpdater::setFirstStart(const std::chrono::steady_clock::time_point& value) diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 5ac28127e..5d2de481e 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -521,7 +521,7 @@ namespace UpdateNavMeshStatus replaceTile(const SharedNavMeshCacheItem& navMeshCacheItem, const TilePosition& changedTile, T&& navMeshData) { - const auto locked = navMeshCacheItem.lock(); + const auto locked = navMeshCacheItem->lock(); auto& navMesh = locked->getValue(); const int layer = 0; const auto tileRef = navMesh.getTileRefAt(changedTile.x(), changedTile.y(), layer); @@ -591,14 +591,14 @@ namespace DetourNavigator " playerTile=", playerTile, " changedTileDistance=", getDistance(changedTile, playerTile)); - const auto params = *navMeshCacheItem.lockConst()->getValue().getParams(); + const auto params = *navMeshCacheItem->lockConst()->getValue().getParams(); const osg::Vec3f origin(params.orig[0], params.orig[1], params.orig[2]); const auto x = changedTile.x(); const auto y = changedTile.y(); const auto removeTile = [&] { - const auto locked = navMeshCacheItem.lock(); + const auto locked = navMeshCacheItem->lock(); auto& navMesh = locked->getValue(); const auto tileRef = navMesh.getTileRefAt(x, y, 0); const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, nullptr, nullptr)); diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index a146fe0fb..cc62d6164 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -174,7 +174,7 @@ namespace DetourNavigator if (!navMesh) return out; const auto settings = getSettings(); - return findSmoothPath(navMesh.lock()->getValue(), toNavMeshCoordinates(settings, agentHalfExtents), + return findSmoothPath(navMesh->lockConst()->getValue(), toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start), toNavMeshCoordinates(settings, end), includeFlags, settings, out); } diff --git a/components/detournavigator/navmeshcacheitem.hpp b/components/detournavigator/navmeshcacheitem.hpp index e64b9d138..e6ca60ef7 100644 --- a/components/detournavigator/navmeshcacheitem.hpp +++ b/components/detournavigator/navmeshcacheitem.hpp @@ -64,7 +64,8 @@ namespace DetourNavigator std::map> mUsedTiles; }; - using SharedNavMeshCacheItem = Misc::SharedGuarded; + using GuardedNavMeshCacheItem = Misc::ScopeGuarded; + using SharedNavMeshCacheItem = std::shared_ptr; } #endif diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 3e2691565..03a8b055f 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -93,7 +93,7 @@ namespace DetourNavigator if (cached != mCache.end()) return; mCache.insert(std::make_pair(agentHalfExtents, - std::make_shared(makeEmptyNavMesh(mSettings), ++mGenerationCounter))); + std::make_shared(makeEmptyNavMesh(mSettings), ++mGenerationCounter))); log("cache add for agent=", agentHalfExtents); } @@ -159,7 +159,7 @@ namespace DetourNavigator } const auto changedTiles = mChangedTiles.find(agentHalfExtents); { - const auto locked = cached.lock(); + const auto locked = cached->lockConst(); const auto& navMesh = locked->getValue(); if (changedTiles != mChangedTiles.end()) { diff --git a/components/misc/guarded.hpp b/components/misc/guarded.hpp index 114a77b10..559476867 100644 --- a/components/misc/guarded.hpp +++ b/components/misc/guarded.hpp @@ -83,38 +83,6 @@ namespace Misc std::mutex mMutex; T mValue; }; - - template - class SharedGuarded - { - public: - SharedGuarded() - : mMutex(std::make_shared()), mValue() - {} - - SharedGuarded(std::shared_ptr value) - : mMutex(std::make_shared()), mValue(std::move(value)) - {} - - Locked lock() const - { - return Locked(*mMutex, *mValue); - } - - Locked lockConst() const - { - return Locked(*mMutex, *mValue); - } - - operator bool() const - { - return static_cast(mValue); - } - - private: - std::shared_ptr mMutex; - std::shared_ptr mValue; - }; } #endif From f6a1d3cecfac2291c3ff6f9c9a5213c57c218550 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 10 Mar 2019 16:04:44 +0300 Subject: [PATCH 020/106] Store weak pointers to navmesh in jobs queue To avoid useless processing for removed navmeshes. --- components/detournavigator/asyncnavmeshupdater.cpp | 12 +++++++++--- components/detournavigator/asyncnavmeshupdater.hpp | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 667fff88e..e9172f041 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -129,12 +129,17 @@ namespace DetourNavigator const auto firstStart = setFirstStart(start); + const auto navMeshCacheItem = job.mNavMeshCacheItem.lock(); + + if (!navMeshCacheItem) + return true; + const auto recastMesh = mRecastMeshManager.get().getMesh(job.mChangedTile); const auto playerTile = *mPlayerTile.lockConst(); const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile); const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile, - offMeshConnections, mSettings, job.mNavMeshCacheItem, mNavMeshTilesCache); + offMeshConnections, mSettings, navMeshCacheItem, mNavMeshTilesCache); const auto finish = std::chrono::steady_clock::now(); @@ -143,7 +148,7 @@ namespace DetourNavigator using FloatMs = std::chrono::duration; { - const auto locked = job.mNavMeshCacheItem->lockConst(); + const auto locked = navMeshCacheItem->lockConst(); log("cache updated for agent=", job.mAgentHalfExtents, " status=", status, " generation=", locked->getGeneration(), " revision=", locked->getNavMeshRevision(), @@ -194,7 +199,8 @@ namespace DetourNavigator writeToFile(*recastMesh, mSettings.get().mRecastMeshPathPrefix + std::to_string(job.mChangedTile.x()) + "_" + std::to_string(job.mChangedTile.y()) + "_", recastMeshRevision); if (mSettings.get().mEnableWriteNavMeshToFile) - writeToFile(job.mNavMeshCacheItem->lockConst()->getValue(), mSettings.get().mNavMeshPathPrefix, navMeshRevision); + if (const auto shared = job.mNavMeshCacheItem.lock()) + writeToFile(shared->lockConst()->getValue(), mSettings.get().mNavMeshPathPrefix, navMeshRevision); } std::chrono::steady_clock::time_point AsyncNavMeshUpdater::setFirstStart(const std::chrono::steady_clock::time_point& value) diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 98359964d..55e502b45 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -48,7 +48,7 @@ namespace DetourNavigator struct Job { osg::Vec3f mAgentHalfExtents; - SharedNavMeshCacheItem mNavMeshCacheItem; + std::weak_ptr mNavMeshCacheItem; TilePosition mChangedTile; unsigned mTryNumber; ChangeType mChangeType; From 578beb630575f6785fdd1eebe71392e548fdce11 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 10 Mar 2019 17:05:56 +0300 Subject: [PATCH 021/106] Use selected object local variables in console (feature #3893) --- CHANGELOG.md | 1 + apps/openmw/mwgui/console.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d14b45e90..534ab2270 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Feature #3610: Option to invert X axis + Feature #3893: Implicit target for "set" function in console Feature #3980: In-game option to disable controller Feature #4209: Editor: Faction rank sub-table Feature #4673: Weapon sheathing diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 929dffe48..899bc0d90 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -11,10 +11,12 @@ #include "../mwscript/extensions.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" +#include "../mwworld/class.hpp" namespace MWGui { @@ -173,6 +175,12 @@ namespace MWGui print("> " + command + "\n"); Compiler::Locals locals; + if (!mPtr.isEmpty()) + { + std::string script = mPtr.getClass().getScript(mPtr); + if (!script.empty()) + locals = MWBase::Environment::get().getScriptManager()->getLocals(script); + } Compiler::Output output (locals); if (compile (command + "\n", output)) From dd952c3ddbbe7d69079a21da0dcdeb7db076f98c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 10 Mar 2019 17:12:43 +0300 Subject: [PATCH 022/106] Fix extra space in "no script" showvars output --- apps/openmw/mwscript/miscextensions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index c480cfec8..a10dc00d2 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -951,7 +951,7 @@ namespace MWScript const std::string script = ptr.getClass().getScript(ptr); if(script.empty()) - str<< ptr.getCellRef().getRefId()<<" does not have a script."; + str<< ptr.getCellRef().getRefId()<<" does not have a script."; else { str<< "Local variables for "< Date: Sun, 4 Nov 2018 22:57:10 +0300 Subject: [PATCH 023/106] Use std::make_shared --- components/nif/data.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index d19c8321e..086021930 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -120,7 +120,7 @@ void NiRotatingParticlesData::read(NIFStream *nif) void NiPosData::read(NIFStream *nif) { - mKeyList.reset(new Vector3KeyMap); + mKeyList = std::make_shared(); mKeyList->read(nif); } @@ -128,14 +128,14 @@ void NiUVData::read(NIFStream *nif) { for(int i = 0;i < 4;i++) { - mKeyList[i].reset(new FloatKeyMap); + mKeyList[i] = std::make_shared(); mKeyList[i]->read(nif); } } void NiFloatData::read(NIFStream *nif) { - mKeyList.reset(new FloatKeyMap); + mKeyList = std::make_shared(); mKeyList->read(nif); } @@ -177,7 +177,7 @@ void NiPixelData::read(NIFStream *nif) void NiColorData::read(NIFStream *nif) { - mKeyMap.reset(new Vector4KeyMap); + mKeyMap = std::make_shared(); mKeyMap->read(nif); } @@ -231,7 +231,7 @@ void NiMorphData::read(NIFStream *nif) mMorphs.resize(morphCount); for(int i = 0;i < morphCount;i++) { - mMorphs[i].mKeyFrames.reset(new FloatKeyMap); + mMorphs[i].mKeyFrames = std::make_shared(); mMorphs[i].mKeyFrames->read(nif, true); nif->getVector3s(mMorphs[i].mVertices, vertCount); } @@ -239,22 +239,22 @@ void NiMorphData::read(NIFStream *nif) void NiKeyframeData::read(NIFStream *nif) { - mRotations.reset(new QuaternionKeyMap); + mRotations = std::make_shared(); mRotations->read(nif); if(mRotations->mInterpolationType == Vector3KeyMap::sXYZInterpolation) { //Chomp unused float nif->getFloat(); - mXRotations.reset(new FloatKeyMap); - mYRotations.reset(new FloatKeyMap); - mZRotations.reset(new FloatKeyMap); + mXRotations = std::make_shared(); + mYRotations = std::make_shared(); + mZRotations = std::make_shared(); mXRotations->read(nif, true); mYRotations->read(nif, true); mZRotations->read(nif, true); } - mTranslations.reset(new Vector3KeyMap); + mTranslations = std::make_shared(); mTranslations->read(nif); - mScales.reset(new FloatKeyMap); + mScales = std::make_shared(); mScales->read(nif); } From 614d5243c35aada206dcfd26a449e40f34dccca6 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 10 Mar 2019 15:48:12 +0300 Subject: [PATCH 024/106] Make NavMeshCacheItem consistent Move all logic related to this type into its methods. --- apps/openmw/mwrender/renderingmanager.cpp | 2 +- .../detournavigator/asyncnavmeshupdater.cpp | 2 +- components/detournavigator/makenavmesh.cpp | 121 +------------- components/detournavigator/makenavmesh.hpp | 15 -- components/detournavigator/navigator.hpp | 2 +- .../detournavigator/navmeshcacheitem.hpp | 151 ++++++++++++++++-- components/detournavigator/navmeshmanager.cpp | 2 +- 7 files changed, 150 insertions(+), 145 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c93707374..c17914b36 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1447,7 +1447,7 @@ namespace MWRender try { const auto locked = it->second->lockConst(); - mNavMesh->update(locked->getValue(), mNavMeshNumber, locked->getGeneration(), + mNavMesh->update(locked->getImpl(), mNavMeshNumber, locked->getGeneration(), locked->getNavMeshRevision(), mNavigator.getSettings()); } catch (const std::exception& e) diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index e9172f041..13600fe06 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -200,7 +200,7 @@ namespace DetourNavigator + "_" + std::to_string(job.mChangedTile.y()) + "_", recastMeshRevision); if (mSettings.get().mEnableWriteNavMeshToFile) if (const auto shared = job.mNavMeshCacheItem.lock()) - writeToFile(shared->lockConst()->getValue(), mSettings.get().mNavMeshPathPrefix, navMeshRevision); + writeToFile(shared->lockConst()->getImpl(), mSettings.get().mNavMeshPathPrefix, navMeshRevision); } std::chrono::steady_clock::time_point AsyncNavMeshUpdater::setFirstStart(const std::chrono::steady_clock::time_point& value) diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 5d2de481e..7caad9ec6 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -25,8 +25,6 @@ namespace { using namespace DetourNavigator; - static const int doNotTransferOwnership = 0; - void initPolyMeshDetail(rcPolyMeshDetail& value) { value.meshes = nullptr; @@ -441,56 +439,7 @@ namespace return NavMeshData(navMeshData, navMeshDataSize); } - class UpdateNavMeshStatusBuilder - { - public: - UpdateNavMeshStatusBuilder() = default; - - UpdateNavMeshStatusBuilder removed(bool value) - { - if (value) - set(UpdateNavMeshStatus::removed); - else - unset(UpdateNavMeshStatus::removed); - return *this; - } - - UpdateNavMeshStatusBuilder added(bool value) - { - if (value) - set(UpdateNavMeshStatus::added); - else - unset(UpdateNavMeshStatus::added); - return *this; - } - UpdateNavMeshStatusBuilder failed(bool value) - { - if (value) - set(UpdateNavMeshStatus::failed); - else - unset(UpdateNavMeshStatus::failed); - return *this; - } - - UpdateNavMeshStatus getResult() const - { - return mResult; - } - - private: - UpdateNavMeshStatus mResult = UpdateNavMeshStatus::ignored; - - void set(UpdateNavMeshStatus value) - { - mResult = static_cast(static_cast(mResult) | static_cast(value)); - } - - void unset(UpdateNavMeshStatus value) - { - mResult = static_cast(static_cast(mResult) & ~static_cast(value)); - } - }; template unsigned long getMinValuableBitsNumber(const T value) @@ -500,49 +449,6 @@ namespace ++power; return power; } - - dtStatus addTile(dtNavMesh& navMesh, const NavMeshData& navMeshData) - { - const dtTileRef lastRef = 0; - dtTileRef* const result = nullptr; - return navMesh.addTile(navMeshData.mValue.get(), navMeshData.mSize, - doNotTransferOwnership, lastRef, result); - } - - dtStatus addTile(dtNavMesh& navMesh, const NavMeshTilesCache::Value& cachedNavMeshData) - { - const dtTileRef lastRef = 0; - dtTileRef* const result = nullptr; - return navMesh.addTile(cachedNavMeshData.get().mValue, cachedNavMeshData.get().mSize, - doNotTransferOwnership, lastRef, result); - } - - template - UpdateNavMeshStatus replaceTile(const SharedNavMeshCacheItem& navMeshCacheItem, - const TilePosition& changedTile, T&& navMeshData) - { - const auto locked = navMeshCacheItem->lock(); - auto& navMesh = locked->getValue(); - const int layer = 0; - const auto tileRef = navMesh.getTileRefAt(changedTile.x(), changedTile.y(), layer); - unsigned char** const data = nullptr; - int* const dataSize = nullptr; - const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, data, dataSize)); - const auto addStatus = addTile(navMesh, navMeshData); - - if (dtStatusSucceed(addStatus)) - { - locked->setUsedTile(changedTile, std::forward(navMeshData)); - return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult(); - } - else - { - if (removed) - locked->removeUsedTile(changedTile); - log("failed to add tile with status=", WriteDtStatus {addStatus}); - return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult(); - } - } } namespace DetourNavigator @@ -591,26 +497,13 @@ namespace DetourNavigator " playerTile=", playerTile, " changedTileDistance=", getDistance(changedTile, playerTile)); - const auto params = *navMeshCacheItem->lockConst()->getValue().getParams(); + const auto params = *navMeshCacheItem->lockConst()->getImpl().getParams(); const osg::Vec3f origin(params.orig[0], params.orig[1], params.orig[2]); - const auto x = changedTile.x(); - const auto y = changedTile.y(); - - const auto removeTile = [&] { - const auto locked = navMeshCacheItem->lock(); - auto& navMesh = locked->getValue(); - const auto tileRef = navMesh.getTileRefAt(x, y, 0); - const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, nullptr, nullptr)); - if (removed) - locked->removeUsedTile(changedTile); - return UpdateNavMeshStatusBuilder().removed(removed).getResult(); - }; - if (!recastMesh) { log("ignore add tile: recastMesh is null"); - return removeTile(); + return navMeshCacheItem->lock()->removeTile(changedTile); } auto recastMeshBounds = recastMesh->getBounds(); @@ -625,13 +518,13 @@ namespace DetourNavigator if (isEmpty(recastMeshBounds)) { log("ignore add tile: recastMesh is empty"); - return removeTile(); + return navMeshCacheItem->lock()->removeTile(changedTile); } if (!shouldAddTile(changedTile, playerTile, std::min(settings.mMaxTilesNumber, params.maxTiles))) { log("ignore add tile: too far from player"); - return removeTile(); + return navMeshCacheItem->lock()->removeTile(changedTile); } auto cachedNavMeshData = navMeshTilesCache.get(agentHalfExtents, changedTile, *recastMesh, offMeshConnections); @@ -648,7 +541,7 @@ namespace DetourNavigator if (!navMeshData.mValue) { log("ignore add tile: NavMeshData is null"); - return removeTile(); + return navMeshCacheItem->lock()->removeTile(changedTile); } try @@ -665,10 +558,10 @@ namespace DetourNavigator if (!cachedNavMeshData) { log("cache overflow"); - return replaceTile(navMeshCacheItem, changedTile, std::move(navMeshData)); + return navMeshCacheItem->lock()->updateTile(changedTile, std::move(navMeshData)); } } - return replaceTile(navMeshCacheItem, changedTile, std::move(cachedNavMeshData)); + return navMeshCacheItem->lock()->updateTile(changedTile, std::move(cachedNavMeshData)); } } diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index 1dfa242ee..f9cf68a73 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -20,21 +20,6 @@ namespace DetourNavigator class RecastMesh; struct Settings; - enum class UpdateNavMeshStatus : unsigned - { - ignored = 0, - removed = 1 << 0, - added = 1 << 1, - replaced = removed | added, - failed = 1 << 2, - lost = removed | failed, - }; - - inline bool isSuccess(UpdateNavMeshStatus value) - { - return (static_cast(value) & static_cast(UpdateNavMeshStatus::failed)) == 0; - } - inline float getLength(const osg::Vec2i& value) { return std::sqrt(float(osg::square(value.x()) + osg::square(value.y()))); diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index cc62d6164..d277fa49e 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -174,7 +174,7 @@ namespace DetourNavigator if (!navMesh) return out; const auto settings = getSettings(); - return findSmoothPath(navMesh->lockConst()->getValue(), toNavMeshCoordinates(settings, agentHalfExtents), + return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start), toNavMeshCoordinates(settings, end), includeFlags, settings, out); } diff --git a/components/detournavigator/navmeshcacheitem.hpp b/components/detournavigator/navmeshcacheitem.hpp index e6ca60ef7..f13341397 100644 --- a/components/detournavigator/navmeshcacheitem.hpp +++ b/components/detournavigator/navmeshcacheitem.hpp @@ -4,29 +4,113 @@ #include "sharednavmesh.hpp" #include "tileposition.hpp" #include "navmeshtilescache.hpp" +#include "dtstatus.hpp" #include +#include + #include namespace DetourNavigator { - class NavMeshCacheItem + enum class UpdateNavMeshStatus : unsigned + { + ignored = 0, + removed = 1 << 0, + added = 1 << 1, + replaced = removed | added, + failed = 1 << 2, + lost = removed | failed, + }; + + inline bool isSuccess(UpdateNavMeshStatus value) + { + return (static_cast(value) & static_cast(UpdateNavMeshStatus::failed)) == 0; + } + + class UpdateNavMeshStatusBuilder { public: - NavMeshCacheItem(const NavMeshPtr& value, std::size_t generation) - : mValue(value), mGeneration(generation), mNavMeshRevision(0) + UpdateNavMeshStatusBuilder() = default; + + UpdateNavMeshStatusBuilder removed(bool value) { + if (value) + set(UpdateNavMeshStatus::removed); + else + unset(UpdateNavMeshStatus::removed); + return *this; } - const dtNavMesh& getValue() const + UpdateNavMeshStatusBuilder added(bool value) { - return *mValue; + if (value) + set(UpdateNavMeshStatus::added); + else + unset(UpdateNavMeshStatus::added); + return *this; } - dtNavMesh& getValue() + UpdateNavMeshStatusBuilder failed(bool value) { - return *mValue; + if (value) + set(UpdateNavMeshStatus::failed); + else + unset(UpdateNavMeshStatus::failed); + return *this; + } + + UpdateNavMeshStatus getResult() const + { + return mResult; + } + + private: + UpdateNavMeshStatus mResult = UpdateNavMeshStatus::ignored; + + void set(UpdateNavMeshStatus value) + { + mResult = static_cast(static_cast(mResult) | static_cast(value)); + } + + void unset(UpdateNavMeshStatus value) + { + mResult = static_cast(static_cast(mResult) & ~static_cast(value)); + } + }; + + inline unsigned char* getRawData(NavMeshData& navMeshData) + { + return navMeshData.mValue.get(); + } + + inline unsigned char* getRawData(NavMeshTilesCache::Value& cachedNavMeshData) + { + return cachedNavMeshData.get().mValue; + } + + inline int getSize(const NavMeshData& navMeshData) + { + return navMeshData.mSize; + } + + inline int getSize(const NavMeshTilesCache::Value& cachedNavMeshData) + { + return cachedNavMeshData.get().mSize; + } + + class NavMeshCacheItem + { + public: + NavMeshCacheItem(const NavMeshPtr& impl, std::size_t generation) + : mImpl(impl), mGeneration(generation), mNavMeshRevision(0) + { + } + + const dtNavMesh& getImpl() const + { + return *mImpl; } std::size_t getGeneration() const @@ -39,6 +123,38 @@ namespace DetourNavigator return mNavMeshRevision; } + template + UpdateNavMeshStatus updateTile(const TilePosition& position, T&& navMeshData) + { + const auto removed = removeTileImpl(position); + const auto addStatus = addTileImpl(getRawData(navMeshData), getSize(navMeshData)); + if (dtStatusSucceed(addStatus)) + { + setUsedTile(position, std::forward(navMeshData)); + return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult(); + } + else + { + if (removed) + removeUsedTile(position); + return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult(); + } + } + + UpdateNavMeshStatus removeTile(const TilePosition& position) + { + const auto removed = dtStatusSucceed(removeTileImpl(position)); + if (removed) + removeUsedTile(position); + return UpdateNavMeshStatusBuilder().removed(removed).getResult(); + } + + private: + NavMeshPtr mImpl; + std::size_t mGeneration; + std::size_t mNavMeshRevision; + std::map> mUsedTiles; + void setUsedTile(const TilePosition& tilePosition, NavMeshTilesCache::Value value) { mUsedTiles[tilePosition] = std::make_pair(std::move(value), NavMeshData()); @@ -57,11 +173,22 @@ namespace DetourNavigator ++mNavMeshRevision; } - private: - NavMeshPtr mValue; - std::size_t mGeneration; - std::size_t mNavMeshRevision; - std::map> mUsedTiles; + dtStatus addTileImpl(unsigned char* data, int size) + { + const int doNotTransferOwnership = 0; + const dtTileRef lastRef = 0; + dtTileRef* const result = nullptr; + return mImpl->addTile(data, size, doNotTransferOwnership, lastRef, result); + } + + dtStatus removeTileImpl(const TilePosition& position) + { + const int layer = 0; + const auto tileRef = mImpl->getTileRefAt(position.x(), position.y(), layer); + unsigned char** const data = nullptr; + int* const dataSize = nullptr; + return mImpl->removeTile(tileRef, data, dataSize); + } }; using GuardedNavMeshCacheItem = Misc::ScopeGuarded; diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 03a8b055f..aca159401 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -160,7 +160,7 @@ namespace DetourNavigator const auto changedTiles = mChangedTiles.find(agentHalfExtents); { const auto locked = cached->lockConst(); - const auto& navMesh = locked->getValue(); + const auto& navMesh = locked->getImpl(); if (changedTiles != mChangedTiles.end()) { for (const auto& tile : changedTiles->second) From 9d61c494789749f1b3cda43f008920af6bf2177a Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 10 Mar 2019 20:28:33 +0300 Subject: [PATCH 025/106] Store key by reference in tiles map --- components/detournavigator/navmeshtilescache.cpp | 10 +++++----- components/detournavigator/navmeshtilescache.hpp | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index 418e69e82..51450cdbd 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -85,7 +85,7 @@ namespace DetourNavigator if (navMeshSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize)) return Value(); - const auto navMeshKey = makeNavMeshKey(recastMesh, offMeshConnections); + auto navMeshKey = makeNavMeshKey(recastMesh, offMeshConnections); const auto itemSize = navMeshSize + 2 * navMeshKey.size(); if (itemSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize)) @@ -94,9 +94,8 @@ namespace DetourNavigator while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize) removeLeastRecentlyUsed(); - const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, navMeshKey); - // TODO: use std::string_view or some alternative to avoid navMeshKey copy into both mFreeItems and mValues - const auto emplaced = mValues[agentHalfExtents][changedTile].mMap.emplace(navMeshKey, iterator); + const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(navMeshKey)); + const auto emplaced = mValues[agentHalfExtents][changedTile].mMap.emplace(iterator->mNavMeshKey, iterator); if (!emplaced.second) { @@ -131,9 +130,10 @@ namespace DetourNavigator mUsedNavMeshDataSize -= getSize(item); mFreeNavMeshDataSize -= getSize(item); - mFreeItems.pop_back(); tileValues->second.mMap.erase(value); + mFreeItems.pop_back(); + if (!tileValues->second.mMap.empty()) return; diff --git a/components/detournavigator/navmeshtilescache.hpp b/components/detournavigator/navmeshtilescache.hpp index e244cda9d..6c57f7563 100644 --- a/components/detournavigator/navmeshtilescache.hpp +++ b/components/detournavigator/navmeshtilescache.hpp @@ -105,10 +105,24 @@ namespace DetourNavigator NavMeshData&& value); private: + class KeyView + { + public: + KeyView(const std::string& value) + : mValue(value) {} + + friend bool operator <(const KeyView& lhs, const KeyView& rhs) + { + return lhs.mValue.get() < rhs.mValue.get(); + } + + private: + std::reference_wrapper mValue; + }; struct TileMap { - std::map mMap; + std::map mMap; }; std::mutex mMutex; From 518e34b403bb907410579fd136106787b4b25597 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 10 Mar 2019 14:24:03 +0300 Subject: [PATCH 026/106] Remove useless variables --- apps/openmw/mwworld/scene.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 6655bef4b..8cccb2fc5 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -338,7 +338,7 @@ namespace MWWorld { if (const auto object = mPhysics->getObject(ptr)) navigator->removeObject(DetourNavigator::ObjectId(object)); - else if (const auto actor = mPhysics->getActor(ptr)) + else if (mPhysics->getActor(ptr)) { navigator->removeAgent(world->getPathfindingHalfExtents(ptr)); mRendering.removeActorPath(ptr); @@ -809,7 +809,7 @@ namespace MWWorld const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); navigator->update(player.getRefData().getPosition().asVec3()); } - else if (const auto actor = mPhysics->getActor(ptr)) + else if (mPhysics->getActor(ptr)) { navigator->removeAgent(MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(ptr)); } From ce9aebcba14965ddca14801488e83810a89f3a0e Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 Feb 2019 00:52:20 +0300 Subject: [PATCH 027/106] Clear all changed tiles after post --- components/detournavigator/navmeshmanager.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 03a8b055f..c9af54593 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -172,10 +172,6 @@ namespace DetourNavigator else tileToPost->second = addChangeType(tileToPost->second, tile.second); } - for (const auto& tile : tilesToPost) - changedTiles->second.erase(tile.first); - if (changedTiles->second.empty()) - mChangedTiles.erase(changedTiles); } const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles); mRecastMeshManager.forEachTilePosition([&] (const TilePosition& tile) @@ -191,6 +187,8 @@ namespace DetourNavigator }); } mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, tilesToPost); + if (changedTiles != mChangedTiles.end()) + changedTiles->second.clear(); log("cache update posted for agent=", agentHalfExtents, " playerTile=", lastPlayerTile->second, " recastMeshManagerRevision=", lastRevision); From c05fc9e054dbf2cae27993aef421a03150ac0605 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 18 Feb 2019 01:29:11 +0300 Subject: [PATCH 028/106] Add missing cleanup --- components/detournavigator/navmeshmanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index c9af54593..9723366f9 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -105,6 +105,9 @@ namespace DetourNavigator if (!resetIfUnique(it->second)) return false; mCache.erase(agentHalfExtents); + mChangedTiles.erase(agentHalfExtents); + mPlayerTile.erase(agentHalfExtents); + mLastRecastMeshManagerRevision.erase(agentHalfExtents); return true; } From ece111d05a238b4b49d810eef878298d6665156d Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 19 Feb 2019 03:05:15 +0300 Subject: [PATCH 029/106] Check for jobs using predicate --- components/detournavigator/asyncnavmeshupdater.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index e9172f041..bb2605a5b 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -162,9 +162,7 @@ namespace DetourNavigator boost::optional AsyncNavMeshUpdater::getNextJob() { std::unique_lock lock(mMutex); - if (mJobs.empty()) - mHasJob.wait_for(lock, std::chrono::milliseconds(10)); - if (mJobs.empty()) + if (!mHasJob.wait_for(lock, std::chrono::milliseconds(10), [&] { return !mJobs.empty(); })) { mFirstStart.lock()->reset(); mDone.notify_all(); From 8d2af94b75fb9c893270ef9df27f6a29ecf6e23b Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 19 Feb 2019 11:46:00 +0300 Subject: [PATCH 030/106] Use default objects for NavigatorStub methods result --- components/detournavigator/navigator.hpp | 2 +- components/detournavigator/navigatorimpl.cpp | 2 +- components/detournavigator/navigatorimpl.hpp | 2 +- components/detournavigator/navigatorstub.hpp | 13 +++++++++---- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index cc62d6164..baf56145b 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -191,7 +191,7 @@ namespace DetourNavigator */ virtual std::map getNavMeshes() const = 0; - virtual Settings getSettings() const = 0; + virtual const Settings& getSettings() const = 0; }; } diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 2e16b6d04..a62220e55 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -133,7 +133,7 @@ namespace DetourNavigator return mNavMeshManager.getNavMeshes(); } - Settings NavigatorImpl::getSettings() const + const Settings& NavigatorImpl::getSettings() const { return mSettings; } diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 8156f6655..d2ff8a57b 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -46,7 +46,7 @@ namespace DetourNavigator std::map getNavMeshes() const override; - Settings getSettings() const override; + const Settings& getSettings() const override; private: Settings mSettings; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index 5d82d2f59..cab93b105 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -5,8 +5,9 @@ namespace DetourNavigator { - struct NavigatorStub final : public Navigator + class NavigatorStub final : public Navigator { + public: NavigatorStub() = default; void addAgent(const osg::Vec3f& /*agentHalfExtents*/) override {} @@ -65,7 +66,7 @@ namespace DetourNavigator SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override { - return SharedNavMeshCacheItem(); + return mEmptyNavMeshCacheItem; } std::map getNavMeshes() const override @@ -73,10 +74,14 @@ namespace DetourNavigator return std::map(); } - Settings getSettings() const override + const Settings& getSettings() const override { - return Settings {}; + return mDefaultSettings; } + + private: + Settings mDefaultSettings {}; + SharedNavMeshCacheItem mEmptyNavMeshCacheItem; }; } From 849f2078c134f670fce9a95318f508fe7bcc380e Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 22 Feb 2019 23:52:00 +0300 Subject: [PATCH 031/106] Swap outside critical section --- components/detournavigator/tilecachedrecastmeshmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index b878c2d3e..0cb1cfb80 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -61,8 +61,8 @@ namespace DetourNavigator for (const auto& tile : currentTiles) if (!newTiles.count(tile) && removeTile(id, tile, tiles.get())) changedTiles.push_back(tile); - std::swap(currentTiles, newTiles); } + std::swap(currentTiles, newTiles); if (!changedTiles.empty()) ++mRevision; return changedTiles; From 4395a92c354cf9ff2a1516df1ab4545588f78e12 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 10 Mar 2019 19:08:30 +0300 Subject: [PATCH 032/106] Use display list to render navmesh Slightly improves performance of massive navmesh rendering. --- components/sceneutil/detourdebugdraw.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sceneutil/detourdebugdraw.cpp b/components/sceneutil/detourdebugdraw.cpp index a0ae67dc2..046bbb08c 100644 --- a/components/sceneutil/detourdebugdraw.cpp +++ b/components/sceneutil/detourdebugdraw.cpp @@ -102,7 +102,7 @@ namespace SceneUtil osg::ref_ptr geometry(new osg::Geometry); geometry->setStateSet(stateSet); - geometry->setUseDisplayList(false); + geometry->setUseDisplayList(true); geometry->setVertexArray(mVertices); geometry->setColorArray(mColors, osg::Array::BIND_PER_VERTEX); geometry->addPrimitiveSet(new osg::DrawArrays(mMode, 0, static_cast(mVertices->size()))); From 68948bc847f51a314b873bc41c60a731e7fe61b5 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 10 Mar 2019 22:04:41 +0300 Subject: [PATCH 033/106] Avoid key allocation to find tile in cache --- .../detournavigator/navmeshtilescache.cpp | 59 ++++++++++++++++++- .../detournavigator/navmeshtilescache.hpp | 45 +++++++++++++- 2 files changed, 99 insertions(+), 5 deletions(-) diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index 51450cdbd..76060981f 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -1,6 +1,8 @@ #include "navmeshtilescache.hpp" #include "exceptions.hpp" +#include + namespace DetourNavigator { namespace @@ -61,8 +63,7 @@ namespace DetourNavigator if (tileValues == agentValues->second.end()) return Value(); - // TODO: use different function to make key to avoid unnecessary std::string allocation - const auto tile = tileValues->second.mMap.find(makeNavMeshKey(recastMesh, offMeshConnections)); + const auto tile = tileValues->second.mMap.find(RecastMeshKeyView(recastMesh, offMeshConnections)); if (tile == tileValues->second.mMap.end()) return Value(); @@ -163,4 +164,58 @@ namespace DetourNavigator mFreeItems.splice(mFreeItems.begin(), mBusyItems, iterator); mFreeNavMeshDataSize += getSize(*iterator); } + + namespace + { + struct CompareBytes + { + const char* mRhsIt; + const char* mRhsEnd; + + template + int operator ()(const std::vector& lhs) + { + const auto lhsBegin = reinterpret_cast(lhs.data()); + const auto lhsEnd = reinterpret_cast(lhs.data() + lhs.size()); + const auto lhsSize = static_cast(lhsEnd - lhsBegin); + const auto rhsSize = static_cast(mRhsEnd - mRhsIt); + const auto size = std::min(lhsSize, rhsSize); + + if (const auto result = std::memcmp(lhsBegin, mRhsIt, size)) + return result; + + if (lhsSize > rhsSize) + return 1; + + mRhsIt += size; + + return 0; + } + }; + } + + int NavMeshTilesCache::RecastMeshKeyView::compare(const std::string& other) const + { + CompareBytes compareBytes {other.data(), other.data() + other.size()}; + + if (const auto result = compareBytes(mRecastMesh.get().getIndices())) + return result; + + if (const auto result = compareBytes(mRecastMesh.get().getVertices())) + return result; + + if (const auto result = compareBytes(mRecastMesh.get().getAreaTypes())) + return result; + + if (const auto result = compareBytes(mRecastMesh.get().getWater())) + return result; + + if (const auto result = compareBytes(mOffMeshConnections.get())) + return result; + + if (compareBytes.mRhsIt < compareBytes.mRhsEnd) + return -1; + + return 0; + } } diff --git a/components/detournavigator/navmeshtilescache.hpp b/components/detournavigator/navmeshtilescache.hpp index 6c57f7563..3f2e0cc01 100644 --- a/components/detournavigator/navmeshtilescache.hpp +++ b/components/detournavigator/navmeshtilescache.hpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace DetourNavigator { @@ -108,16 +109,54 @@ namespace DetourNavigator class KeyView { public: + KeyView() = default; + KeyView(const std::string& value) - : mValue(value) {} + : mValue(&value) {} + + const std::string& getValue() const + { + assert(mValue); + return *mValue; + } + + virtual int compare(const std::string& other) const + { + assert(mValue); + return mValue->compare(other); + } + + virtual bool isLess(const KeyView& other) const + { + assert(mValue); + return other.compare(*mValue) > 0; + } friend bool operator <(const KeyView& lhs, const KeyView& rhs) { - return lhs.mValue.get() < rhs.mValue.get(); + return lhs.isLess(rhs); + } + + private: + const std::string* mValue = nullptr; + }; + + class RecastMeshKeyView : public KeyView + { + public: + RecastMeshKeyView(const RecastMesh& recastMesh, const std::vector& offMeshConnections) + : mRecastMesh(recastMesh), mOffMeshConnections(offMeshConnections) {} + + int compare(const std::string& other) const override; + + bool isLess(const KeyView& other) const override + { + return compare(other.getValue()) < 0; } private: - std::reference_wrapper mValue; + std::reference_wrapper mRecastMesh; + std::reference_wrapper> mOffMeshConnections; }; struct TileMap From 943279abbbcd31c111af1bb8963a2c212bbbd340 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 11 Mar 2019 15:05:43 +0400 Subject: [PATCH 034/106] Consider land texture with given ID and index as override for base texture with the same ID and index (bug #4736) --- CHANGELOG.md | 1 + apps/openmw/mwworld/store.cpp | 15 +++++++++++++++ components/esm/loadltex.hpp | 15 +++------------ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d14b45e90..3a1a3396e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Bug #4720: Inventory avatar has shield with two-handed weapon during [un]equipping animation Bug #4723: ResetActors command works incorrectly Bug #4745: Editor: Interior cell lighting field values are not displayed as colors + Bug #4736: LandTexture records overrides do not work Bug #4746: Non-solid player can't run or sneak Bug #4747: Bones are not read from X.NIF file for NPC animation Bug #4750: Sneaking doesn't work in first person view if the player is in attack ready state diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 79d9174ed..2a0c39466 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -387,6 +387,21 @@ namespace MWWorld assert(plugin < mStatic.size()); + // Replace texture for records with given ID and index from all plugins. + for (unsigned int i=0; i(search(lt.mIndex, i)); + if (tex) + { + const std::string texId = Misc::StringUtils::lowerCase(tex->mId); + const std::string ltId = Misc::StringUtils::lowerCase(lt.mId); + if (texId == ltId) + { + tex->mTexture = lt.mTexture; + } + } + } + LandTextureList <exl = mStatic[plugin]; if(lt.mIndex + 1 > (int)ltexl.size()) ltexl.resize(lt.mIndex+1); diff --git a/components/esm/loadltex.hpp b/components/esm/loadltex.hpp index 1604e9281..e3e258246 100644 --- a/components/esm/loadltex.hpp +++ b/components/esm/loadltex.hpp @@ -11,18 +11,9 @@ class ESMWriter; /* * Texture used for texturing landscape. - * - * They are probably indexed by 'num', not 'id', but I don't know for - * sure. And num is not unique between files, so one option is to keep - * a separate list for each input file (that has LTEX records, of - * course.) We also need to resolve references to already existing - * land textures to save space. - - * I'm not sure if it is even possible to override existing land - * textures, probably not. I'll have to try it, and have to mimic the - * behaviour of morrowind. First, check what you are allowed to do in - * the editor. Then make an esp which changes a commonly used land - * texture, and see if it affects the game. + * They are indexed by 'num', but still use 'id' to override base records. + * Original editor even does not allow to create new records with existing ID's. + * TODO: currently OpenMW-CS does not allow to override LTEX records at all. */ struct LandTexture From 8e09468f45d45a77787096151ee81b43f4ae1999 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 12 Mar 2019 00:05:55 +0300 Subject: [PATCH 035/106] Don't set display list usage for navmesh --- components/sceneutil/detourdebugdraw.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/sceneutil/detourdebugdraw.cpp b/components/sceneutil/detourdebugdraw.cpp index 046bbb08c..ea8a4c2f6 100644 --- a/components/sceneutil/detourdebugdraw.cpp +++ b/components/sceneutil/detourdebugdraw.cpp @@ -102,7 +102,6 @@ namespace SceneUtil osg::ref_ptr geometry(new osg::Geometry); geometry->setStateSet(stateSet); - geometry->setUseDisplayList(true); geometry->setVertexArray(mVertices); geometry->setColorArray(mColors, osg::Array::BIND_PER_VERTEX); geometry->addPrimitiveSet(new osg::DrawArrays(mMode, 0, static_cast(mVertices->size()))); From 4aa21b908811f18d0924e5de08f4028e1e5d0f46 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 13 Mar 2019 14:25:55 +0400 Subject: [PATCH 036/106] Do not swap buffers for non-exposed windows (bug #4911) --- CHANGELOG.md | 1 + extern/osgQt/GraphicsWindowQt.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a1a3396e..bbdcd2e63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Bug #4876: AI ratings handling inconsistencies Bug #4877: Startup script executes only on a new game start Bug #4888: Global variable stray explicit reference calls break script compilation + Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5 Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Feature #3610: Option to invert X axis diff --git a/extern/osgQt/GraphicsWindowQt.cpp b/extern/osgQt/GraphicsWindowQt.cpp index 9e5f6e55e..e46cbba2d 100644 --- a/extern/osgQt/GraphicsWindowQt.cpp +++ b/extern/osgQt/GraphicsWindowQt.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #if (QT_VERSION>=QT_VERSION_CHECK(4, 6, 0)) # define USE_GESTURES @@ -518,6 +519,12 @@ bool GraphicsWindowQt::releaseContextImplementation() void GraphicsWindowQt::swapBuffersImplementation() { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + // QOpenGLContext complains if we swap on an non-exposed QWindow + if (!_widget || !_widget->windowHandle()->isExposed()) + return; +#endif + _widget->swapBuffers(); // FIXME: the processDeferredEvents should really be executed in a GUI (main) thread context but From 46fee678a78a933a985a00fea3448f0d8bedda5d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 13 Mar 2019 20:00:06 +0400 Subject: [PATCH 037/106] Place QWindow include under Qt version check --- extern/osgQt/GraphicsWindowQt.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extern/osgQt/GraphicsWindowQt.cpp b/extern/osgQt/GraphicsWindowQt.cpp index e46cbba2d..0135a1784 100644 --- a/extern/osgQt/GraphicsWindowQt.cpp +++ b/extern/osgQt/GraphicsWindowQt.cpp @@ -17,7 +17,10 @@ #include #include #include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) #include +#endif #if (QT_VERSION>=QT_VERSION_CHECK(4, 6, 0)) # define USE_GESTURES From 2ed05a519571fd046261f2ccf4cefe68b03b5373 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 13 Mar 2019 11:15:58 +0400 Subject: [PATCH 038/106] Implement generic caching system --- apps/openmw/mwrender/landmanager.cpp | 9 +- apps/openmw/mwrender/landmanager.hpp | 3 +- components/resource/objectcache.cpp | 165 ------------------------ components/resource/objectcache.hpp | 130 ++++++++++++++++--- components/resource/resourcemanager.cpp | 46 ------- components/resource/resourcemanager.hpp | 52 ++++++-- components/resource/resourcesystem.cpp | 16 +-- components/resource/resourcesystem.hpp | 8 +- components/terrain/chunkmanager.cpp | 15 +-- components/terrain/chunkmanager.hpp | 10 +- 10 files changed, 186 insertions(+), 268 deletions(-) delete mode 100644 components/resource/objectcache.cpp delete mode 100644 components/resource/resourcemanager.cpp diff --git a/apps/openmw/mwrender/landmanager.cpp b/apps/openmw/mwrender/landmanager.cpp index e3b19ca47..560c1ba72 100644 --- a/apps/openmw/mwrender/landmanager.cpp +++ b/apps/openmw/mwrender/landmanager.cpp @@ -12,16 +12,15 @@ namespace MWRender { LandManager::LandManager(int loadFlags) - : ResourceManager(nullptr) + : GenericResourceManager >(nullptr) , mLoadFlags(loadFlags) { + mCache = new CacheType; } osg::ref_ptr LandManager::getLand(int x, int y) { - std::string idstr = std::to_string(x) + " " + std::to_string(y); - - osg::ref_ptr obj = mCache->getRefFromObjectCache(idstr); + osg::ref_ptr obj = mCache->getRefFromObjectCache(std::make_pair(x,y)); if (obj) return static_cast(obj.get()); else @@ -30,7 +29,7 @@ osg::ref_ptr LandManager::getLand(int x, int y) if (!land) return nullptr; osg::ref_ptr landObj (new ESMTerrain::LandObject(land, mLoadFlags)); - mCache->addEntryToObjectCache(idstr, landObj.get()); + mCache->addEntryToObjectCache(std::make_pair(x,y), landObj.get()); return landObj; } } diff --git a/apps/openmw/mwrender/landmanager.hpp b/apps/openmw/mwrender/landmanager.hpp index 81253bba3..0d37097d8 100644 --- a/apps/openmw/mwrender/landmanager.hpp +++ b/apps/openmw/mwrender/landmanager.hpp @@ -3,6 +3,7 @@ #include +#include #include #include @@ -14,7 +15,7 @@ namespace ESM namespace MWRender { - class LandManager : public Resource::ResourceManager + class LandManager : public Resource::GenericResourceManager > { public: LandManager(int loadFlags); diff --git a/components/resource/objectcache.cpp b/components/resource/objectcache.cpp deleted file mode 100644 index ffe150c74..000000000 --- a/components/resource/objectcache.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield - * - * This library is open source and may be redistributed and/or modified under - * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or - * (at your option) any later version. The full license is in LICENSE file - * included with this distribution, and on the openscenegraph.org website. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * OpenSceneGraph Public License for more details. -*/ - -#include "objectcache.hpp" - -#include -#include - -namespace Resource -{ - -//////////////////////////////////////////////////////////////////////////////////////////// -// -// ObjectCache -// -ObjectCache::ObjectCache(): - osg::Referenced(true) -{ -} - -ObjectCache::~ObjectCache() -{ -} - -void ObjectCache::addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp) -{ - if (!object) - { - OSG_ALWAYS << " trying to add NULL object to cache for " << filename << std::endl; - return; - } - OpenThreads::ScopedLock lock(_objectCacheMutex); - _objectCache[filename]=ObjectTimeStampPair(object,timestamp); -} - -osg::ref_ptr ObjectCache::getRefFromObjectCache(const std::string& fileName) -{ - OpenThreads::ScopedLock lock(_objectCacheMutex); - ObjectCacheMap::iterator itr = _objectCache.find(fileName); - if (itr!=_objectCache.end()) - { - return itr->second.first; - } - 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); - - // look for objects with external references and update their time stamp. - for(ObjectCacheMap::iterator itr=_objectCache.begin(); - itr!=_objectCache.end(); - ++itr) - { - // If ref count is greater than 1, the object has an external reference. - // If the timestamp is yet to be initialized, it needs to be updated too. - if (itr->second.first->referenceCount()>1 || itr->second.second == 0.0) - { - // So update it. - itr->second.second = referenceTime; - } - } -} - -void ObjectCache::removeExpiredObjectsInCache(double expiryTime) -{ - std::vector > objectsToRemove; - - { - OpenThreads::ScopedLock lock(_objectCacheMutex); - - // Remove expired entries from object cache - ObjectCacheMap::iterator oitr = _objectCache.begin(); - while(oitr != _objectCache.end()) - { - if (oitr->second.second<=expiryTime) - { - objectsToRemove.push_back(oitr->second.first); - _objectCache.erase(oitr++); - } - else - { - ++oitr; - } - } - } - - // note, actual unref happens outside of the lock - objectsToRemove.clear(); -} - -void ObjectCache::removeFromObjectCache(const std::string& fileName) -{ - OpenThreads::ScopedLock lock(_objectCacheMutex); - ObjectCacheMap::iterator itr = _objectCache.find(fileName); - if (itr!=_objectCache.end()) _objectCache.erase(itr); -} - -void ObjectCache::clear() -{ - OpenThreads::ScopedLock lock(_objectCacheMutex); - _objectCache.clear(); -} - -void ObjectCache::releaseGLObjects(osg::State* state) -{ - OpenThreads::ScopedLock lock(_objectCacheMutex); - - for(ObjectCacheMap::iterator itr = _objectCache.begin(); - itr != _objectCache.end(); - ++itr) - { - osg::Object* object = itr->second.first.get(); - object->releaseGLObjects(state); - } -} - -void ObjectCache::accept(osg::NodeVisitor &nv) -{ - OpenThreads::ScopedLock lock(_objectCacheMutex); - - for(ObjectCacheMap::iterator itr = _objectCache.begin(); - itr != _objectCache.end(); - ++itr) - { - osg::Object* object = itr->second.first.get(); - if (object) - { - osg::Node* node = dynamic_cast(object); - if (node) - node->accept(nv); - } - } -} - -unsigned int ObjectCache::getCacheSize() const -{ - OpenThreads::ScopedLock lock(_objectCacheMutex); - return _objectCache.size(); -} - -} diff --git a/components/resource/objectcache.hpp b/components/resource/objectcache.hpp index 72db835e8..cfd41f19c 100644 --- a/components/resource/objectcache.hpp +++ b/components/resource/objectcache.hpp @@ -1,5 +1,8 @@ // Resource ObjectCache for OpenMW, forked from osgDB ObjectCache by Robert Osfield, see copyright notice below. -// The main change from the upstream version is that removeExpiredObjectsInCache no longer keeps a lock while the unref happens. +// Changes: +// - removeExpiredObjectsInCache no longer keeps a lock while the unref happens. +// - template allows customized KeyType. +// - objects with uninitialized time stamp are not removed. /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * @@ -19,6 +22,7 @@ #include #include +#include #include #include @@ -32,11 +36,13 @@ namespace osg namespace Resource { -class ObjectCache : public osg::Referenced +template +class GenericObjectCache : public osg::Referenced { public: - ObjectCache(); + GenericObjectCache() + : osg::Referenced(true) {} /** For each object in the cache which has an reference count greater than 1 * (and therefore referenced by elsewhere in the application) set the time stamp @@ -44,59 +50,149 @@ class ObjectCache : public osg::Referenced * This would typically be called once per frame by applications which are doing database paging, * and need to prune objects that are no longer required. * The time used should be taken from the FrameStamp::getReferenceTime().*/ - void updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime); + void updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime) + { + // look for objects with external references and update their time stamp. + OpenThreads::ScopedLock lock(_objectCacheMutex); + for(typename ObjectCacheMap::iterator itr=_objectCache.begin(); itr!=_objectCache.end(); ++itr) + { + // If ref count is greater than 1, the object has an external reference. + // If the timestamp is yet to be initialized, it needs to be updated too. + if (itr->second.first->referenceCount()>1 || itr->second.second == 0.0) + itr->second.second = referenceTime; + } + } /** Removed object in the cache which have a time stamp at or before the specified expiry time. * This would typically be called once per frame by applications which are doing database paging, * and need to prune objects that are no longer required, and called after the a called * after the call to updateTimeStampOfObjectsInCacheWithExternalReferences(expirtyTime).*/ - void removeExpiredObjectsInCache(double expiryTime); + void removeExpiredObjectsInCache(double expiryTime) + { + std::vector > objectsToRemove; + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + // Remove expired entries from object cache + typename ObjectCacheMap::iterator oitr = _objectCache.begin(); + while(oitr != _objectCache.end()) + { + if (oitr->second.second<=expiryTime) + { + objectsToRemove.push_back(oitr->second.first); + _objectCache.erase(oitr++); + } + else + ++oitr; + } + } + // note, actual unref happens outside of the lock + objectsToRemove.clear(); + } /** Remove all objects in the cache regardless of having external references or expiry times.*/ - void clear(); + void clear() + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + _objectCache.clear(); + } - /** Add a filename,object,timestamp triple to the Registry::ObjectCache.*/ - void addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp = 0.0); + /** Add a key,object,timestamp triple to the Registry::ObjectCache.*/ + void addEntryToObjectCache(const KeyType& key, osg::Object* object, double timestamp = 0.0) + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + _objectCache[key]=ObjectTimeStampPair(object,timestamp); + } /** Remove Object from cache.*/ - void removeFromObjectCache(const std::string& fileName); + void removeFromObjectCache(const KeyType& key) + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + typename ObjectCacheMap::iterator itr = _objectCache.find(key); + if (itr!=_objectCache.end()) _objectCache.erase(itr); + } /** Get an ref_ptr from the object cache*/ - osg::ref_ptr getRefFromObjectCache(const std::string& fileName); + osg::ref_ptr getRefFromObjectCache(const KeyType& key) + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + typename ObjectCacheMap::iterator itr = _objectCache.find(key); + if (itr!=_objectCache.end()) + return itr->second.first; + else return 0; + } /** 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); + bool checkInObjectCache(const KeyType& key, double timeStamp) + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + typename ObjectCacheMap::iterator itr = _objectCache.find(key); + if (itr!=_objectCache.end()) + { + itr->second.second = timeStamp; + return true; + } + else return false; + } /** call releaseGLObjects on all objects attached to the object cache.*/ - void releaseGLObjects(osg::State* state); + void releaseGLObjects(osg::State* state) + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + for(typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr) + { + osg::Object* object = itr->second.first.get(); + object->releaseGLObjects(state); + } + } /** call node->accept(nv); for all nodes in the objectCache. */ - void accept(osg::NodeVisitor& nv); + void accept(osg::NodeVisitor& nv) + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + for(typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr) + { + osg::Object* object = itr->second.first.get(); + if (object) + { + osg::Node* node = dynamic_cast(object); + if (node) + node->accept(nv); + } + } + } /** call operator()(osg::Object*) for each object in the cache. */ template void call(Functor& f) { OpenThreads::ScopedLock lock(_objectCacheMutex); - for (ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it) + for (typename ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it) f(it->second.first.get()); } /** Get the number of objects in the cache. */ - unsigned int getCacheSize() const; + unsigned int getCacheSize() const + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + return _objectCache.size(); + } protected: - virtual ~ObjectCache(); + virtual ~GenericObjectCache() {} typedef std::pair, double > ObjectTimeStampPair; - typedef std::map ObjectCacheMap; + typedef std::map ObjectCacheMap; ObjectCacheMap _objectCache; mutable OpenThreads::Mutex _objectCacheMutex; }; +class ObjectCache : public GenericObjectCache +{ +}; + } #endif diff --git a/components/resource/resourcemanager.cpp b/components/resource/resourcemanager.cpp deleted file mode 100644 index c0e99674e..000000000 --- a/components/resource/resourcemanager.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "resourcemanager.hpp" - -#include "objectcache.hpp" - -namespace Resource -{ - - ResourceManager::ResourceManager(const VFS::Manager *vfs) - : mVFS(vfs) - , mCache(new Resource::ObjectCache) - , mExpiryDelay(0.0) - { - - } - - ResourceManager::~ResourceManager() - { - } - - void ResourceManager::updateCache(double referenceTime) - { - mCache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime); - mCache->removeExpiredObjectsInCache(referenceTime - mExpiryDelay); - } - - void ResourceManager::clearCache() - { - mCache->clear(); - } - - void ResourceManager::setExpiryDelay(double expiryDelay) - { - mExpiryDelay = expiryDelay; - } - - const VFS::Manager* ResourceManager::getVFS() const - { - return mVFS; - } - - void ResourceManager::releaseGLObjects(osg::State *state) - { - mCache->releaseGLObjects(state); - } - -} diff --git a/components/resource/resourcemanager.hpp b/components/resource/resourcemanager.hpp index 6031ecc01..a5ae27c6a 100644 --- a/components/resource/resourcemanager.hpp +++ b/components/resource/resourcemanager.hpp @@ -3,6 +3,8 @@ #include +#include "objectcache.hpp" + namespace VFS { class Manager; @@ -16,37 +18,67 @@ namespace osg namespace Resource { - class ObjectCache; + + class BaseResourceManager + { + public: + virtual ~BaseResourceManager() {} + virtual void updateCache(double referenceTime) {} + virtual void clearCache() {} + virtual void setExpiryDelay(double expiryDelay) {} + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const {} + virtual void releaseGLObjects(osg::State* state) {} + }; /// @brief Base class for managers that require a virtual file system and object cache. /// @par This base class implements clearing of the cache, but populating it and what it's used for is up to the individual sub classes. - class ResourceManager + template + class GenericResourceManager : public BaseResourceManager { public: - ResourceManager(const VFS::Manager* vfs); - virtual ~ResourceManager(); + typedef GenericObjectCache CacheType; + + GenericResourceManager(const VFS::Manager* vfs) + : mVFS(vfs) + , mCache(new CacheType) + , mExpiryDelay(0.0) + { + } + + virtual ~GenericResourceManager() {} /// Clear cache entries that have not been referenced for longer than expiryDelay. - virtual void updateCache(double referenceTime); + virtual void updateCache(double referenceTime) + { + mCache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime); + mCache->removeExpiredObjectsInCache(referenceTime - mExpiryDelay); + } /// Clear all cache entries. - virtual void clearCache(); + virtual void clearCache() { mCache->clear(); } /// How long to keep objects in cache after no longer being referenced. - void setExpiryDelay (double expiryDelay); + void setExpiryDelay (double expiryDelay) { mExpiryDelay = expiryDelay; } - const VFS::Manager* getVFS() const; + const VFS::Manager* getVFS() const { return mVFS; } virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const {} - virtual void releaseGLObjects(osg::State* state); + virtual void releaseGLObjects(osg::State* state) { mCache->releaseGLObjects(state); } protected: const VFS::Manager* mVFS; - osg::ref_ptr mCache; + osg::ref_ptr mCache; double mExpiryDelay; }; + + class ResourceManager : public GenericResourceManager + { + public: + ResourceManager(const VFS::Manager* vfs) : GenericResourceManager(vfs) {} + }; + } #endif diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp index 4d61dce69..2015ba874 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -54,7 +54,7 @@ namespace Resource void ResourceSystem::setExpiryDelay(double expiryDelay) { - for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) + for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) (*it)->setExpiryDelay(expiryDelay); // NIF files aren't needed any more once the converted objects are cached in SceneManager / BulletShapeManager, @@ -64,24 +64,24 @@ namespace Resource void ResourceSystem::updateCache(double referenceTime) { - for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) + for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) (*it)->updateCache(referenceTime); } void ResourceSystem::clearCache() { - for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) + for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) (*it)->clearCache(); } - void ResourceSystem::addResourceManager(ResourceManager *resourceMgr) + void ResourceSystem::addResourceManager(BaseResourceManager *resourceMgr) { mResourceManagers.push_back(resourceMgr); } - void ResourceSystem::removeResourceManager(ResourceManager *resourceMgr) + void ResourceSystem::removeResourceManager(BaseResourceManager *resourceMgr) { - std::vector::iterator found = std::find(mResourceManagers.begin(), mResourceManagers.end(), resourceMgr); + std::vector::iterator found = std::find(mResourceManagers.begin(), mResourceManagers.end(), resourceMgr); if (found != mResourceManagers.end()) mResourceManagers.erase(found); } @@ -93,13 +93,13 @@ namespace Resource void ResourceSystem::reportStats(unsigned int frameNumber, osg::Stats *stats) const { - for (std::vector::const_iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) + for (std::vector::const_iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) (*it)->reportStats(frameNumber, stats); } void ResourceSystem::releaseGLObjects(osg::State *state) { - for (std::vector::const_iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) + for (std::vector::const_iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) (*it)->releaseGLObjects(state); } diff --git a/components/resource/resourcesystem.hpp b/components/resource/resourcesystem.hpp index 396bdb8fa..db8bbafc9 100644 --- a/components/resource/resourcesystem.hpp +++ b/components/resource/resourcesystem.hpp @@ -22,7 +22,7 @@ namespace Resource class ImageManager; class NifFileManager; class KeyframeManager; - class ResourceManager; + class BaseResourceManager; /// @brief Wrapper class that constructs and provides access to the most commonly used resource subsystems. /// @par Resource subsystems can be used with multiple OpenGL contexts, just like the OSG equivalents, but @@ -48,10 +48,10 @@ namespace Resource /// Add this ResourceManager to be handled by the ResourceSystem. /// @note Does not transfer ownership. - void addResourceManager(ResourceManager* resourceMgr); + void addResourceManager(BaseResourceManager* resourceMgr); /// @note Do nothing if resourceMgr does not exist. /// @note Does not delete resourceMgr. - void removeResourceManager(ResourceManager* resourceMgr); + void removeResourceManager(BaseResourceManager* resourceMgr); /// How long to keep objects in cache after no longer being referenced. void setExpiryDelay(double expiryDelay); @@ -72,7 +72,7 @@ namespace Resource // Store the base classes separately to get convenient access to the common interface // Here users can register their own resourcemanager as well - std::vector mResourceManagers; + std::vector mResourceManagers; const VFS::Manager* mVFS; diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 80f414541..4b2bee89a 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -22,7 +22,7 @@ namespace Terrain { ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer) - : ResourceManager(nullptr) + : GenericResourceManager(nullptr) , mStorage(storage) , mSceneManager(sceneMgr) , mTextureManager(textureManager) @@ -35,12 +35,9 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T } -osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, int lod, unsigned int lodFlags) +osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags) { - std::ostringstream stream; - stream << size << " " << center.x() << " " << center.y() << " " << lod << " " << lodFlags; - std::string id = stream.str(); - + ChunkId id = std::make_tuple(center, lod, lodFlags); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); if (obj) return obj->asNode(); @@ -59,14 +56,14 @@ void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats *stats) cons void ChunkManager::clearCache() { - ResourceManager::clearCache(); + GenericResourceManager::clearCache(); mBufferCache.clearCache(); } void ChunkManager::releaseGLObjects(osg::State *state) { - ResourceManager::releaseGLObjects(state); + GenericResourceManager::releaseGLObjects(state); mBufferCache.releaseGLObjects(state); } @@ -164,7 +161,7 @@ std::vector > ChunkManager::createPasses(float chunk return ::Terrain::createPasses(useShaders, &mSceneManager->getShaderManager(), layers, blendmapTextures, blendmapScale, blendmapScale); } -osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, int lod, unsigned int lodFlags) +osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags) { osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize(); osg::ref_ptr transform (new SceneUtil::PositionAttitudeTransform); diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 27201864f..2dea1cf92 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_COMPONENTS_TERRAIN_CHUNKMANAGER_H #define OPENMW_COMPONENTS_TERRAIN_CHUNKMANAGER_H +#include + #include #include "buffercache.hpp" @@ -24,13 +26,15 @@ namespace Terrain class Storage; class CompositeMap; + typedef std::tuple ChunkId; // Center, Lod, Lod Flags + /// @brief Handles loading and caching of terrain chunks - class ChunkManager : public Resource::ResourceManager + class ChunkManager : public Resource::GenericResourceManager { public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, int lod, unsigned int lodFlags); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); void setCullingActive(bool active) { mCullingActive = active; } void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; } @@ -44,7 +48,7 @@ namespace Terrain void releaseGLObjects(osg::State* state) override; private: - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, int lod, unsigned int lodFlags); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); osg::ref_ptr createCompositeMapRTT(); From 4ab93aeffe030993d10a5a1e4dd3cbb4bca7f152 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 039/106] Do not allocate empty callbacks in the RigGeometry --- components/sceneutil/riggeometry.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 0c8198055..97c8e1d39 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -40,8 +40,8 @@ RigGeometry::RigGeometry() , mLastFrameNumber(0) , mBoundsFirstFrame(true) { - setUpdateCallback(new osg::Callback); // dummy to make sure getNumChildrenRequiringUpdateTraversal() is correct - // update done in accept(NodeVisitor&) + setNumChildrenRequiringUpdateTraversal(1); + // update done in accept(NodeVisitor&) } RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op) @@ -54,6 +54,7 @@ RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op) , mBoundsFirstFrame(true) { setSourceGeometry(copy.mSourceGeometry); + setNumChildrenRequiringUpdateTraversal(1); } void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) From dd0b45ede66934d904831ee7e979349497b5d5a3 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 14 Mar 2019 22:15:57 +0300 Subject: [PATCH 040/106] Do not pass nullptr to std::memcmp --- components/detournavigator/navmeshtilescache.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index 76060981f..8dd14f04c 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -179,6 +179,17 @@ namespace DetourNavigator const auto lhsEnd = reinterpret_cast(lhs.data() + lhs.size()); const auto lhsSize = static_cast(lhsEnd - lhsBegin); const auto rhsSize = static_cast(mRhsEnd - mRhsIt); + + if (lhsBegin == nullptr || mRhsIt == nullptr) + { + if (lhsSize < rhsSize) + return -1; + else if (lhsSize > rhsSize) + return 1; + else + return 0; + } + const auto size = std::min(lhsSize, rhsSize); if (const auto result = std::memcmp(lhsBegin, mRhsIt, size)) From dd03d3b2318ddb93dcbc7569cdf7963ea68959ed Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 15 Mar 2019 19:04:47 +0300 Subject: [PATCH 041/106] Print SDL version at startup --- apps/openmw/engine.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index a68bf4c36..4fae3061a 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -651,6 +651,9 @@ void OMW::Engine::go() assert (!mContentFiles.empty()); Log(Debug::Info) << "OSG version: " << osgGetVersion(); + SDL_version sdlVersion; + SDL_GetVersion(&sdlVersion); + Log(Debug::Info) << "SDL version: " << (int)sdlVersion.major << "." << (int)sdlVersion.minor << "." << (int)sdlVersion.patch; Misc::Rng::init(mRandomSeed); From 5b2691e7447dc8f5f82a0a14332b3452273669b3 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 15 Mar 2019 17:44:52 +0000 Subject: [PATCH 042/106] Actually use specular shininess parameter instead of hardcoded value. --- files/shaders/lighting.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/shaders/lighting.glsl b/files/shaders/lighting.glsl index 90d849472..6de34bc07 100644 --- a/files/shaders/lighting.glsl +++ b/files/shaders/lighting.glsl @@ -78,5 +78,5 @@ vec3 getSpecular(vec3 viewNormal, vec3 viewDirection, float shininess, vec3 matS if (NdotL < 0.0) return vec3(0.,0.,0.); vec3 halfVec = normalize(lightDir - viewDirection); - return pow(max(dot(viewNormal, halfVec), 0.0), 128.) * gl_LightSource[0].specular.xyz * matSpec; + return pow(max(dot(viewNormal, halfVec), 0.0), shininess) * gl_LightSource[0].specular.xyz * matSpec; } From a92690d43393946953f14785c35f3099a3103cee Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 15 Mar 2019 21:30:57 +0000 Subject: [PATCH 043/106] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c0f476b8..2f3fcc329 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Bug #4877: Startup script executes only on a new game start Bug #4888: Global variable stray explicit reference calls break script compilation Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5 + Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used. Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Feature #3610: Option to invert X axis From c91deaf7affb5bd43a105c3bc802c98b65ad7520 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 Mar 2019 00:08:24 +0300 Subject: [PATCH 044/106] Use std::vector::data method to avoid reference binding to null pointer --- components/esm/loadscpt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 808b416d7..b41dc496f 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -104,7 +104,7 @@ namespace ESM } mScriptData.resize(subSize); - esm.getExact(&mScriptData[0], mScriptData.size()); + esm.getExact(mScriptData.data(), mScriptData.size()); break; } case ESM::FourCC<'S','C','T','X'>::value: @@ -156,7 +156,7 @@ namespace ESM } esm.startSubRecord("SCDT"); - esm.write(reinterpret_cast(&mScriptData[0]), mData.mScriptDataSize); + esm.write(reinterpret_cast(mScriptData.data()), mData.mScriptDataSize); esm.endRecord("SCDT"); esm.writeHNOString("SCTX", mScriptText); From 2b674cedaad6261594418689bbd5bc1ae7bc04f7 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 16 Mar 2019 14:38:51 +0200 Subject: [PATCH 045/106] Implement view cell from r-type hint --- .../view/render/pagedworldspacewidget.cpp | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index ff69656a2..7f31373ee 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -615,7 +616,39 @@ void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) } else if (hint[0]=='r') { - /// \todo implement 'r' type hints + // syntax r:ref#number (e.g. r:ref#100) + char ignore; + + std::istringstream stream (hint.c_str()); + if (stream >> ignore) // ignore r + { + char ignore1; // : or ; + + std::string refCode; // ref#number (e.g. ref#100) + + while (stream >> ignore1 >> refCode) {} + + //Find out cell coordinate + CSMWorld::IdTable& references = dynamic_cast ( + *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_References)); + int cellColumn = references.findColumnIndex(CSMWorld::Columns::ColumnId_Cell); + QVariant cell = references.data(references.getModelIndex(refCode, cellColumn)).value(); + QString cellqs = cell.toString(); + std::istringstream streamCellCoord (cellqs.toStdString().c_str()); + + if (streamCellCoord >> ignore) //ignore # + { + // Current coordinate + int x, y; + + // Loop through all the coordinates to add them to selection + while (streamCellCoord >> x >> y) + selection.add (CSMWorld::CellCoordinates (x, y)); + + // Mark that camera needs setup + mCamPositionSet=false; + } + } } setCellSelection (selection); From 5f86933dc6e7361f3a4a16e97f9ade81786fbb97 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 11 Mar 2019 16:31:16 +0300 Subject: [PATCH 046/106] Merge upstream osgQt changes --- extern/osgQt/GraphicsWindowQt | 2 -- extern/osgQt/GraphicsWindowQt.cpp | 17 ++++++----------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/extern/osgQt/GraphicsWindowQt b/extern/osgQt/GraphicsWindowQt index 1e34fc9db..274f0a149 100644 --- a/extern/osgQt/GraphicsWindowQt +++ b/extern/osgQt/GraphicsWindowQt @@ -14,8 +14,6 @@ #ifndef OSGVIEWER_GRAPHICSWINDOWQT #define OSGVIEWER_GRAPHICSWINDOWQT -#include - #include #include diff --git a/extern/osgQt/GraphicsWindowQt.cpp b/extern/osgQt/GraphicsWindowQt.cpp index 0135a1784..af963c04b 100644 --- a/extern/osgQt/GraphicsWindowQt.cpp +++ b/extern/osgQt/GraphicsWindowQt.cpp @@ -22,12 +22,6 @@ #include #endif -#if (QT_VERSION>=QT_VERSION_CHECK(4, 6, 0)) -# define USE_GESTURES -# include -# include -#endif - using namespace osgQt; #if (QT_VERSION < QT_VERSION_CHECK(5, 2, 0)) @@ -132,6 +126,8 @@ bool GLWidget::event( QEvent* event ) void GLWidget::resizeEvent( QResizeEvent* event ) { + if (_gw == nullptr || !_gw->valid()) + return; const QSize& size = event->size(); int scaled_width = static_cast(size.width()*_devicePixelRatio); @@ -143,6 +139,8 @@ void GLWidget::resizeEvent( QResizeEvent* event ) void GLWidget::moveEvent( QMoveEvent* event ) { + if (_gw == nullptr || !_gw->valid()) + return; const QPoint& pos = event->pos(); int scaled_width = static_cast(width()*_devicePixelRatio); int scaled_height = static_cast(height()*_devicePixelRatio); @@ -527,9 +525,6 @@ void GraphicsWindowQt::swapBuffersImplementation() if (!_widget || !_widget->windowHandle()->isExposed()) return; #endif - - _widget->swapBuffers(); - // FIXME: the processDeferredEvents should really be executed in a GUI (main) thread context but // I couln't find any reliable way to do this. For now, lets hope non of *GUI thread only operations* will // be executed in a QGLWidget::event handler. On the other hand, calling GUI only operations in the @@ -539,8 +534,8 @@ void GraphicsWindowQt::swapBuffersImplementation() // We need to call makeCurrent here to restore our previously current context // which may be changed by the processDeferredEvents function. - if (QGLContext::currentContext() != _widget->context()) - _widget->makeCurrent(); + _widget->makeCurrent(); + _widget->swapBuffers(); } void GraphicsWindowQt::requestWarpPointer( float x, float y ) From 168e758921933fcc857067312cf9864f2711ccd8 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 17 Mar 2019 06:37:20 +0300 Subject: [PATCH 047/106] Correct look and behavior of some setting sliders --- apps/openmw/mwgui/settingswindow.cpp | 11 ++++++--- files/mygui/openmw_settings_window.layout | 29 +++++++++++++---------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 98bbe859c..deb1116d0 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -126,7 +126,8 @@ namespace MWGui { MyGUI::ScrollBar* scroll = current->castType(); std::string valueStr; - if (getSettingValueType(current) == "Float") + std::string valueType = getSettingValueType(current); + if (valueType == "Float" || valueType == "Integer") { // TODO: ScrollBar isn't meant for this. should probably use a dedicated FloatSlider widget float min,max; @@ -423,14 +424,18 @@ namespace MWGui if (getSettingType(scroller) == "Slider") { std::string valueStr; - if (getSettingValueType(scroller) == "Float") + std::string valueType = getSettingValueType(scroller); + if (valueType == "Float" || valueType == "Integer") { float value = pos / float(scroller->getScrollRange()-1); float min,max; getSettingMinMax(scroller, min, max); value = min + (max-min) * value; - Settings::Manager::setFloat(getSettingName(scroller), getSettingCategory(scroller), value); + if (valueType == "Float") + Settings::Manager::setFloat(getSettingName(scroller), getSettingCategory(scroller), value); + else + Settings::Manager::setInt(getSettingName(scroller), getSettingCategory(scroller), (int)value); valueStr = MyGUI::utility::toString(int(value)); } else diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 58be98cca..2341bfa19 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -53,12 +53,12 @@ - - + + - + @@ -83,11 +83,11 @@ - + - + @@ -309,12 +309,12 @@ - - + + - + @@ -353,18 +353,19 @@ - + - + + @@ -372,18 +373,20 @@ - + - + - + + + From e131e6699c91d2c3478209346c2ac05faff595b2 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 048/106] Match vertex colors data type to source --- components/esmterrain/storage.cpp | 28 ++++++++++++++-------------- components/esmterrain/storage.hpp | 4 ++-- components/terrain/chunkmanager.cpp | 3 ++- components/terrain/storage.hpp | 2 +- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 567d93bbd..6c7d6130e 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -158,7 +158,7 @@ namespace ESMTerrain normal.normalize(); } - void Storage::fixColour (osg::Vec4f& color, int cellX, int cellY, int col, int row, LandCache& cache) + void Storage::fixColour (osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache) { if (col == ESM::Land::LAND_SIZE-1) { @@ -175,22 +175,22 @@ namespace ESMTerrain const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; if (data) { - color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; - color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; - color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; + color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3]; + color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1]; + color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2]; } else { - color.r() = 1; - color.g() = 1; - color.b() = 1; + color.r() = 255; + color.g() = 255; + color.b() = 255; } } void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, osg::ref_ptr positions, osg::ref_ptr normals, - osg::ref_ptr colours) + osg::ref_ptr colours) { // LOD level n means every 2^n-th vertex is kept size_t increment = static_cast(1) << lodLevel; @@ -207,7 +207,7 @@ namespace ESMTerrain colours->resize(numVerts*numVerts); osg::Vec3f normal; - osg::Vec4f color; + osg::Vec4ub color; float vertY = 0; float vertX = 0; @@ -295,20 +295,20 @@ namespace ESMTerrain if (colourData) { for (int i=0; i<3; ++i) - color[i] = colourData->mColours[srcArrayIndex+i] / 255.f; + color[i] = colourData->mColours[srcArrayIndex+i]; } else { - color.r() = 1; - color.g() = 1; - color.b() = 1; + color.r() = 255; + color.g() = 255; + color.b() = 255; } // Unlike normals, colors mostly connect seamlessly between cells, but not always... if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) fixColour(color, cellX, cellY, col, row, cache); - color.a() = 1; + color.a() = 255; (*colours)[static_cast(vertX*numVerts + vertY)] = color; diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index f3300f748..58c937e89 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -75,7 +75,7 @@ namespace ESMTerrain virtual void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, osg::ref_ptr positions, osg::ref_ptr normals, - osg::ref_ptr colours); + osg::ref_ptr colours); /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might @@ -106,7 +106,7 @@ namespace ESMTerrain const VFS::Manager* mVFS; void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); - void fixColour (osg::Vec4f& colour, int cellX, int cellY, int col, int row, LandCache& cache); + void fixColour (osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache); void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); float getVertexHeight (const ESM::Land::LandData* data, int x, int y); diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 80f414541..1b297d11b 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -172,7 +172,8 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve osg::ref_ptr positions (new osg::Vec3Array); osg::ref_ptr normals (new osg::Vec3Array); - osg::ref_ptr colors (new osg::Vec4Array); + osg::ref_ptr colors (new osg::Vec4ubArray); + colors->setNormalize(true); osg::ref_ptr vbo (new osg::VertexBufferObject); positions->setVertexBufferObject(vbo); diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index ebac1148c..0cc4a315d 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -52,7 +52,7 @@ namespace Terrain virtual void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, osg::ref_ptr positions, osg::ref_ptr normals, - osg::ref_ptr colours) = 0; + osg::ref_ptr colours) = 0; typedef std::vector > ImageVector; /// Create textures holding layer blend values for a terrain chunk. From b42ad0f610e8d5567f076c20b6fe7acdfe87d362 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 049/106] Inline certain terrain storage functions --- components/esmterrain/storage.cpp | 13 ------------- components/esmterrain/storage.hpp | 26 +++++++++++++++++--------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 6c7d6130e..3d6e521d1 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -46,19 +46,6 @@ namespace ESMTerrain { } - const ESM::Land::LandData *LandObject::getData(int flags) const - { - if ((mData.mDataLoaded & flags) != flags) - return nullptr; - return &mData; - } - - int LandObject::getPlugin() const - { - return mLand->mPlugin; - } - - const float defaultHeight = ESM::Land::DEFAULT_HEIGHT; Storage::Storage(const VFS::Manager *vfs, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 58c937e89..27d6232eb 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -29,8 +29,17 @@ namespace ESMTerrain META_Object(ESMTerrain, LandObject) - const ESM::Land::LandData* getData(int flags) const; - int getPlugin() const; + inline const ESM::Land::LandData* getData(int flags) const + { + if ((mData.mDataLoaded & flags) != flags) + return nullptr; + return &mData; + } + + inline int getPlugin() const + { + return mLand->mPlugin; + } private: const ESM::Land* mLand; @@ -105,21 +114,20 @@ namespace ESMTerrain private: const VFS::Manager* mVFS; - void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); - void fixColour (osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache); - void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); + inline void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); + inline void fixColour (osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache); + inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); - float getVertexHeight (const ESM::Land::LandData* data, int x, int y); + inline float getVertexHeight (const ESM::Land::LandData* data, int x, int y); - const LandObject* getLand(int cellX, int cellY, LandCache& cache); + inline const LandObject* getLand(int cellX, int cellY, LandCache& cache); // Since plugins can define new texture palettes, we need to know the plugin index too // in order to retrieve the correct texture name. // pair typedef std::pair UniqueTextureId; - UniqueTextureId getVtexIndexAt(int cellX, int cellY, - int x, int y, LandCache&); + inline UniqueTextureId getVtexIndexAt(int cellX, int cellY, int x, int y, LandCache&); std::string getTextureName (UniqueTextureId id); std::map mLayerInfoMap; From de572226e4ce536624313974a4618d363b7633e1 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 17 Mar 2019 07:59:16 +0300 Subject: [PATCH 050/106] Update optimizer with upstream improvements --- components/sceneutil/optimizer.cpp | 206 ++++++++++------------------- 1 file changed, 70 insertions(+), 136 deletions(-) diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index aaff38797..7c568ff9d 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -422,7 +422,7 @@ void CollectLowestTransformsVisitor::disableObject(ObjectMap::iterator itr) if (itr->second._canBeApplied) { - // we havn't been disabled yet so we need to disable, + // we haven't been disabled yet so we need to disable, itr->second._canBeApplied = false; // and then inform everybody we have been disabled. @@ -446,7 +446,7 @@ void CollectLowestTransformsVisitor::disableTransform(osg::Transform* transform) if (itr->second._canBeApplied) { - // we havn't been disabled yet so we need to disable, + // we haven't been disabled yet so we need to disable, itr->second._canBeApplied = false; // and then inform everybody we have been disabled. for(TransformStruct::ObjectSet::iterator oitr = itr->second._objectSet.begin(); @@ -905,7 +905,7 @@ void Optimizer::RemoveRedundantNodesVisitor::removeRedundantNodes() struct LessGeometry { - bool operator() (const osg::Geometry* lhs,const osg::Geometry* rhs) const + bool operator() (const osg::ref_ptr& lhs,const osg::ref_ptr& rhs) const { if (lhs->getStateSet()getStateSet()) return true; if (rhs->getStateSet()getStateSet()) return false; @@ -1001,7 +1001,7 @@ struct LessGeometry struct LessGeometryPrimitiveType { - bool operator() (const osg::Geometry* lhs,const osg::Geometry* rhs) const + bool operator() (const osg::ref_ptr& lhs,const osg::ref_ptr& rhs) const { for(unsigned int i=0; igetNumPrimitiveSets() && igetNumPrimitiveSets(); @@ -1115,45 +1115,39 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (group.getNumChildren()>=2) { - typedef std::vector DuplicateList; - typedef std::vector< osg::ref_ptr > DrawableList; - typedef std::map GeometryDuplicateMap; + typedef std::vector< osg::ref_ptr > DuplicateList; + typedef std::vector< osg::ref_ptr > Nodes; + typedef std::map< osg::ref_ptr ,DuplicateList,LessGeometry> GeometryDuplicateMap; typedef std::vector MergeList; GeometryDuplicateMap geometryDuplicateMap; - DrawableList standardDrawables; + Nodes standardChildren; unsigned int i; for(i=0;iasDrawable(); - if (drawable) + osg::Node* child = group.getChild(i); + osg::Geometry* geom = child->asGeometry(); + if (geom) { - osg::Geometry* geom = drawable->asGeometry(); - if (geom) + if (!geometryContainsSharedArrays(*geom) && + geom->getDataVariance()!=osg::Object::DYNAMIC && + isOperationPermissibleForObject(geom)) { - //geom->computeCorrectBindingsAndArraySizes(); - - if (!geometryContainsSharedArrays(*geom) && - geom->getDataVariance()!=osg::Object::DYNAMIC && - isOperationPermissibleForObject(geom)) - { - geometryDuplicateMap[geom].push_back(geom); - } - else - { - standardDrawables.push_back(drawable); - } + geometryDuplicateMap[geom].push_back(geom); } else { - standardDrawables.push_back(drawable); + standardChildren.push_back(geom); } } + else + { + standardChildren.push_back(child); + } } -#if 1 // first try to group geometries with the same properties // (i.e. array types) to avoid loss of data during merging MergeList mergeListChecked; // List of drawables just before merging, grouped by "compatibility" and vertex limit @@ -1183,7 +1177,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) dupItr!=itr->second.end(); ++dupItr) { - osg::Geometry* geomToPush = *dupItr; + osg::Geometry* geomToPush = dupItr->get(); // try to group geomToPush with another geometry MergeList::iterator eachMergeList=mergeListTmp.begin(); @@ -1216,125 +1210,73 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) // then build merge list using _targetMaximumNumberOfVertices bool needToDoMerge = false; // dequeue each DuplicateList when vertices limit is reached or when all elements has been checked - for(;!mergeListChecked.empty();) + for(MergeList::iterator itr=mergeListChecked.begin(); itr!=mergeListChecked.end(); ++itr) { - MergeList::iterator itr=mergeListChecked.begin(); DuplicateList& duplicateList(*itr); if (duplicateList.size()==0) { - mergeListChecked.erase(itr); continue; } if (duplicateList.size()==1) { mergeList.push_back(duplicateList); - mergeListChecked.erase(itr); continue; } - unsigned int numVertices(duplicateList.front()->getVertexArray() ? duplicateList.front()->getVertexArray()->getNumElements() : 0); - DuplicateList::iterator eachGeom(duplicateList.begin()+1); - // until all geometries have been checked or _targetMaximumNumberOfVertices is reached - for(;eachGeom!=duplicateList.end(); ++eachGeom) + unsigned int totalNumberVertices = 0; + DuplicateList subset; + for(DuplicateList::iterator ditr = duplicateList.begin(); + ditr != duplicateList.end(); + ++ditr) { - unsigned int numAddVertices((*eachGeom)->getVertexArray() ? (*eachGeom)->getVertexArray()->getNumElements() : 0); - if ((numVertices+numAddVertices)>_targetMaximumNumberOfVertices) - { - break; - } - else + osg::Geometry* geometry = ditr->get(); + unsigned int numVertices = (geometry->getVertexArray() ? geometry->getVertexArray()->getNumElements() : 0); + if ((totalNumberVertices+numVertices)>_targetMaximumNumberOfVertices && !subset.empty()) { - numVertices += numAddVertices; + mergeList.push_back(subset); + subset.clear(); + totalNumberVertices = 0; } + totalNumberVertices += numVertices; + subset.push_back(geometry); + if (subset.size()>1) needToDoMerge = true; } - - // push back if bellow the limit - if (eachGeom==duplicateList.end()) - { - if (duplicateList.size()>1) needToDoMerge = true; - mergeList.push_back(duplicateList); - mergeListChecked.erase(itr); - } - // else split the list to store what is below the limit and retry on what is above - else - { - mergeList.push_back(DuplicateList()); - DuplicateList* duplicateListResult = &mergeList.back(); - duplicateListResult->insert(duplicateListResult->end(),duplicateList.begin(),eachGeom); - duplicateList.erase(duplicateList.begin(),eachGeom); - if (duplicateListResult->size()>1) needToDoMerge = true; - } + if (!subset.empty()) mergeList.push_back(subset); } if (needToDoMerge) { + // to avoid performance issues associated with incrementally removing a large number children, we remove them all and add back the ones we need. + group.removeChildren(0, group.getNumChildren()); + + for(Nodes::iterator itr = standardChildren.begin(); + itr != standardChildren.end(); + ++itr) + { + group.addChild(*itr); + } + // now do the merging of geometries for(MergeList::iterator mitr = mergeList.begin(); mitr != mergeList.end(); ++mitr) { DuplicateList& duplicateList = *mitr; - if (duplicateList.size()>1) + if (!duplicateList.empty()) { - osg::Geometry* lhs = duplicateList.front(); - for(DuplicateList::iterator ditr = duplicateList.begin()+1; + DuplicateList::iterator ditr = duplicateList.begin(); + osg::ref_ptr lhs = *ditr++; + group.addChild(lhs.get()); + for(; ditr != duplicateList.end(); ++ditr) { - mergeGeometry(*lhs,**ditr); - - group.removeChild(*ditr); - } - } - } - } - -#else - // don't merge geometry if its above a maximum number of vertices. - for(GeometryDuplicateMap::iterator itr=geometryDuplicateMap.begin(); - itr!=geometryDuplicateMap.end(); - ++itr) - { - if (itr->second.size()>1) - { - std::sort(itr->second.begin(),itr->second.end(),LessGeometryPrimitiveType()); - osg::Geometry* lhs = itr->second[0]; - for(DuplicateList::iterator dupItr=itr->second.begin()+1; - dupItr!=itr->second.end(); - ++dupItr) - { - - osg::Geometry* rhs = *dupItr; - - if (lhs->getVertexArray() && lhs->getVertexArray()->getNumElements()>=_targetMaximumNumberOfVertices) - { - lhs = rhs; - continue; - } - - if (rhs->getVertexArray() && rhs->getVertexArray()->getNumElements()>=_targetMaximumNumberOfVertices) - { - continue; - } - - if (lhs->getVertexArray() && rhs->getVertexArray() && - (lhs->getVertexArray()->getNumElements()+rhs->getVertexArray()->getNumElements())>=_targetMaximumNumberOfVertices) - { - continue; - } - - if (mergeGeometry(*lhs,*rhs)) - { - geode.removeDrawable(rhs); - - static int co = 0; - OSG_INFO<<"merged and removed Geometry "<<++co<getType()!=rhs->getType()) return false; _lhs = lhs; - _offset = offset; rhs->accept(*this); return true; @@ -1607,30 +1546,24 @@ class MergeArrayVisitor : public osg::ArrayVisitor lhs->insert(lhs->end(),rhs.begin(),rhs.end()); } - template - void _mergeAndOffset(T& rhs) - { - T* lhs = static_cast(_lhs); - - typename T::iterator itr; - for(itr = rhs.begin(); - itr != rhs.end(); - ++itr) - { - lhs->push_back(*itr + _offset); - } - } - virtual void apply(osg::Array&) { OSG_WARN << "Warning: Optimizer's MergeArrayVisitor cannot merge Array type." << std::endl; } - virtual void apply(osg::ByteArray& rhs) { if (_offset) _mergeAndOffset(rhs); else _merge(rhs); } - virtual void apply(osg::ShortArray& rhs) { if (_offset) _mergeAndOffset(rhs); else _merge(rhs); } - virtual void apply(osg::IntArray& rhs) { if (_offset) _mergeAndOffset(rhs); else _merge(rhs); } - virtual void apply(osg::UByteArray& rhs) { if (_offset) _mergeAndOffset(rhs); else _merge(rhs); } - virtual void apply(osg::UShortArray& rhs) { if (_offset) _mergeAndOffset(rhs); else _merge(rhs); } - virtual void apply(osg::UIntArray& rhs) { if (_offset) _mergeAndOffset(rhs); else _merge(rhs); } + + virtual void apply(osg::ByteArray& rhs) { _merge(rhs); } + virtual void apply(osg::ShortArray& rhs) { _merge(rhs); } + virtual void apply(osg::IntArray& rhs) { _merge(rhs); } + virtual void apply(osg::UByteArray& rhs) { _merge(rhs); } + virtual void apply(osg::UShortArray& rhs) { _merge(rhs); } + virtual void apply(osg::UIntArray& rhs) { _merge(rhs); } virtual void apply(osg::Vec4ubArray& rhs) { _merge(rhs); } + virtual void apply(osg::Vec3ubArray& rhs) { _merge(rhs); } + virtual void apply(osg::Vec2ubArray& rhs) { _merge(rhs); } + + virtual void apply(osg::Vec4usArray& rhs) { _merge(rhs); } + virtual void apply(osg::Vec3usArray& rhs) { _merge(rhs); } + virtual void apply(osg::Vec2usArray& rhs) { _merge(rhs); } + virtual void apply(osg::FloatArray& rhs) { _merge(rhs); } virtual void apply(osg::Vec2Array& rhs) { _merge(rhs); } virtual void apply(osg::Vec3Array& rhs) { _merge(rhs); } @@ -1644,6 +1577,7 @@ class MergeArrayVisitor : public osg::ArrayVisitor virtual void apply(osg::Vec2bArray& rhs) { _merge(rhs); } virtual void apply(osg::Vec3bArray& rhs) { _merge(rhs); } virtual void apply(osg::Vec4bArray& rhs) { _merge(rhs); } + virtual void apply(osg::Vec2sArray& rhs) { _merge(rhs); } virtual void apply(osg::Vec3sArray& rhs) { _merge(rhs); } virtual void apply(osg::Vec4sArray& rhs) { _merge(rhs); } From 2c78d530a273e20e3e8e57e53145caaabe25f2c6 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 Mar 2019 19:45:54 +0300 Subject: [PATCH 051/106] Use static vector to store stat names --- components/resource/stats.cpp | 49 +++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 0a70d75ab..b23558e99 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -197,14 +197,14 @@ class ResourceStatsTextDrawCallback : public osg::Drawable::DrawCallback { public: ResourceStatsTextDrawCallback(osg::Stats* stats, const std::vector& statNames) - : _stats(stats) - , _statNames(statNames) + : mStats(stats) + , mStatNames(statNames) { } virtual void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* drawable) const { - if (!_stats) return; + if (!mStats) return; osgText::Text* text = (osgText::Text*)(drawable); @@ -218,14 +218,14 @@ public: unsigned int frameNumber = renderInfo.getState()->getFrameStamp()->getFrameNumber()-1; - for (std::vector::const_iterator it = _statNames.begin(); it != _statNames.end(); ++it) + for (const auto& statName : mStatNames.get()) { - if (it->empty()) + if (statName.empty()) viewStr << std::endl; else { double value = 0.0; - if (_stats->getAttribute(frameNumber, *it, value)) + if (mStats->getAttribute(frameNumber, statName, value)) viewStr << std::setw(8) << value << std::endl; else viewStr << std::setw(8) << "." << std::endl; @@ -237,8 +237,8 @@ public: text->drawImplementation(renderInfo); } - osg::ref_ptr _stats; - std::vector _statNames; + osg::ref_ptr mStats; + std::reference_wrapper> mStatNames; }; void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) @@ -269,9 +269,30 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) _resourceStatsChildNum = _switch->getNumChildren(); _switch->addChild(group, false); - const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "", "Terrain Chunk", "Terrain Texture", "Land", "Composite", "", "UnrefQueue"}; - - int numLines = sizeof(statNames) / sizeof(statNames[0]); + static const std::vector statNames({ + "Compiling", + "WorkQueue", + "WorkThread", + "", + "Texture", + "StateSet", + "Node", + "Node Instance", + "Shape", + "Shape Instance", + "Image", + "Nif", + "Keyframe", + "", + "Terrain Chunk", + "Terrain Texture", + "Land", + "Composite", + "", + "UnrefQueue", + }); + + const int numLines = statNames.size(); group->addChild(createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0), 10 * _characterSize + 2 * backgroundMargin, @@ -289,9 +310,9 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) viewStr.clear(); viewStr.setf(std::ios::left, std::ios::adjustfield); viewStr.width(14); - for (size_t i = 0; isetText(viewStr.str()); @@ -311,7 +332,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) statsText->setCharacterSize(_characterSize); statsText->setPosition(pos); statsText->setText(""); - statsText->setDrawCallback(new ResourceStatsTextDrawCallback(viewer->getViewerStats(), std::vector(statNames, statNames + numLines))); + statsText->setDrawCallback(new ResourceStatsTextDrawCallback(viewer->getViewerStats(), statNames)); } } From abae35e88bc3fa87317b6652217314a937e604f0 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 4 Mar 2019 01:31:51 +0300 Subject: [PATCH 052/106] Loop title screen music (bug #4896) --- CHANGELOG.md | 1 + apps/openmw/engine.cpp | 14 ++++------- apps/openmw/mwbase/soundmanager.hpp | 3 +++ apps/openmw/mwsound/soundmanagerimp.cpp | 32 ++++++++++++++++++++++++- apps/openmw/mwsound/soundmanagerimp.hpp | 3 +++ 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f3fcc329..c9f23a26c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Bug #4876: AI ratings handling inconsistencies Bug #4877: Startup script executes only on a new game start Bug #4888: Global variable stray explicit reference calls break script compilation + Bug #4896: Title screen music doesn't loop Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5 Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used. Feature #2229: Improve pathfinding AI diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4fae3061a..b317c8b40 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -706,16 +706,10 @@ void OMW::Engine::go() { // start in main menu mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - try - { - // Is there an ini setting for this filename or something? - mEnvironment.getSoundManager()->streamMusic("Special/morrowind title.mp3"); - - std::string logo = mFallbackMap["Movies_Morrowind_Logo"]; - if (!logo.empty()) - mEnvironment.getWindowManager()->playVideo(logo, true); - } - catch (...) {} + mEnvironment.getSoundManager()->playTitleMusic(); + std::string logo = mFallbackMap["Movies_Morrowind_Logo"]; + if (!logo.empty()) + mEnvironment.getWindowManager()->playVideo(logo, true); } else { diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 2b3cf1f0d..bad80d6bd 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -89,6 +89,9 @@ namespace MWBase ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist + virtual void playTitleMusic() = 0; + ///< Start playing title music + virtual void say(const MWWorld::ConstPtr &reference, const std::string& filename) = 0; ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 7b722a835..ae2c25ead 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -471,6 +471,36 @@ namespace MWSound startRandomTitle(); } + void SoundManager::playTitleMusic() + { + if (mCurrentPlaylist == "Title") + return; + + if (mMusicFiles.find("Title") == mMusicFiles.end()) + { + std::vector filelist; + const std::map& index = mVFS->getIndex(); + // Is there an ini setting for this filename or something? + std::string filename = "music/special/morrowind title.mp3"; + auto found = index.find(filename); + if (found != index.end()) + { + filelist.emplace_back(found->first); + mMusicFiles["Title"] = filelist; + } + else + { + Log(Debug::Warning) << "Title music not found"; + return; + } + } + + if (mMusicFiles["Title"].empty()) + return; + + mCurrentPlaylist = "Title"; + startRandomTitle(); + } void SoundManager::say(const MWWorld::ConstPtr &ptr, const std::string &filename) { @@ -1122,10 +1152,10 @@ namespace MWSound if(!mOutput->isInitialized()) return; + updateSounds(duration); if (MWBase::Environment::get().getStateManager()->getState()!= MWBase::StateManager::State_NoGame) { - updateSounds(duration); updateRegionSound(duration); updateWaterSound(duration); } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index d8a4cfc8c..9878e924a 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -168,6 +168,9 @@ namespace MWSound ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist + virtual void playTitleMusic(); + ///< Start playing title music + virtual void say(const MWWorld::ConstPtr &reference, const std::string& filename); ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. From 8adc83f6e2d240f372cf7bdc6dffabcbd0391d8d Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 Mar 2019 21:24:49 +0300 Subject: [PATCH 053/106] Fix clean unused navmeshes weak_ptr doesn't have constructor for shared_ptr&& type, so ptr wasn't moved, just copied. --- components/detournavigator/navmeshmanager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index fc3563ace..871bb6e59 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -22,7 +22,8 @@ namespace template bool resetIfUnique(std::shared_ptr& ptr) { - const std::weak_ptr weak = std::move(ptr); + const std::weak_ptr weak(ptr); + ptr.reset(); if (auto shared = weak.lock()) { ptr = std::move(shared); From 4624f3178801b38288aa8b031d89def24d58ee4b Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 Mar 2019 20:18:53 +0300 Subject: [PATCH 054/106] Report navigator stats --- apps/openmw/engine.cpp | 4 ++++ .../detournavigator/asyncnavmeshupdater.cpp | 16 +++++++++++++++ .../detournavigator/asyncnavmeshupdater.hpp | 4 +++- components/detournavigator/navigator.hpp | 2 ++ components/detournavigator/navigatorimpl.cpp | 5 +++++ components/detournavigator/navigatorimpl.hpp | 2 ++ components/detournavigator/navigatorstub.hpp | 2 ++ components/detournavigator/navmeshmanager.cpp | 5 +++++ components/detournavigator/navmeshmanager.hpp | 2 ++ .../detournavigator/navmeshtilescache.cpp | 20 +++++++++++++++++++ .../detournavigator/navmeshtilescache.hpp | 9 ++++++++- components/resource/stats.cpp | 19 +++++++++++++----- 12 files changed, 83 insertions(+), 7 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4fae3061a..98869703f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -32,6 +32,8 @@ #include +#include + #include "mwinput/inputmanagerimp.hpp" #include "mwgui/windowmanagerimp.hpp" @@ -198,6 +200,8 @@ bool OMW::Engine::frame(float frametime) stats->setAttribute(frameNumber, "WorkQueue", mWorkQueue->getNumItems()); stats->setAttribute(frameNumber, "WorkThread", mWorkQueue->getNumActiveThreads()); + + mEnvironment.getWorld()->getNavigator()->reportStats(frameNumber, *stats); } } diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 2013931ed..6bd4f01c9 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -5,6 +5,8 @@ #include +#include + namespace { using DetourNavigator::ChangeType; @@ -102,6 +104,20 @@ namespace DetourNavigator mDone.wait(lock, [&] { return mJobs.empty(); }); } + void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const + { + std::size_t jobs = 0; + + { + const std::lock_guard lock(mMutex); + jobs = mJobs.size(); + } + + stats.setAttribute(frameNumber, "NavMesh UpdateJobs", jobs); + + mNavMeshTilesCache.reportStats(frameNumber, stats); + } + void AsyncNavMeshUpdater::process() throw() { log("start process jobs"); diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 55e502b45..3493ba02f 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -44,6 +44,8 @@ namespace DetourNavigator void wait(); + void reportStats(unsigned int frameNumber, osg::Stats& stats) const; + private: struct Job { @@ -72,7 +74,7 @@ namespace DetourNavigator std::reference_wrapper mRecastMeshManager; std::reference_wrapper mOffMeshConnectionsManager; std::atomic_bool mShouldStop; - std::mutex mMutex; + mutable std::mutex mMutex; std::condition_variable mHasJob; std::condition_variable mDone; Jobs mJobs; diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index d9c5ebe0b..561b7f02b 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -192,6 +192,8 @@ namespace DetourNavigator virtual std::map getNavMeshes() const = 0; virtual const Settings& getSettings() const = 0; + + virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0; }; } diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index a62220e55..b743f26b2 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -138,6 +138,11 @@ namespace DetourNavigator return mSettings; } + void NavigatorImpl::reportStats(unsigned int frameNumber, osg::Stats& stats) const + { + mNavMeshManager.reportStats(frameNumber, stats); + } + void NavigatorImpl::updateAvoidShapeId(const ObjectId id, const ObjectId avoidId) { updateId(id, avoidId, mWaterIds); diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index d2ff8a57b..b6b3b1b7f 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -48,6 +48,8 @@ namespace DetourNavigator const Settings& getSettings() const override; + void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; + private: Settings mSettings; NavMeshManager mNavMeshManager; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index cab93b105..885482a45 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -79,6 +79,8 @@ namespace DetourNavigator return mDefaultSettings; } + void reportStats(unsigned int /*frameNumber*/, osg::Stats& /*stats*/) const override {} + private: Settings mDefaultSettings {}; SharedNavMeshCacheItem mEmptyNavMeshCacheItem; diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index fc3563ace..c3e13ad7f 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -212,6 +212,11 @@ namespace DetourNavigator return mCache; } + void NavMeshManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const + { + mAsyncNavMeshUpdater.reportStats(frameNumber, stats); + } + void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform, const ChangeType changeType) { diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index cee33c63d..3ef898b05 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -50,6 +50,8 @@ namespace DetourNavigator std::map getNavMeshes() const; + void reportStats(unsigned int frameNumber, osg::Stats& stats) const; + private: const Settings& mSettings; TileCachedRecastMeshManager mRecastMeshManager; diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index 8dd14f04c..466d2e708 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -1,6 +1,8 @@ #include "navmeshtilescache.hpp" #include "exceptions.hpp" +#include + #include namespace DetourNavigator @@ -113,6 +115,24 @@ namespace DetourNavigator return Value(*this, iterator); } + void NavMeshTilesCache::reportStats(unsigned int frameNumber, osg::Stats& stats) const + { + std::size_t navMeshCacheSize = 0; + std::size_t usedNavMeshTiles = 0; + std::size_t cachedNavMeshTiles = 0; + + { + const std::lock_guard lock(mMutex); + navMeshCacheSize = mUsedNavMeshDataSize; + usedNavMeshTiles = mBusyItems.size(); + cachedNavMeshTiles = mFreeItems.size(); + } + + stats.setAttribute(frameNumber, "NavMesh CacheSize", navMeshCacheSize); + stats.setAttribute(frameNumber, "NavMesh UsedTiles", usedNavMeshTiles); + stats.setAttribute(frameNumber, "NavMesh CachedTiles", cachedNavMeshTiles); + } + void NavMeshTilesCache::removeLeastRecentlyUsed() { const auto& item = mFreeItems.back(); diff --git a/components/detournavigator/navmeshtilescache.hpp b/components/detournavigator/navmeshtilescache.hpp index 3f2e0cc01..5d5db47a8 100644 --- a/components/detournavigator/navmeshtilescache.hpp +++ b/components/detournavigator/navmeshtilescache.hpp @@ -12,6 +12,11 @@ #include #include +namespace osg +{ + class Stats; +} + namespace DetourNavigator { struct NavMeshDataRef @@ -105,6 +110,8 @@ namespace DetourNavigator const RecastMesh& recastMesh, const std::vector& offMeshConnections, NavMeshData&& value); + void reportStats(unsigned int frameNumber, osg::Stats& stats) const; + private: class KeyView { @@ -164,7 +171,7 @@ namespace DetourNavigator std::map mMap; }; - std::mutex mMutex; + mutable std::mutex mMutex; std::size_t mMaxNavMeshDataSize; std::size_t mUsedNavMeshDataSize; std::size_t mFreeNavMeshDataSize; diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index b23558e99..51497cd27 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -255,7 +256,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) stateset->setAttribute(new osg::PolygonMode(), osg::StateAttribute::PROTECTED); #endif - osg::Vec3 pos(_statsWidth-300.f, _statsHeight-500.0f,0.0f); + osg::Vec3 pos(_statsWidth-420.f, _statsHeight-500.0f,0.0f); osg::Vec4 backgroundColor(0.0, 0.0, 0.0f, 0.3); osg::Vec4 staticTextColor(1.0, 1.0, 0.0f, 1.0); osg::Vec4 dynamicTextColor(1.0, 1.0, 1.0f, 1.0); @@ -290,12 +291,20 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) "Composite", "", "UnrefQueue", + "", + "NavMesh UpdateJobs", + "NavMesh CacheSize", + "NavMesh UsedTiles", + "NavMesh CachedTiles", }); + static const auto longest = std::max_element(statNames.begin(), statNames.end(), + [] (const std::string& lhs, const std::string& rhs) { return lhs.size() < rhs.size(); }); const int numLines = statNames.size(); + const float statNamesWidth = 13 * _characterSize + 2 * backgroundMargin; group->addChild(createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0), - 10 * _characterSize + 2 * backgroundMargin, + statNamesWidth, numLines * _characterSize + 2 * backgroundMargin, backgroundColor)); @@ -309,7 +318,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) std::ostringstream viewStr; viewStr.clear(); viewStr.setf(std::ios::left, std::ios::adjustfield); - viewStr.width(14); + viewStr.width(longest->size()); for (const auto& statName : statNames) { viewStr << statName << std::endl; @@ -317,10 +326,10 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) staticText->setText(viewStr.str()); - pos.x() += 10 * _characterSize + 2 * backgroundMargin + backgroundSpacing; + pos.x() += statNamesWidth + backgroundSpacing; group->addChild(createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0), - 5 * _characterSize + 2 * backgroundMargin, + 7 * _characterSize + 2 * backgroundMargin, numLines * _characterSize + 2 * backgroundMargin, backgroundColor)); From 80e0fbdd887063d91c26bb034f1d069922393577 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 18 Mar 2019 08:47:46 +0400 Subject: [PATCH 055/106] Fix missing \n characters on the active effects tooltips --- apps/openmw/mwgui/spellicons.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index c1bd421db..51508cd06 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -79,14 +79,11 @@ namespace MWGui static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get().find("fMagicStartIconBlink")->mValue.getFloat(); std::vector& effectInfos = effectInfoPair.second; - bool addNewLine = true; + bool addNewLine = false; for (const MagicEffectInfo& effectInfo : effectInfos) { if (addNewLine) - { sourcesDescription += "\n"; - addNewLine = false; - } // if at least one of the effect sources is permanent, the effect will never wear off if (effectInfo.mPermanent) @@ -161,6 +158,8 @@ namespace MWGui sourcesDescription += MWGui::ToolTips::toString(duration) + "s"; } } + + addNewLine = true; } if (remainingDuration > 0.f) From 36fa51b6ad8a947d70bea2e726bfca87976c2c4a Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 056/106] Fix bounding box calculation for terrain shapes --- components/terrain/chunkmanager.cpp | 2 +- components/terrain/quadtreeworld.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 80f414541..1cdfcf243 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -188,7 +188,7 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->setUseDisplayList(false); geometry->setUseVertexBufferObjects(true); - if (chunkSize <= 2.f) + if (chunkSize <= 1.f) geometry->setLightListCallback(new SceneUtil::LightListCallback); unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize / (1 << lod) + 1; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 597073229..efe0dd58a 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -173,10 +173,10 @@ public: node->setLodCallback(parent->getLodCallback()); node->setViewDataMap(mViewDataMap); - if (center.x() - size > mMaxX - || center.x() + size < mMinX - || center.y() - size > mMaxY - || center.y() + size < mMinY ) + if (center.x() - halfSize > mMaxX + || center.x() + halfSize < mMinX + || center.y() - halfSize > mMaxY + || center.y() + halfSize < mMinY ) // Out of bounds of the actual terrain - this will happen because // we rounded the size up to the next power of two { @@ -191,8 +191,8 @@ public: if (mStorage->getMinMaxHeights(size, center, minZ, maxZ)) { float cellWorldSize = mStorage->getCellWorldSize(); - osg::BoundingBox boundingBox(osg::Vec3f((center.x()-size)*cellWorldSize, (center.y()-size)*cellWorldSize, minZ), - osg::Vec3f((center.x()+size)*cellWorldSize, (center.y()+size)*cellWorldSize, maxZ)); + osg::BoundingBox boundingBox(osg::Vec3f((center.x()-halfSize)*cellWorldSize, (center.y()-halfSize)*cellWorldSize, minZ), + osg::Vec3f((center.x()+halfSize)*cellWorldSize, (center.y()+halfSize)*cellWorldSize, maxZ)); node->setBoundingBox(boundingBox); } return node; From 6029ed4eccd7adc97faa7718c582064c1f316119 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 057/106] Reject empty quad tree nodes at the cell level without land data --- apps/openmw/mwrender/terrainstorage.cpp | 9 +++++++++ apps/openmw/mwrender/terrainstorage.hpp | 2 ++ components/terrain/quadtreeworld.cpp | 5 +++++ components/terrain/storage.hpp | 8 ++++++++ 4 files changed, 24 insertions(+) diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 7894a8393..528ce70ea 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -22,6 +22,15 @@ namespace MWRender mResourceSystem->removeResourceManager(mLandManager.get()); } + bool TerrainStorage::hasData(int cellX, int cellY) + { + const MWWorld::ESMStore &esmStore = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::Land* land = esmStore.get().search(cellX, cellY); + return land != nullptr; + } + void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY) { minX = 0, minY = 0, maxX = 0, maxY = 0; diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 6f716e752..cae56d3aa 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -23,6 +23,8 @@ namespace MWRender virtual osg::ref_ptr getLand (int cellX, int cellY); virtual const ESM::LandTexture* getLandTexture(int index, short plugin); + virtual bool hasData(int cellX, int cellY) override; + /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index efe0dd58a..cf877142a 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -184,6 +184,11 @@ public: return node; } + // Do not add child nodes for default cells without data. + // size = 1 means that the single shape covers the whole cell. + if (node->getSize() == 1 && !mStorage->hasData(center.x()-0.5, center.y()-0.5)) + return node; + if (node->getSize() <= mMinSize) { // We arrived at a leaf diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index ebac1148c..3bc20aa59 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -28,6 +28,14 @@ namespace Terrain /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0; + /// Return true if there is land data for this cell + /// May be overriden for a faster implementation + virtual bool hasData(int cellX, int cellY) + { + float dummy; + return getMinMaxHeights(1, osg::Vec2f(cellX+0.5, cellY+0.5), dummy, dummy); + } + /// Get the minimum and maximum heights of a terrain region. /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree. /// Larger chunks can simply merge AABB of children. From 540709fdae4147462430875cc7f37c6e2ef11a68 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 2 Mar 2019 19:01:25 +0400 Subject: [PATCH 058/106] Add a changelog entry for terrain optimization --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6c510fa1..92b2fa9ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ Feature #4887: Add openmw command option to set initial random seed Feature #4890: Make Distant Terrain configurable Task #4686: Upgrade media decoder to a more current FFmpeg API + Task #4695: Optimize Distant Terrain memory consumption 0.45.0 ------ From c6cb91ce614eefb32153b496e736edf9e419fb75 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 3 Mar 2019 21:29:51 +0400 Subject: [PATCH 059/106] Limit maximum FOV value --- apps/openmw/mwrender/renderingmanager.cpp | 10 +++++++--- docs/source/reference/modding/settings/camera.rst | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 42bd0c69b..5f35b401e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -371,8 +371,10 @@ namespace MWRender mNearClip = Settings::Manager::getFloat("near clip", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); - mFieldOfView = Settings::Manager::getFloat("field of view", "Camera"); - mFirstPersonFieldOfView = Settings::Manager::getFloat("first person field of view", "Camera"); + float fov = Settings::Manager::getFloat("field of view", "Camera"); + mFieldOfView = std::min(std::max(1.f, fov), 179.f); + float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera"); + mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f); mStateUpdater->setFogEnd(mViewDistance); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip)); @@ -1200,7 +1202,9 @@ namespace MWRender mUniformFar->set(mViewDistance); // Since our fog is not radial yet, we should take FOV in account, otherwise terrain near viewing distance may disappear. - float distanceMult = std::cos(osg::DegreesToRadians(mFieldOfView)/2.f); + // Limit FOV here just for sure, otherwise viewing distance can be too high. + fov = std::min(mFieldOfView, 140.f); + float distanceMult = std::cos(osg::DegreesToRadians(fov)/2.f); mTerrain->setViewDistance(mViewDistance * (distanceMult ? 1.f/distanceMult : 1.f)); } diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index aed68658f..6f13e3305 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -94,7 +94,7 @@ field of view ------------- :Type: floating point -:Range: 0-360 +:Range: 1-179 :Default: 55.0 Sets the camera field of view in degrees. Recommended values range from 30 degrees to 110 degrees. @@ -109,7 +109,7 @@ first person field of view -------------------------- :Type: floating point -:Range: 0-360 +:Range: 1-179 :Default: 55.0 This setting controls the field of view for first person meshes such as the player's hands and held objects. From 5ec28e09c9707f586812bd7d1b1823028f866e07 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 060/106] Do not recreate shaders to update transparency --- apps/openmw/mwrender/animation.cpp | 6 +----- apps/openmw/mwrender/npcanimation.cpp | 12 ------------ 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index c21cab6c9..8ec75b1ce 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1798,17 +1798,13 @@ namespace MWRender material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,alpha)); material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); - + stateset->addUniform(new osg::Uniform("colorMode", 0), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); mObjectRoot->setStateSet(stateset); - - mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } } else { mObjectRoot->setStateSet(nullptr); - - mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } setRenderBin(); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index f1ebc80df..0b421a753 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -649,9 +649,6 @@ void NpcAnimation::updateParts() if (wasArrowAttached) attachArrow(); - - if (mAlpha != 1.f) - mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } @@ -918,9 +915,6 @@ void NpcAnimation::showWeapons(bool showWeapon) attachArrow(); } } - // Note: we will need to recreate shaders later if we use weapon sheathing anyway, so there is no point to update them here - if (mAlpha != 1.f && !mWeaponSheathing) - mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } else { @@ -932,10 +926,6 @@ void NpcAnimation::showWeapons(bool showWeapon) updateHolsteredWeapon(!mShowWeapons); updateQuiver(); - - // Recreate shaders for invisible actors, otherwise sheath nodes will be visible - if (mAlpha != 1.f && mWeaponSheathing) - mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } void NpcAnimation::showCarriedLeft(bool show) @@ -953,8 +943,6 @@ void NpcAnimation::showCarriedLeft(bool show) if (iter->getTypeName() == typeid(ESM::Light).name() && mObjectParts[ESM::PRT_Shield]) addExtraLight(mObjectParts[ESM::PRT_Shield]->getNode()->asGroup(), iter->get()->mBase); } - if (mAlpha != 1.f) - mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } else removeIndividualPart(ESM::PRT_Shield); From aa5a071aef59d4aa4a3f4b8f664d0a322d28f130 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 061/106] Delete composite map layers in the background thread --- apps/openmw/mwrender/renderingmanager.cpp | 1 + components/terrain/compositemaprenderer.cpp | 21 +++++++++++++++++++++ components/terrain/compositemaprenderer.hpp | 13 +++++++++++++ components/terrain/quadtreeworld.hpp | 2 +- components/terrain/world.cpp | 5 +++++ components/terrain/world.hpp | 8 ++++++++ 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5f35b401e..7333a4c2c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -314,6 +314,7 @@ namespace MWRender mTerrain->setDefaultViewer(mViewer->getCamera()); mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); + mTerrain->setWorkQueue(mWorkQueue.get()); mCamera.reset(new Camera(mViewer->getCamera())); diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index 0ef649197..39d00db36 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -6,6 +6,9 @@ #include #include +#include +#include + #include namespace Terrain @@ -20,9 +23,20 @@ CompositeMapRenderer::CompositeMapRenderer() mFBO = new osg::FrameBufferObject; + mUnrefQueue = new SceneUtil::UnrefQueue; + getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); } +CompositeMapRenderer::~CompositeMapRenderer() +{ +} + +void CompositeMapRenderer::setWorkQueue(SceneUtil::WorkQueue* workQueue) +{ + mWorkQueue = workQueue; +} + void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const { double dt = mTimer.time_s(); @@ -35,6 +49,9 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const mCompiled.clear(); + if (mWorkQueue) + mUnrefQueue->flush(mWorkQueue.get()); + OpenThreads::ScopedLock lock(mMutex); if (mImmediateCompileSet.empty() && mCompileSet.empty()) @@ -122,6 +139,10 @@ void CompositeMapRenderer::compile(CompositeMap &compositeMap, osg::RenderInfo & ++compositeMap.mCompiled; + if (mWorkQueue) + { + mUnrefQueue->push(compositeMap.mDrawables[i]); + } compositeMap.mDrawables[i] = nullptr; if (timeLeft) diff --git a/components/terrain/compositemaprenderer.hpp b/components/terrain/compositemaprenderer.hpp index 54e158ed4..5c81fdca6 100644 --- a/components/terrain/compositemaprenderer.hpp +++ b/components/terrain/compositemaprenderer.hpp @@ -14,6 +14,12 @@ namespace osg class Texture2D; } +namespace SceneUtil +{ + class UnrefQueue; + class WorkQueue; +} + namespace Terrain { @@ -34,11 +40,15 @@ namespace Terrain { public: CompositeMapRenderer(); + ~CompositeMapRenderer(); virtual void drawImplementation(osg::RenderInfo& renderInfo) const; void compile(CompositeMap& compositeMap, osg::RenderInfo& renderInfo, double* timeLeft) const; + /// Set a WorkQueue to delete compiled composite map layers in the background thread + void setWorkQueue(SceneUtil::WorkQueue* workQueue); + /// Set the available time in seconds for compiling (non-immediate) composite maps each frame void setMinimumTimeAvailableForCompile(double time); @@ -58,6 +68,9 @@ namespace Terrain double mMinimumTimeAvailable; mutable osg::Timer mTimer; + osg::ref_ptr mUnrefQueue; + osg::ref_ptr mWorkQueue; + typedef std::set > CompileSet; mutable CompileSet mCompileSet; diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index bb09048c2..3e4055671 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -16,7 +16,7 @@ namespace Terrain class RootNode; class ViewDataMap; - /// @brief Terrain implementation that loads cells into a Quad Tree, with geometry LOD and texture LOD. The entire world is displayed at all times. + /// @brief Terrain implementation that loads cells into a Quad Tree, with geometry LOD and texture LOD. class QuadTreeWorld : public TerrainGrid // note: derived from TerrainGrid is only to render default cells (see loadCell) { public: diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 6a8322bb5..da3bdb5c2 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -66,6 +66,11 @@ World::~World() delete mStorage; } +void World::setWorkQueue(SceneUtil::WorkQueue* workQueue) +{ + mCompositeMapRenderer->setWorkQueue(workQueue); +} + void World::setBordersVisible(bool visible) { mBorderVisible = visible; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 83a2655fd..ba5cf6788 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -24,6 +24,11 @@ namespace Resource class ResourceSystem; } +namespace SceneUtil +{ + class WorkQueue; +} + namespace Terrain { class Storage; @@ -59,6 +64,9 @@ namespace Terrain World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask); virtual ~World(); + /// Set a WorkQueue to delete objects in the background thread. + void setWorkQueue(SceneUtil::WorkQueue* workQueue); + /// See CompositeMapRenderer::setTargetFrameRate void setTargetFrameRate(float rate); From 02242ce66b0bd5b9e8df64d67284117b5925919d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 18 Mar 2019 17:07:27 +0300 Subject: [PATCH 062/106] Fix shader specular lighting (again) --- files/shaders/lighting.glsl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/files/shaders/lighting.glsl b/files/shaders/lighting.glsl index 6de34bc07..782d17d4e 100644 --- a/files/shaders/lighting.glsl +++ b/files/shaders/lighting.glsl @@ -74,9 +74,10 @@ vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, out vec3 shadow vec3 getSpecular(vec3 viewNormal, vec3 viewDirection, float shininess, vec3 matSpec) { vec3 lightDir = normalize(gl_LightSource[0].position.xyz); - float NdotL = max(dot(viewNormal, lightDir), 0.0); - if (NdotL < 0.0) + float NdotL = dot(viewNormal, lightDir); + if (NdotL <= 0.0) return vec3(0.,0.,0.); vec3 halfVec = normalize(lightDir - viewDirection); - return pow(max(dot(viewNormal, halfVec), 0.0), shininess) * gl_LightSource[0].specular.xyz * matSpec; + float NdotH = dot(viewNormal, halfVec); + return pow(max(NdotH, 0.0), max(1e-4, shininess)) * gl_LightSource[0].specular.xyz * matSpec; } From 9b65f0dbca5ecb467221e926a9630ce80bb7aaab Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 18 Mar 2019 19:21:07 +0300 Subject: [PATCH 063/106] Vsync tweaks Try to use adaptive vsync if available Don't use vsync if unavailable --- components/sdlutil/sdlgraphicswindow.cpp | 24 ++++++++++++++++++++++-- components/sdlutil/sdlgraphicswindow.hpp | 3 +++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/components/sdlutil/sdlgraphicswindow.cpp b/components/sdlutil/sdlgraphicswindow.cpp index dc6129e43..3b76e6887 100644 --- a/components/sdlutil/sdlgraphicswindow.cpp +++ b/components/sdlutil/sdlgraphicswindow.cpp @@ -113,7 +113,7 @@ void GraphicsWindowSDL2::init() return; } - SDL_GL_SetSwapInterval(_traits->vsync ? 1 : 0); + setSwapInterval(_traits->vsync); SDL_GL_MakeCurrent(oldWin, oldCtx); @@ -194,11 +194,31 @@ void GraphicsWindowSDL2::setSyncToVBlank(bool on) SDL_GL_MakeCurrent(mWindow, mContext); - SDL_GL_SetSwapInterval(on ? 1 : 0); + setSwapInterval(on); SDL_GL_MakeCurrent(oldWin, oldCtx); } +void GraphicsWindowSDL2::setSwapInterval(bool enable) +{ + if (enable) + { + if (SDL_GL_SetSwapInterval(-1) == -1) + { + OSG_NOTICE << "Adaptive vsync unsupported" << std::endl; + if (SDL_GL_SetSwapInterval(1) == -1) + { + OSG_NOTICE << "Vertical synchronization unsupported, disabling" << std::endl; + SDL_GL_SetSwapInterval(0); + } + } + } + else + { + SDL_GL_SetSwapInterval(0); + } +} + void GraphicsWindowSDL2::raiseWindow() { SDL_RaiseWindow(mWindow); diff --git a/components/sdlutil/sdlgraphicswindow.hpp b/components/sdlutil/sdlgraphicswindow.hpp index 4b48b4073..dd8076776 100644 --- a/components/sdlutil/sdlgraphicswindow.hpp +++ b/components/sdlutil/sdlgraphicswindow.hpp @@ -80,6 +80,9 @@ public: SDL_Window *mWindow; }; + +private: + void setSwapInterval(bool enable); }; } From fd5e9cf27176c3b7a120b46cfd9d63424e9476ef Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 19 Mar 2019 09:11:14 +0400 Subject: [PATCH 064/106] Catch possible boost::bad_any_cast exception --- apps/niftest/niftest.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index e3f2b89f8..f848ae330 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -102,6 +102,17 @@ bool parseOptions (int argc, char** argv, std::vector& files) bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv). options(desc).positional(p).run(); bpo::store(valid_opts, variables); + bpo::notify(variables); + if (variables.count ("help")) + { + std::cout << desc << std::endl; + return false; + } + if (variables.count("input-file")) + { + files = variables["input-file"].as< std::vector >(); + return true; + } } catch(std::exception &e) { @@ -110,18 +121,6 @@ bool parseOptions (int argc, char** argv, std::vector& files) return false; } - bpo::notify(variables); - if (variables.count ("help")) - { - std::cout << desc << std::endl; - return false; - } - if (variables.count("input-file")) - { - files = variables["input-file"].as< std::vector >(); - return true; - } - std::cout << "No input files or directories specified!" << std::endl; std::cout << desc << std::endl; return false; From 7995a9267291a785f024a10f43710f5e625fb09e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 19 Mar 2019 09:12:31 +0400 Subject: [PATCH 065/106] Initialize missing variables --- apps/openmw/engine.cpp | 1 + components/nifosg/controller.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index fb7be22b2..181ebd822 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -226,6 +226,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mActivationDistanceOverride(-1) , mGrab(true) , mExportFonts(false) + , mRandomSeed(0) , mScriptContext (0) , mFSStrict (false) , mScriptBlacklistUse (true) diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 934b3b914..4029e9b15 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -311,10 +311,11 @@ void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv) RollController::RollController(const Nif::NiFloatData *data) : mData(data->mKeyList, 1.f) + , mStartingTime(0) { } -RollController::RollController() +RollController::RollController() : mStartingTime(0) { } From b2fca46206765553d719702fdddafde0a9333e48 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 19 Mar 2019 09:14:07 +0400 Subject: [PATCH 066/106] Fix a couple of minor issues in shadows --- components/sceneutil/mwshadowtechnique.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index b9d8e4bd3..92bf44ec1 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -777,6 +777,7 @@ MWShadowTechnique::MWShadowTechnique(const MWShadowTechnique& vdsm, const osg::C ShadowTechnique(vdsm,copyop) { _shadowRecievingPlaceholderStateSet = new osg::StateSet; + _enableShadows = vdsm._enableShadows; } MWShadowTechnique::~MWShadowTechnique() @@ -2011,18 +2012,20 @@ struct ConvexHull Vertices unwantedEdgeEnds = findInternalEdges(vertex, connectedVertices); for (auto edgeEnd : unwantedEdgeEnds) { - for (auto itr = _edges.begin(); itr != _edges.end(); ++itr) + for (auto itr = _edges.begin(); itr != _edges.end();) { if (*itr == Edge(vertex, edgeEnd)) { - _edges.erase(itr); + itr = _edges.erase(itr); break; } else if (*itr == Edge(edgeEnd, vertex)) { - _edges.erase(itr); + itr = _edges.erase(itr); break; } + else + ++itr; } } } From 7501f18d212f7cbd9dc1e7bd4b348290b0f2a651 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 19 Mar 2019 09:16:03 +0400 Subject: [PATCH 067/106] Avoid the 'structurally dead code' warning --- apps/openmw/mwinput/inputmanagerimp.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 6f615564c..b7014c99d 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -196,8 +196,6 @@ namespace MWInput void InputManager::handleGuiArrowKey(int action) { - // Temporary shut-down of this function until deemed necessary. - return; if (SDL_IsTextInputActive()) return; @@ -402,7 +400,8 @@ namespace MWInput case A_MoveRight: case A_MoveForward: case A_MoveBackward: - handleGuiArrowKey(action); + // Temporary shut-down of this function until deemed necessary. + //handleGuiArrowKey(action); break; case A_Journal: toggleJournal (); From ccb325c663bea1e5a8f687cade18168a7d17fae1 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 20 Mar 2019 00:19:26 +0300 Subject: [PATCH 068/106] Add override to fix warnings --- apps/opencs/view/render/terrainstorage.hpp | 6 +++--- apps/openmw/mwrender/terrainstorage.hpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 949311248..6c3151e8d 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -18,10 +18,10 @@ namespace CSVRender private: const CSMWorld::Data& mData; - virtual osg::ref_ptr getLand (int cellX, int cellY); - virtual const ESM::LandTexture* getLandTexture(int index, short plugin); + virtual osg::ref_ptr getLand (int cellX, int cellY) override; + virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override; - virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); + virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; }; } diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index cae56d3aa..14bed7b7b 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -20,13 +20,13 @@ namespace MWRender TerrainStorage(Resource::ResourceSystem* resourceSystem, const std::string& normalMapPattern = "", const std::string& normalHeightMapPatteern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); ~TerrainStorage(); - virtual osg::ref_ptr getLand (int cellX, int cellY); - virtual const ESM::LandTexture* getLandTexture(int index, short plugin); + virtual osg::ref_ptr getLand (int cellX, int cellY) override; + virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override; virtual bool hasData(int cellX, int cellY) override; /// Get bounds of the whole terrain in cell units - virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); + virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; LandManager* getLandManager() const; From e171d34192b9ffca36a6d7842363fa2c4f6f14c3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 20 Mar 2019 11:52:47 +0400 Subject: [PATCH 069/106] Init NPC type properly in the NpcAnimation --- apps/openmw/mwrender/npcanimation.cpp | 30 ++++++++++++++++----------- apps/openmw/mwrender/npcanimation.hpp | 2 ++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index f1ebc80df..877c0d99b 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -238,6 +238,18 @@ void HeadAnimationTime::setBlinkStop(float value) // ---------------------------------------------------- +NpcAnimation::NpcType NpcAnimation::getNpcType() +{ + const MWWorld::Class &cls = mPtr.getClass(); + NpcAnimation::NpcType curType = Type_Normal; + if (cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0) + curType = Type_Vampire; + if (cls.getNpcStats(mPtr).isWerewolf()) + curType = Type_Werewolf; + + return curType; +} + static NpcAnimation::PartBoneMap createPartListMap() { NpcAnimation::PartBoneMap result; @@ -283,7 +295,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr par mViewMode(viewMode), mShowWeapons(false), mShowCarriedLeft(true), - mNpcType(Type_Normal), + mNpcType(getNpcType()), mFirstPersonFieldOfView(firstPersonFieldOfView), mSoundsDisabled(disableSounds), mAccurateAiming(false), @@ -431,8 +443,9 @@ void NpcAnimation::updateNpcBase() const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mNpc->mRace); - bool isWerewolf = (mNpcType == Type_Werewolf); - bool isVampire = (mNpcType == Type_Vampire); + NpcType curType = getNpcType(); + bool isWerewolf = (curType == Type_Werewolf); + bool isVampire = (curType == Type_Vampire); bool isFemale = !mNpc->isMale(); if (isWerewolf) @@ -517,14 +530,7 @@ void NpcAnimation::updateParts() if (!mObjectRoot.get()) return; - const MWWorld::Class &cls = mPtr.getClass(); - - NpcType curType = Type_Normal; - if (cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0) - curType = Type_Vampire; - if (cls.getNpcStats(mPtr).isWerewolf()) - curType = Type_Werewolf; - + NpcType curType = getNpcType(); if (curType != mNpcType) { mNpcType = curType; @@ -632,7 +638,7 @@ void NpcAnimation::updateParts() showWeapons(mShowWeapons); showCarriedLeft(mShowCarriedLeft); - bool isWerewolf = (mNpcType == Type_Werewolf); + bool isWerewolf = (getNpcType() == Type_Werewolf); std::string race = (isWerewolf ? "werewolf" : Misc::StringUtils::lowerCase(mNpc->mRace)); const std::vector &parts = getBodyParts(race, !mNpc->isMale(), mViewMode == VM_FirstPerson, isWerewolf); diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 1ec0dfa59..bed07dcdc 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -74,6 +74,8 @@ private: void updateNpcBase(); + NpcType getNpcType(); + PartHolderPtr insertBoundedPart(const std::string &model, const std::string &bonename, const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=nullptr); From be12d241da2aeeba92298e05e321cae1b78fb3d9 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 20 Mar 2019 12:19:35 +0400 Subject: [PATCH 070/106] Reset current attack during force update (bug #4922) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e56632db7..86eb3712e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Bug #4896: Title screen music doesn't loop Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5 Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used. + Bug #4922: Werewolves can not attack if the transformation happens during attack Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Feature #3610: Option to invert X axis diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d1f8562f4..ffbf5dd09 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2554,6 +2554,13 @@ void CharacterController::forceStateUpdate() return; clearAnimQueue(); + // Make sure we canceled the current attack or spellcasting, + // because we disabled attack animations anyway. + mCastingManualSpell = false; + mAttackingOrSpell = false; + if (mUpperBodyState != UpperCharState_Nothing) + mUpperBodyState = UpperCharState_WeapEquiped; + refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true); if(mDeathState != CharState_None) From 695cd5bb49d12b552fa9c125a6576cd5a602f410 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 20 Mar 2019 00:50:05 +0300 Subject: [PATCH 071/106] Minor NpcAnimation cleanup --- apps/openmw/mwrender/npcanimation.cpp | 165 ++++++++++++-------------- 1 file changed, 77 insertions(+), 88 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 877c0d99b..6baf99e56 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -54,10 +54,8 @@ std::string getVampireHead(const std::string& race, bool female) if (sVampireMapping.find(thisCombination) == sVampireMapping.end()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const MWWorld::Store &partStore = store.get(); - for(MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) + for (const ESM::BodyPart& bodypart : store.get()) { - const ESM::BodyPart& bodypart = *it; if (!bodypart.mData.mVampire) continue; if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) @@ -68,12 +66,11 @@ std::string getVampireHead(const std::string& race, bool female) continue; if (!Misc::StringUtils::ciEqual(bodypart.mRace, race)) continue; - sVampireMapping[thisCombination] = &*it; + sVampireMapping[thisCombination] = &bodypart; } } - if (sVampireMapping.find(thisCombination) == sVampireMapping.end()) - sVampireMapping[thisCombination] = nullptr; + sVampireMapping.emplace(thisCombination, nullptr); const ESM::BodyPart* bodyPart = sVampireMapping[thisCombination]; if (!bodyPart) @@ -448,37 +445,34 @@ void NpcAnimation::updateNpcBase() bool isVampire = (curType == Type_Vampire); bool isFemale = !mNpc->isMale(); - if (isWerewolf) + mHeadModel.clear(); + mHairModel.clear(); + + std::string headName = isWerewolf ? "WerewolfHead" : mNpc->mHead; + std::string hairName = isWerewolf ? "WerewolfHair" : mNpc->mHair; + + if (!headName.empty()) { - mHeadModel = "meshes\\" + store.get().find("WerewolfHead")->mModel; - mHairModel = "meshes\\" + store.get().find("WerewolfHair")->mModel; + const ESM::BodyPart* bp = store.get().search(headName); + if (bp) + mHeadModel = "meshes\\" + bp->mModel; + else + Log(Debug::Warning) << "Warning: Failed to load body part '" << headName << "'"; } - else - { - mHeadModel = ""; - const std::string& vampireHead = getVampireHead(mNpc->mRace, isFemale); - if (isVampire && !vampireHead.empty()) - mHeadModel = vampireHead; - else if (!mNpc->mHead.empty()) - { - const ESM::BodyPart* bp = store.get().search(mNpc->mHead); - if (bp) - mHeadModel = "meshes\\" + bp->mModel; - else - Log(Debug::Warning) << "Warning: Failed to load body part '" << mNpc->mHead << "'"; - } - mHairModel = ""; - if (!mNpc->mHair.empty()) - { - const ESM::BodyPart* bp = store.get().search(mNpc->mHair); - if (bp) - mHairModel = "meshes\\" + bp->mModel; - else - Log(Debug::Warning) << "Warning: Failed to load body part '" << mNpc->mHair << "'"; - } + if (!hairName.empty()) + { + const ESM::BodyPart* bp = store.get().search(hairName); + if (bp) + mHairModel = "meshes\\" + bp->mModel; + else + Log(Debug::Warning) << "Warning: Failed to load body part '" << hairName << "'"; } + const std::string& vampireHead = getVampireHead(mNpc->mRace, isFemale); + if (!isWerewolf && isVampire && !vampireHead.empty()) + mHeadModel = vampireHead; + bool is1stPerson = mViewMode == VM_FirstPerson; bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; @@ -486,7 +480,7 @@ void NpcAnimation::updateNpcBase() defaultSkeleton = Misc::ResourceHelpers::correctActorModelPath(defaultSkeleton, mResourceSystem->getVFS()); std::string smodel = defaultSkeleton; - if (!is1stPerson && !isWerewolf & !mNpc->mModel.empty()) + if (!is1stPerson && !isWerewolf && !mNpc->mModel.empty()) smodel = Misc::ResourceHelpers::correctActorModelPath("meshes\\" + mNpc->mModel, mResourceSystem->getVFS()); setObjectRoot(smodel, true, true, false); @@ -736,10 +730,7 @@ void NpcAnimation::removePartGroup(int group) bool NpcAnimation::isFirstPersonPart(const ESM::BodyPart* bodypart) { - return (bodypart->mId.size() >= 3) - && bodypart->mId[bodypart->mId.size()-3] == '1' - && bodypart->mId[bodypart->mId.size()-2] == 's' - && bodypart->mId[bodypart->mId.size()-1] == 't'; + return bodypart->mId.size() >= 3 && bodypart->mId.substr(bodypart->mId.size()-3, 3) == "1st"; } bool NpcAnimation::isFemalePart(const ESM::BodyPart* bodypart) @@ -799,16 +790,16 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g osg::Object* obj = node->getUserDataContainer()->getUserObject(i); if (NifOsg::TextKeyMapHolder* keys = dynamic_cast(obj)) { - for (NifOsg::TextKeyMap::const_iterator it = keys->mTextKeys.begin(); it != keys->mTextKeys.end(); ++it) + for (const std::pair &key : keys->mTextKeys) { - if (Misc::StringUtils::ciEqual(it->second, "talk: start")) - mHeadAnimationTime->setTalkStart(it->first); - if (Misc::StringUtils::ciEqual(it->second, "talk: stop")) - mHeadAnimationTime->setTalkStop(it->first); - if (Misc::StringUtils::ciEqual(it->second, "blink: start")) - mHeadAnimationTime->setBlinkStart(it->first); - if (Misc::StringUtils::ciEqual(it->second, "blink: stop")) - mHeadAnimationTime->setBlinkStop(it->first); + if (Misc::StringUtils::ciEqual(key.second, "talk: start")) + mHeadAnimationTime->setTalkStart(key.first); + if (Misc::StringUtils::ciEqual(key.second, "talk: stop")) + mHeadAnimationTime->setTalkStop(key.first); + if (Misc::StringUtils::ciEqual(key.second, "blink: start")) + mHeadAnimationTime->setBlinkStart(key.first); + if (Misc::StringUtils::ciEqual(key.second, "blink: stop")) + mHeadAnimationTime->setBlinkStop(key.first); } break; @@ -834,16 +825,15 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector &partStore = store.get(); const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : ""; - std::vector::const_iterator part(parts.begin()); - for(;part != parts.end();++part) + for(const ESM::PartReference& part : parts) { - const ESM::BodyPart *bodypart = 0; - if(!mNpc->isMale() && !part->mFemale.empty()) + const ESM::BodyPart *bodypart = nullptr; + if(!mNpc->isMale() && !part.mFemale.empty()) { - bodypart = partStore.search(part->mFemale+ext); + bodypart = partStore.search(part.mFemale+ext); if(!bodypart && mViewMode == VM_FirstPerson) { - bodypart = partStore.search(part->mFemale); + bodypart = partStore.search(part.mFemale); if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand || bodypart->mData.mPart == ESM::BodyPart::MP_Wrist || bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || @@ -851,14 +841,14 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormFemale << "'"; + Log(Debug::Warning) << "Warning: Failed to find body part '" << part.mFemale << "'"; } - if(!bodypart && !part->mMale.empty()) + if(!bodypart && !part.mMale.empty()) { - bodypart = partStore.search(part->mMale+ext); + bodypart = partStore.search(part.mMale+ext); if(!bodypart && mViewMode == VM_FirstPerson) { - bodypart = partStore.search(part->mMale); + bodypart = partStore.search(part.mMale); if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand || bodypart->mData.mPart == ESM::BodyPart::MP_Wrist || bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || @@ -866,13 +856,13 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormMale << "'"; + Log(Debug::Warning) << "Warning: Failed to find body part '" << part.mMale << "'"; } if(bodypart) - addOrReplaceIndividualPart((ESM::PartReferenceType)part->mPart, group, priority, "meshes\\"+bodypart->mModel, enchantedGlow, glowColor); + addOrReplaceIndividualPart((ESM::PartReferenceType)part.mPart, group, priority, "meshes\\"+bodypart->mModel, enchantedGlow, glowColor); else - reserveIndividualPart((ESM::PartReferenceType)part->mPart, group, priority); + reserveIndividualPart((ESM::PartReferenceType)part.mPart, group, priority); } } @@ -891,7 +881,7 @@ void NpcAnimation::addControllers() osg::MatrixTransform* node = found->second.get(); mFirstPersonNeckController = new NeckController(mObjectRoot.get()); node->addUpdateCallback(mFirstPersonNeckController); - mActiveControllers.insert(std::make_pair(node, mFirstPersonNeckController)); + mActiveControllers.emplace(node, mFirstPersonNeckController); } } else if (mViewMode == VM_Normal) @@ -1093,40 +1083,39 @@ const std::vector& NpcAnimation::getBodyParts(const std:: std::vector& parts = sRaceMapping[std::make_pair(race, flags)]; typedef std::multimap BodyPartMapType; - static BodyPartMapType sBodyPartMap; - if(sBodyPartMap.empty()) + static const BodyPartMapType sBodyPartMap = { - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Neck, ESM::PRT_Neck)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Groin, ESM::PRT_Groin)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_RHand)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_LHand)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_RFoot)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_LFoot)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_RKnee)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_LKnee)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail)); - } + {ESM::BodyPart::MP_Neck, ESM::PRT_Neck}, + {ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass}, + {ESM::BodyPart::MP_Groin, ESM::PRT_Groin}, + {ESM::BodyPart::MP_Hand, ESM::PRT_RHand}, + {ESM::BodyPart::MP_Hand, ESM::PRT_LHand}, + {ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist}, + {ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist}, + {ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm}, + {ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm}, + {ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm}, + {ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm}, + {ESM::BodyPart::MP_Foot, ESM::PRT_RFoot}, + {ESM::BodyPart::MP_Foot, ESM::PRT_LFoot}, + {ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle}, + {ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle}, + {ESM::BodyPart::MP_Knee, ESM::PRT_RKnee}, + {ESM::BodyPart::MP_Knee, ESM::PRT_LKnee}, + {ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg}, + {ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg}, + {ESM::BodyPart::MP_Tail, ESM::PRT_Tail} + }; parts.resize(ESM::PRT_Count, nullptr); + if (werewolf) + return parts; + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const MWWorld::Store &partStore = store.get(); - for(MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) + + for(const ESM::BodyPart& bodypart : store.get()) { - if(werewolf) - break; - const ESM::BodyPart& bodypart = *it; if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) continue; if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) From c2986b3bd72457c0e3f035c973fa2db06beb19b7 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 072/106] Do not block the loading thread while compiling composite maps in the draw thread --- components/terrain/compositemaprenderer.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index 39d00db36..3fc56a882 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -59,26 +59,29 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const while (!mImmediateCompileSet.empty()) { - CompositeMap* node = *mImmediateCompileSet.begin(); + osg::ref_ptr node = *mImmediateCompileSet.begin(); mCompiled.insert(node); + mImmediateCompileSet.erase(node); + mMutex.unlock(); compile(*node, renderInfo, nullptr); - - mImmediateCompileSet.erase(mImmediateCompileSet.begin()); + mMutex.lock(); } double timeLeft = availableTime; while (!mCompileSet.empty() && timeLeft > 0) { - CompositeMap* node = *mCompileSet.begin(); + osg::ref_ptr node = *mCompileSet.begin(); + mMutex.unlock(); compile(*node, renderInfo, &timeLeft); + mMutex.lock(); if (node->mCompiled >= node->mDrawables.size()) { mCompiled.insert(node); - mCompileSet.erase(mCompileSet.begin()); + mCompileSet.erase(node); } } mTimer.setStartTick(); From 32da9a8ab10fb04895b74a113509e22d793c9615 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 20 Mar 2019 23:32:01 +0000 Subject: [PATCH 073/106] Use irreflexive, asymmetric comparator as required by the spec. --- apps/opencs/model/prefs/shortcuteventhandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/prefs/shortcuteventhandler.cpp b/apps/opencs/model/prefs/shortcuteventhandler.cpp index e42cb5da1..07c48fcaa 100644 --- a/apps/opencs/model/prefs/shortcuteventhandler.cpp +++ b/apps/opencs/model/prefs/shortcuteventhandler.cpp @@ -195,7 +195,7 @@ namespace CSMPrefs // Only activate the best match; in exact conflicts, this will favor the first shortcut added. if (!potentials.empty()) { - std::sort(potentials.begin(), potentials.end(), ShortcutEventHandler::sort); + std::stable_sort(potentials.begin(), potentials.end(), ShortcutEventHandler::sort); Shortcut* shortcut = potentials.front().second; if (shortcut->getModifierStatus() && shortcut->getSecondaryMode() == Shortcut::SM_Replace) @@ -325,7 +325,7 @@ namespace CSMPrefs if (left.first == Matches_WithMod && right.first == Matches_NoMod) return true; else - return left.second->getPosition() >= right.second->getPosition(); + return left.second->getPosition() > right.second->getPosition(); } void ShortcutEventHandler::widgetDestroyed() From cad45e96ac4b9f66448b5a8ebde23433e0487957 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 21 Mar 2019 08:42:48 +0400 Subject: [PATCH 074/106] Remove redundant mCompile set - we do not use data from it anyway --- components/terrain/compositemaprenderer.cpp | 4 ---- components/terrain/compositemaprenderer.hpp | 2 -- 2 files changed, 6 deletions(-) diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index 3fc56a882..355ef6c16 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -47,8 +47,6 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const double availableTime = std::max((targetFrameTime - dt)*conservativeTimeRatio, mMinimumTimeAvailable); - mCompiled.clear(); - if (mWorkQueue) mUnrefQueue->flush(mWorkQueue.get()); @@ -60,7 +58,6 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const while (!mImmediateCompileSet.empty()) { osg::ref_ptr node = *mImmediateCompileSet.begin(); - mCompiled.insert(node); mImmediateCompileSet.erase(node); mMutex.unlock(); @@ -80,7 +77,6 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const if (node->mCompiled >= node->mDrawables.size()) { - mCompiled.insert(node); mCompileSet.erase(node); } } diff --git a/components/terrain/compositemaprenderer.hpp b/components/terrain/compositemaprenderer.hpp index 5c81fdca6..201130e30 100644 --- a/components/terrain/compositemaprenderer.hpp +++ b/components/terrain/compositemaprenderer.hpp @@ -76,8 +76,6 @@ namespace Terrain mutable CompileSet mCompileSet; mutable CompileSet mImmediateCompileSet; - mutable CompileSet mCompiled; - mutable OpenThreads::Mutex mMutex; osg::ref_ptr mFBO; From 0e1f5f68b6947eb11d713208ca2a6b0281ba9bcb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 21 Mar 2019 08:52:13 +0400 Subject: [PATCH 075/106] Do not allow different threads to compile the same composite map --- components/terrain/compositemaprenderer.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index 355ef6c16..ee4a66fcd 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -70,14 +70,17 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const while (!mCompileSet.empty() && timeLeft > 0) { osg::ref_ptr node = *mCompileSet.begin(); + mCompileSet.erase(node); mMutex.unlock(); compile(*node, renderInfo, &timeLeft); mMutex.lock(); - if (node->mCompiled >= node->mDrawables.size()) + if (node->mCompiled < node->mDrawables.size()) { - mCompileSet.erase(node); + // We did not compile the map fully. + // Place it back to queue to continue work in the next time. + mCompileSet.insert(node); } } mTimer.setStartTick(); From b466bfee40068dba7c26c2d466d8e3236b07ab46 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 21 Mar 2019 10:27:50 +0400 Subject: [PATCH 076/106] Enable light sources directly to avoid virtual calls --- components/sceneutil/lightmanager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index f0fd0ef9f..c657e66fd 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -245,7 +245,8 @@ namespace SceneUtil osg::ref_ptr attr = new LightStateAttribute(mStartLight, lights); // don't use setAttributeAndModes, that does not support light indices! stateset->setAttribute(attr, osg::StateAttribute::ON); - stateset->setAssociatedModes(attr, osg::StateAttribute::ON); + for (unsigned int i=0; isetMode(GL_LIGHT0 + mStartLight + i, osg::StateAttribute::ON); // need to push some dummy attributes to ensure proper state tracking // lights need to reset to their default when the StateSet is popped From af47ec7756ff29542aa29078ae5d1fa5deeb3b93 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 21 Feb 2019 20:01:40 +0300 Subject: [PATCH 077/106] Skip extra text after variable declaration (bug #4867) --- CHANGELOG.md | 1 + components/compiler/declarationparser.cpp | 38 ++++++++++++++++------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86eb3712e..312cb7b5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ Bug #4828: Potion looping effects VFX are not shown for NPCs Bug #4837: CTD when a mesh with NiLODNode root node with particles is loaded Bug #4860: Actors outside of processing range visible for one frame after spawning + Bug #4867: Arbitrary text after local variable declarations breaks script compilation Bug #4876: AI ratings handling inconsistencies Bug #4877: Startup script executes only on a new game start Bug #4888: Global variable stray explicit reference calls break script compilation diff --git a/components/compiler/declarationparser.cpp b/components/compiler/declarationparser.cpp index e85c8c3ec..1e3bc0585 100644 --- a/components/compiler/declarationparser.cpp +++ b/components/compiler/declarationparser.cpp @@ -22,20 +22,20 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke char type = mLocals.getType (name2); if (type!=' ') - { - /// \todo add option to make re-declared local variables an error - getErrorHandler().warning ("ignoring local variable re-declaration", - loc); - - mState = State_End; - return true; - } - - mLocals.declare (mType, name2); + getErrorHandler().warning ("ignoring local variable re-declaration", loc); + else + mLocals.declare (mType, name2); mState = State_End; return true; } + else if (mState==State_End) + { + getErrorHandler().warning ("Ignoring extra text after local variable declaration", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return false; + } return Parser::parseName (name, loc, scanner); } @@ -61,17 +61,31 @@ bool Compiler::DeclarationParser::parseKeyword (int keyword, const TokenLoc& loc else if (mState==State_Name) { // allow keywords to be used as local variable names. MW script compiler, you suck! - /// \todo option to disable this atrocity. return parseName (loc.mLiteral, loc, scanner); } + else if (mState==State_End) + { + getErrorHandler().warning ("Ignoring extra text after local variable declaration", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return false; + } return Parser::parseKeyword (keyword, loc, scanner); } bool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { - if (code==Scanner::S_newline && mState==State_End) + if (mState==State_End) + { + if (code!=Scanner::S_newline) + { + getErrorHandler().warning ("Ignoring extra text after local variable declaration", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + } return false; + } return Parser::parseSpecial (code, loc, scanner); } From fa7b304e78595d34365eb049007f97e01892839b Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 21 Mar 2019 23:08:35 +0300 Subject: [PATCH 078/106] Use auto for map value_type in range-based for loops To avoid implicit call of copy constructor for pair to pair conversion. --- apps/esmtool/record.cpp | 2 +- apps/opencs/model/world/data.cpp | 4 ++-- apps/openmw/mwrender/npcanimation.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index cfd658fc9..c5153dadb 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -739,7 +739,7 @@ void Record::print() std::cout << " Faction Reaction: " << mData.mData.mRankData[i].mFactReaction << std::endl; } - for (const std::pair &reaction : mData.mReactions) + for (const auto &reaction : mData.mReactions) std::cout << " Reaction: " << reaction.second << " = " << reaction.first << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index da46ea876..8fa449b8f 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -1006,7 +1006,7 @@ void CSMWorld::Data::loadFallbackEntries() std::make_pair("PrisonMarker", "marker_prison.nif") }; - for (const std::pair &marker : staticMarkers) + for (const auto &marker : staticMarkers) { if (mReferenceables.searchId (marker.first)==-1) { @@ -1020,7 +1020,7 @@ void CSMWorld::Data::loadFallbackEntries() } } - for (const std::pair &marker : doorMarkers) + for (const auto &marker : doorMarkers) { if (mReferenceables.searchId (marker.first)==-1) { diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6baf99e56..5fad40a9a 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -790,7 +790,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g osg::Object* obj = node->getUserDataContainer()->getUserObject(i); if (NifOsg::TextKeyMapHolder* keys = dynamic_cast(obj)) { - for (const std::pair &key : keys->mTextKeys) + for (const auto &key : keys->mTextKeys) { if (Misc::StringUtils::ciEqual(key.second, "talk: start")) mHeadAnimationTime->setTalkStart(key.first); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d8576800d..24e8cb43c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -470,7 +470,7 @@ namespace MWWorld gmst["iWereWolfBounty"] = ESM::Variant(1000); gmst["fCombatDistanceWerewolfMod"] = ESM::Variant(0.3f); - for (const std::pair ¶ms : gmst) + for (const auto ¶ms : gmst) { if (!mStore.get().search(params.first)) { @@ -500,7 +500,7 @@ namespace MWWorld globals["crimegoldturnin"] = ESM::Variant(0); globals["pchasturnin"] = ESM::Variant(0); - for (const std::pair ¶ms : globals) + for (const auto ¶ms : globals) { if (!mStore.get().search(params.first)) { @@ -519,7 +519,7 @@ namespace MWWorld statics["templemarker"] = "marker_temple.nif"; statics["travelmarker"] = "marker_travel.nif"; - for (const std::pair ¶ms : statics) + for (const auto ¶ms : statics) { if (!mStore.get().search(params.first)) { @@ -533,7 +533,7 @@ namespace MWWorld std::map doors; doors["prisonmarker"] = "marker_prison.nif"; - for (const std::pair ¶ms : doors) + for (const auto ¶ms : doors) { if (!mStore.get().search(params.first)) { From e82d65a2c79aa61cdf242856a69f03d1f6b866ee Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 20 Mar 2019 01:02:59 +0300 Subject: [PATCH 079/106] Use if-continue to skip build path --- apps/openmw/mwmechanics/aiwander.cpp | 31 +++++++++++++++------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 405f36767..6b7d6bf16 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -309,22 +309,25 @@ namespace MWMechanics mDestination = osg::Vec3f(destinationX, destinationY, destinationZ); // Check if land creature will walk onto water or if water creature will swim onto land - if ((!isWaterCreature && !destinationIsAtWater(actor, mDestination)) || - (isWaterCreature && !destinationThroughGround(currentPosition, mDestination))) - { - const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); - mPathFinder.buildPath(actor, currentPosition, mDestination, actor.getCell(), - getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor)); - mPathFinder.addPointToPath(mDestination); + if (!isWaterCreature && destinationIsAtWater(actor, mDestination)) + continue; - if (mPathFinder.isPathConstructed()) - { - storage.setState(AiWanderStorage::Wander_Walking, true); - mHasDestination = true; - mUsePathgrid = false; - } - return; + if (isWaterCreature && destinationThroughGround(currentPosition, mDestination)) + continue; + + const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); + mPathFinder.buildPath(actor, currentPosition, mDestination, actor.getCell(), + getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor)); + mPathFinder.addPointToPath(mDestination); + + if (mPathFinder.isPathConstructed()) + { + storage.setState(AiWanderStorage::Wander_Walking, true); + mHasDestination = true; + mUsePathgrid = false; } + + break; } while (--attempts); } From e033b0c565849b80c0f92def1e11fc6fdea292ec Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 20 Mar 2019 01:04:07 +0300 Subject: [PATCH 080/106] Avoid build path through the ground for flying wandering creatures --- apps/openmw/mwmechanics/aiwander.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 6b7d6bf16..8c01c7b96 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -298,7 +298,8 @@ namespace MWMechanics const auto currentPosition = actor.getRefData().getPosition().asVec3(); std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here - bool isWaterCreature = actor.getClass().isPureWaterCreature(actor); + const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor); + const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor); do { // Determine a random location within radius of original position const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance; @@ -312,7 +313,7 @@ namespace MWMechanics if (!isWaterCreature && destinationIsAtWater(actor, mDestination)) continue; - if (isWaterCreature && destinationThroughGround(currentPosition, mDestination)) + if ((isWaterCreature || isFlyingCreature) && destinationThroughGround(currentPosition, mDestination)) continue; const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); From ebdff5d96e32fa6d8672d544e0741050892c45b0 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 20 Mar 2019 01:05:13 +0300 Subject: [PATCH 081/106] Check for height map when cast ray for AiWander path --- apps/openmw/mwmechanics/aiwander.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 8c01c7b96..8ed9cf8d2 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -13,6 +13,8 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwphysics/collisiontype.hpp" + #include "pathgrid.hpp" #include "creaturestats.hpp" #include "steering.hpp" @@ -346,8 +348,10 @@ namespace MWMechanics * Returns true if the start to end point travels through a collision point (land). */ bool AiWander::destinationThroughGround(const osg::Vec3f& startPoint, const osg::Vec3f& destination) { + const int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door; return MWBase::Environment::get().getWorld()->castRay(startPoint.x(), startPoint.y(), startPoint.z(), - destination.x(), destination.y(), destination.z()); + destination.x(), destination.y(), destination.z(), + mask); } void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) { From ff67a9e2333fbbee8144dcba1c70d2f3d3743098 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 20 Mar 2019 01:08:06 +0300 Subject: [PATCH 082/106] Build straight path for wandering flying and water creatures --- apps/openmw/mwmechanics/aiwander.cpp | 15 +++++++++++---- apps/openmw/mwmechanics/pathfinding.cpp | 7 +++++++ apps/openmw/mwmechanics/pathfinding.hpp | 2 ++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 8ed9cf8d2..e334f72da 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -318,10 +318,17 @@ namespace MWMechanics if ((isWaterCreature || isFlyingCreature) && destinationThroughGround(currentPosition, mDestination)) continue; - const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); - mPathFinder.buildPath(actor, currentPosition, mDestination, actor.getCell(), - getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor)); - mPathFinder.addPointToPath(mDestination); + if (isWaterCreature || isFlyingCreature) + { + mPathFinder.buildStraightPath(mDestination); + } + else + { + const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); + mPathFinder.buildPath(actor, currentPosition, mDestination, actor.getCell(), + getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor)); + mPathFinder.addPointToPath(mDestination); + } if (mPathFinder.isPathConstructed()) { diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index e0285be92..13c218008 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -269,6 +269,13 @@ namespace MWMechanics mPath.pop_front(); } + void PathFinder::buildStraightPath(const osg::Vec3f& endPoint) + { + mPath.clear(); + mPath.push_back(endPoint); + mConstructed = true; + } + void PathFinder::buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph) { diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 8a5b8338a..b59af44fd 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -72,6 +72,8 @@ namespace MWMechanics mCell = nullptr; } + void buildStraightPath(const osg::Vec3f& endPoint); + void buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph); From a65f60e1f1bc1f8bf35cddbdd2f809fb37a8cab9 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 20 Mar 2019 01:10:46 +0300 Subject: [PATCH 083/106] Build path only by navmesh for wandering near spawn --- apps/openmw/mwmechanics/aiwander.cpp | 5 ++--- apps/openmw/mwmechanics/pathfinding.cpp | 10 ++++++++++ apps/openmw/mwmechanics/pathfinding.hpp | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index e334f72da..353786677 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -325,9 +325,8 @@ namespace MWMechanics else { const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); - mPathFinder.buildPath(actor, currentPosition, mDestination, actor.getCell(), - getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor)); - mPathFinder.addPointToPath(mDestination); + mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, + getNavigatorFlags(actor)); } if (mPathFinder.isPathConstructed()) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 13c218008..db6b8d686 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -287,6 +287,16 @@ namespace MWMechanics mConstructed = true; } + void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, + const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags) + { + mPath.clear(); + + buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); + + mConstructed = true; + } + void PathFinder::buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index b59af44fd..1dc85c5e5 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -77,6 +77,9 @@ namespace MWMechanics void buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph); + void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, + const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags); + void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags); From 287433efa8ab146db98f31dc6dcbc58edd681813 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 20 Mar 2019 01:58:22 +0300 Subject: [PATCH 084/106] Stop walking for water and flying creatures after single stuck --- apps/openmw/mwmechanics/aiwander.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 353786677..db2057df3 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -47,6 +47,16 @@ namespace MWMechanics std::string("idle9"), }; + namespace + { + inline int getCountBeforeReset(const MWWorld::ConstPtr& actor) + { + if (actor.getClass().isPureWaterCreature(actor) || actor.getClass().isPureFlyingCreature(actor)) + return 1; + return COUNT_BEFORE_RESET; + } + } + AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), @@ -493,7 +503,7 @@ namespace MWMechanics } // if stuck for sufficiently long, act like current location was the destination - if (storage.mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset + if (storage.mStuckCount >= getCountBeforeReset(actor)) // something has gone wrong, reset { mObstacleCheck.clear(); stopWalking(actor, storage); From 1e8bf3846e7b61a1b5265894e1cb7e7250de0586 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 20 Mar 2019 01:59:09 +0300 Subject: [PATCH 085/106] Remove unused argument --- apps/openmw/mwmechanics/obstacle.cpp | 4 ++-- apps/openmw/mwmechanics/obstacle.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 3d8fe7919..c8b548bad 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -121,13 +121,13 @@ namespace MWMechanics * u = how long to move sideways * */ - void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration, float scaleMinimumDistance) + void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration) { const MWWorld::Class& cls = actor.getClass(); ESM::Position pos = actor.getRefData().getPosition(); if(mDistSameSpot == -1) - mDistSameSpot = DIST_SAME_SPOT * cls.getSpeed(actor) * scaleMinimumDistance; + mDistSameSpot = DIST_SAME_SPOT * cls.getSpeed(actor); float distSameSpot = mDistSameSpot * duration; diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index d7e582f8c..46c1bc83d 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -30,7 +30,7 @@ namespace MWMechanics bool isEvading() const; // Updates internal state, call each frame for moving actor - void update(const MWWorld::Ptr& actor, float duration, float scaleMinimumDistance = 1.0f); + void update(const MWWorld::Ptr& actor, float duration); // change direction to try to fix "stuck" actor void takeEvasiveAction(MWMechanics::Movement& actorMovement) const; From 5434e924375ae878124aaab1b3353a1781b4ed8a Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 20 Mar 2019 02:00:08 +0300 Subject: [PATCH 086/106] Take in account actor half extents for obstacle check --- apps/openmw/mwmechanics/obstacle.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index c8b548bad..63167e302 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -2,6 +2,8 @@ #include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" @@ -123,15 +125,17 @@ namespace MWMechanics */ void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration) { - const MWWorld::Class& cls = actor.getClass(); - ESM::Position pos = actor.getRefData().getPosition(); + const ESM::Position pos = actor.getRefData().getPosition(); - if(mDistSameSpot == -1) - mDistSameSpot = DIST_SAME_SPOT * cls.getSpeed(actor); - - float distSameSpot = mDistSameSpot * duration; + if (mDistSameSpot == -1) + { + const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); + mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor) + 1.2 * std::max(halfExtents.x(), halfExtents.y()); + } - bool samePosition = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2() < distSameSpot * distSameSpot; + const float distSameSpot = mDistSameSpot * duration; + const float squaredMovedDistance = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2(); + const bool samePosition = squaredMovedDistance < distSameSpot * distSameSpot; // update position mPrevX = pos.pos[0]; From c2176945bd8cdfe8b5c34563e05bac68584585c4 Mon Sep 17 00:00:00 2001 From: bzzt Date: Thu, 28 Feb 2019 09:10:22 +0400 Subject: [PATCH 087/106] Do not use the delayed map cells update --- apps/openmw/mwgui/mapwindow.cpp | 13 ++----------- apps/openmw/mwgui/mapwindow.hpp | 4 ---- apps/openmw/mwgui/windowmanagerimp.cpp | 3 --- apps/openmw/mwrender/globalmap.cpp | 2 +- 4 files changed, 3 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index f3bdd4c7d..a6e9c9818 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -837,22 +837,13 @@ namespace MWGui void MapWindow::cellExplored(int x, int y) { - mQueuedToExplore.push_back(std::make_pair(x,y)); + mGlobalMapRender->cleanupCameras(); + mGlobalMapRender->exploreCell(x, y, mLocalMapRender->getMapTexture(x, y)); } void MapWindow::onFrame(float dt) { LocalMapBase::onFrame(dt); - - mGlobalMapRender->cleanupCameras(); - - for (CellId& cellId : mQueuedToExplore) - { - mGlobalMapRender->exploreCell(cellId.first, cellId.second, mLocalMapRender->getMapTexture(cellId.first, cellId.second)); - } - - mQueuedToExplore.clear(); - NoDrop::onFrame(dt); } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 3d9ca23d8..033672c7f 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -261,10 +261,6 @@ namespace MWGui typedef std::pair CellId; std::set mMarkers; - // Cells that should be explored in the next frame (i.e. their map revealed on the global map) - // We can't do this immediately, because the map update is not immediate either (see mNeedMapUpdate in scene.cpp) - std::vector mQueuedToExplore; - MyGUI::Button* mEventBoxGlobal; MyGUI::Button* mEventBoxLocal; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index badfa2f3c..abd483a72 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1027,9 +1027,6 @@ namespace MWGui updateMap(); - if (!mMap->isVisible()) - mMap->onFrame(frameDuration); - mHud->onFrame(frameDuration); mDebugWindow->onFrame(frameDuration); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 588039639..57137d415 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -293,7 +293,7 @@ namespace MWRender camera->setViewMatrix(osg::Matrix::identity()); camera->setProjectionMatrix(osg::Matrix::identity()); camera->setProjectionResizePolicy(osg::Camera::FIXED); - camera->setRenderOrder(osg::Camera::PRE_RENDER); + camera->setRenderOrder(osg::Camera::PRE_RENDER, 1); // Make sure the global map is rendered after the local map y = mHeight - y - height; // convert top-left origin to bottom-left camera->setViewport(x, y, width, height); From 786f3e5fc31bac7db5a179fab2fb9b07f0c6c36b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 22 Mar 2019 21:59:27 +0300 Subject: [PATCH 088/106] Remove unused and unfinished blendmap packing feature --- components/esmterrain/storage.cpp | 26 +++++++++----------------- components/esmterrain/storage.hpp | 8 ++------ components/terrain/chunkmanager.cpp | 2 +- components/terrain/storage.hpp | 8 ++------ 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 3d6e521d1..1e23569b5 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -375,8 +375,7 @@ namespace ESMTerrain return texture; } - void Storage::getBlendmaps(float chunkSize, const osg::Vec2f &chunkCenter, - bool pack, ImageVector &blendmaps, std::vector &layerList) + void Storage::getBlendmaps(float chunkSize, const osg::Vec2f &chunkCenter, ImageVector &blendmaps, std::vector &layerList) { osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize/2.f, chunkSize/2.f); int cellX = static_cast(std::floor(origin.x())); @@ -416,11 +415,8 @@ namespace ESMTerrain layerList.push_back(getLayerInfo(getTextureName(*it))); } - int numTextures = textureIndices.size(); - // numTextures-1 since the base layer doesn't need blending - int numBlendmaps = pack ? static_cast(std::ceil((numTextures - 1) / 4.f)) : (numTextures - 1); - - int channels = pack ? 4 : 1; + // size-1 since the base layer doesn't need blending + int numBlendmaps = textureIndices.size() - 1; // Second iteration - create and fill in the blend maps const int blendmapSize = (realTextureSize-1) * chunkSize + 1; @@ -430,10 +426,8 @@ namespace ESMTerrain for (int i=0; i image (new osg::Image); - image->allocateImage(blendmapImageSize, blendmapImageSize, 1, format, GL_UNSIGNED_BYTE); + image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE); unsigned char* pData = image->data(); for (int y=0; ysecond; - int blendIndex = (pack ? static_cast(std::floor((layerIndex - 1) / 4.f)) : layerIndex - 1); - int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0; - int alpha = (blendIndex == i) ? 255 : 0; + int alpha = (layerIndex == i+1) ? 255 : 0; int realY = (blendmapSize - y - 1)*imageScaleFactor; int realX = x*imageScaleFactor; - pData[((realY+0)*blendmapImageSize + realX + 0)*channels + channel] = alpha; - pData[((realY+1)*blendmapImageSize + realX + 0)*channels + channel] = alpha; - pData[((realY+0)*blendmapImageSize + realX + 1)*channels + channel] = alpha; - pData[((realY+1)*blendmapImageSize + realX + 1)*channels + channel] = alpha; + pData[(realY+0)*blendmapImageSize + realX + 0] = alpha; + pData[(realY+1)*blendmapImageSize + realX + 0] = alpha; + pData[(realY+0)*blendmapImageSize + realX + 1] = alpha; + pData[(realY+1)*blendmapImageSize + realX + 1] = alpha; } } blendmaps.push_back(image); diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 27d6232eb..613d2e218 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -92,14 +92,10 @@ namespace ESMTerrain /// @note May be called from background threads. /// @param chunkSize size of the terrain chunk in cell units /// @param chunkCenter center of the chunk in cell units - /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - - /// otherwise, each texture contains blend values for one layer only. Shader-based rendering - /// can utilize packing, FFP can't. /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here - virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, bool pack, - ImageVector& blendmaps, - std::vector& layerList); + virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, + std::vector& layerList); virtual float getHeightAt (const osg::Vec3f& worldPos); diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 8cfccabce..a22d43d74 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -119,7 +119,7 @@ std::vector > ChunkManager::createPasses(float chunk { std::vector layerList; std::vector > blendmaps; - mStorage->getBlendmaps(chunkSize, chunkCenter, false, blendmaps, layerList); + mStorage->getBlendmaps(chunkSize, chunkCenter, blendmaps, layerList); bool useShaders = mSceneManager->getForceShaders(); if (!mSceneManager->getClampLighting()) diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index 8a59c104c..cdde06e47 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -69,14 +69,10 @@ namespace Terrain /// @note May be called from background threads. Make sure to only call thread-safe functions from here! /// @param chunkSize size of the terrain chunk in cell units /// @param chunkCenter center of the chunk in cell units - /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - - /// otherwise, each texture contains blend values for one layer only. Shader-based rendering - /// can utilize packing, FFP can't. /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here - virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, bool pack, - ImageVector& blendmaps, - std::vector& layerList) = 0; + virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, + std::vector& layerList) = 0; virtual float getHeightAt (const osg::Vec3f& worldPos) = 0; From 231e629e66f70dfcf59c9bdd8bb09fcab1ee82e4 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 089/106] Store map widgets and textures in one vector instead of 4 different ones --- apps/openmw/mwgui/mapwindow.cpp | 37 +++++++++++++++++---------------- apps/openmw/mwgui/mapwindow.hpp | 15 ++++++++----- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index a6e9c9818..5f4c9971c 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -213,8 +213,7 @@ namespace MWGui map->setNeedMouseFocus(false); fog->setNeedMouseFocus(false); - mMapWidgets.push_back(map); - mFogWidgets.push_back(fog); + mMaps.emplace_back(map, fog); } } } @@ -234,36 +233,37 @@ namespace MWGui void LocalMapBase::applyFogOfWar() { - TextureVector fogTextures; for (int mx=0; mxsetImageTexture(""); + entry.mFogTexture.reset(); continue; } osg::ref_ptr tex = mLocalMapRender->getFogOfWarTexture(x, y); if (tex) { - std::shared_ptr myguitex (new osgMyGUI::OSGTexture(tex)); - fog->setRenderItemTexture(myguitex.get()); + entry.mFogTexture.reset(new osgMyGUI::OSGTexture(tex)); + fog->setRenderItemTexture(entry.mFogTexture.get()); fog->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); - fogTextures.push_back(myguitex); } else + { fog->setImageTexture("black"); + entry.mFogTexture.reset(); + } } } - // Move the textures we just set into mFogTextures, and move the previous textures into fogTextures, for deletion when this function ends. - // Note, above we need to ensure that all widgets are getting a new texture set, lest we delete textures that are still in use. - mFogTextures.swap(fogTextures); redraw(); } @@ -369,7 +369,6 @@ namespace MWGui applyFogOfWar(); // Update the map textures - TextureVector textures; for (int mx=0; mx texture = mLocalMapRender->getMapTexture(mapX, mapY); if (texture) { - std::shared_ptr guiTex (new osgMyGUI::OSGTexture(texture)); - textures.push_back(guiTex); - box->setRenderItemTexture(guiTex.get()); + entry.mMapTexture.reset(new osgMyGUI::OSGTexture(texture)); + box->setRenderItemTexture(entry.mMapTexture.get()); box->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); } else + { box->setRenderItemTexture(nullptr); + entry.mMapTexture.reset(); + } } } - mMapTextures.swap(textures); // Delay the door markers update until scripts have been given a chance to run. // If we don't do this, door markers that should be disabled will still appear on the map. @@ -1046,8 +1047,8 @@ namespace MWGui NoDrop::setAlpha(alpha); // can't allow showing map with partial transparency, as the fog of war will also go transparent // and reveal parts of the map you shouldn't be able to see - for (MyGUI::ImageBox* widget : mMapWidgets) - widget->setVisible(alpha == 1); + for (MapEntry& entry : mMaps) + entry.mMapWidget->setVisible(alpha == 1); } void MapWindow::customMarkerCreated(MyGUI::Widget *marker) diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 033672c7f..4104a7824 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -126,12 +126,17 @@ namespace MWGui // Stores markers that were placed by a player. May be shared between multiple map views. CustomMarkerCollection& mCustomMarkers; - std::vector mMapWidgets; - std::vector mFogWidgets; + struct MapEntry + { + MapEntry(MyGUI::ImageBox* mapWidget, MyGUI::ImageBox* fogWidget) + : mMapWidget(mapWidget), mFogWidget(fogWidget) {} - typedef std::vector > TextureVector; - TextureVector mMapTextures; - TextureVector mFogTextures; + MyGUI::ImageBox* mMapWidget; + MyGUI::ImageBox* mFogWidget; + std::shared_ptr mMapTexture; + std::shared_ptr mFogTexture; + }; + std::vector mMaps; // Keep track of created marker widgets, just to easily remove them later. std::vector mDoorMarkerWidgets; From bbb1fdd92f378e0d995e8a4c9403d6bd4e8dea6e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 23 Mar 2019 14:25:44 +0400 Subject: [PATCH 090/106] Reduce default composite map distance to decrease GPU loading --- docs/source/reference/modding/settings/terrain.rst | 2 +- files/settings-default.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/reference/modding/settings/terrain.rst b/docs/source/reference/modding/settings/terrain.rst index e15a0035d..c8ae2a227 100644 --- a/docs/source/reference/modding/settings/terrain.rst +++ b/docs/source/reference/modding/settings/terrain.rst @@ -68,7 +68,7 @@ composite map level :Type: integer :Range: >= -3 -:Default: 0 +:Default: -2 Controls at which minimum size (in 2^value cell units) terrain chunks will start to use a composite map instead of the high-detail textures. With value -3 composite maps are used everywhere. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index e95646602..353a3ac3d 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -98,7 +98,7 @@ vertex lod mod = 0 # Controls when the distant terrain will flip to composited textures instead of high-detail textures, should be >= -3. # Higher value is more detailed textures. -composite map level = 0 +composite map level = -2 # Controls the resolution of composite maps. composite map resolution = 512 From 1143985bc7559e5f19d996eeac7a04c4a3edd30d Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 091/106] Do not update map texture if it did not change --- apps/openmw/mwrender/localmap.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 1650dc92c..6c8dfec60 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -613,7 +613,8 @@ void LocalMap::updatePlayer (const osg::Vec3f& position, const osg::Quat& orient if (!segment.mFogOfWarImage || !segment.mMapTexture) continue; - unsigned char* data = segment.mFogOfWarImage->data(); + uint32_t* data = (uint32_t*)segment.mFogOfWarImage->data(); + bool changed = false; for (int texV = 0; texV> 24); alpha = std::min( alpha, (uint8_t) (std::max(0.f, std::min(1.f, (sqrDist/sqrExploreRadius)))*255) ); - *(uint32_t*)data = (uint32_t) (alpha << 24); + uint32_t val = (uint32_t) (alpha << 24); + if ( *data != val) + { + *data = val; + changed = true; + } - data += 4; + ++data; } } - segment.mHasFogState = true; - segment.mFogOfWarImage->dirty(); + if (changed) + { + segment.mHasFogState = true; + segment.mFogOfWarImage->dirty(); + } } } } From 6dbd875f75ce3597d1e8a7484b516c57f9f07d2f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 23 Mar 2019 22:56:30 +0300 Subject: [PATCH 092/106] Disallow binding some reserved keys (bug #3282) --- CHANGELOG.md | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86eb3712e..8a00f4d72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Bug #2969: Scripted items can stack Bug #2987: Editor: some chance and AI data fields can overflow + Bug #3282: Unintended behaviour when assigning F3 and Windows keys Bug #3623: Fix HiDPI on Windows Bug #3733: Normal maps are inverted on mirrored UVs Bug #3765: DisableTeleporting makes Mark/Recall/Intervention effects undetectable diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b7014c99d..a5cb66220 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1834,6 +1834,17 @@ namespace MWInput MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); return; } + + // Disallow binding reserved keys + if (key == SDL_SCANCODE_F3 || key == SDL_SCANCODE_F4 || key == SDL_SCANCODE_F10 || key == SDL_SCANCODE_F11) + return; + + #ifndef __APPLE__ + // Disallow binding Windows/Meta keys + if (key == SDL_SCANCODE_LGUI || key == SDL_SCANCODE_RGUI) + return; + #endif + if(!mDetectingKeyboard) return; From cbce1a1b7c10e315f51163c5b75ae2f5e5f598e3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 19 Sep 2018 22:44:09 +0300 Subject: [PATCH 093/106] Ignore the rest of the line after else (bug #3006) --- CHANGELOG.md | 1 + components/compiler/controlparser.cpp | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a00f4d72..6f6586511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Bug #2969: Scripted items can stack Bug #2987: Editor: some chance and AI data fields can overflow + Bug #3006: 'else if' operator breaks script compilation Bug #3282: Unintended behaviour when assigning F3 and Windows keys Bug #3623: Fix HiDPI on Windows Bug #3733: Normal maps are inverted on mirrored UVs diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index b202467db..6f9fad35f 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -179,6 +179,14 @@ namespace Compiler scanner.scan (mLineParser); return true; } + else if (mState==IfElseJunkState) + { + getErrorHandler().warning ("Ignoring extra text after else", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + mState = IfElseBodyState; + return true; + } return Parser::parseName (name, loc, scanner); } @@ -207,8 +215,7 @@ namespace Compiler return true; } } - else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState || - mState==IfElseJunkState) + else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState) { if (parseIfBody (keyword, loc, scanner)) return true; @@ -218,6 +225,14 @@ namespace Compiler if ( parseWhileBody (keyword, loc, scanner)) return true; } + else if (mState==IfElseJunkState) + { + getErrorHandler().warning ("Ignoring extra text after else", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + mState = IfElseBodyState; + return true; + } return Parser::parseKeyword (keyword, loc, scanner); } @@ -250,8 +265,9 @@ namespace Compiler default: ; } } - else if (code==Scanner::S_open && mState==IfElseJunkState) + else if (mState==IfElseJunkState) { + getErrorHandler().warning ("Ignoring extra text after else", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); mState = IfElseBodyState; From 93f78aad60346d5a4369074e99b743be67ebcbbe Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 24 Mar 2019 22:53:22 +0300 Subject: [PATCH 094/106] Avoid menu button texture vertical cutoff --- apps/openmw/mwgui/mainmenu.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 8c06859e4..2c2bd84df 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -287,11 +287,10 @@ namespace MWGui MyGUI::IntSize requested = button->getRequestedSize(); + button->setImageCoord(MyGUI::IntCoord(0, 0, requested.width, requested.height)); // Trim off some of the excessive padding // TODO: perhaps do this within ImageButton? - int trim = 8; - button->setImageCoord(MyGUI::IntCoord(0, trim, requested.width, requested.height-trim)); - int height = requested.height-trim*2; + int height = requested.height-16; button->setImageTile(MyGUI::IntSize(requested.width, height)); button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, height); curH += height; From 6c0837259c3779fac14ae349b6523655c3d118b7 Mon Sep 17 00:00:00 2001 From: Alex Kokkinos Date: Mon, 25 Mar 2019 21:11:00 -0500 Subject: [PATCH 095/106] Update paths.rst - Windows paths, shell friendly paths - Adds path expansion based paths for Windows for easier copying and pasting - Escapes Application Support in Mac to make the paths terminal and Finder Go menu friendly - Places paths in tables --- docs/source/reference/modding/paths.rst | 48 ++++++++++++++++++++----- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/docs/source/reference/modding/paths.rst b/docs/source/reference/modding/paths.rst index 23e33190d..085168189 100644 --- a/docs/source/reference/modding/paths.rst +++ b/docs/source/reference/modding/paths.rst @@ -9,20 +9,50 @@ The following describes the locations for the various OpenMW file paths for diff Configuration files and log files --------------------------------- -:Linux: ``$HOME/.config/openmw`` -:Mac: ``$HOME/Library/Preferences/openmw`` -:Windows: ``C:\Users\Username\Documents\my games\openmw`` ++--------------+------------------------------------------------------------------+ +| OS | Location | ++==============+==================================================================+ +| Linux | ``$HOME/.config/openmw`` | ++--------------+------------------------------------------------------------------+ +| Mac | ``$HOME/Library/Preferences/openmw`` | ++--------------+---------------+--------------------------------------------------+ +| Windows | File Explorer | ``%USERPROFILE%\Documents\My Games\OpenMW`` | +| | | | +| | PowerShell | ``"$env:USERPROFILE\Documents\My Games\OpenMW"`` | +| | | | +| | Example | ``C:\Users\Username\Documents\My Games\OpenMW`` | ++--------------+---------------+--------------------------------------------------+ Savegames --------- -:Linux: ``$HOME/.local/share/openmw/saves`` -:Mac: ``$HOME/Library/Application Support/openmw/saves`` -:Windows: ``C:\Users\Username\Documents\my games\openmw\saves`` ++--------------+------------------------------------------------------------------------+ +| OS | Location | ++==============+========================================================================+ +| Linux | ``$HOME/.config/openmw/saves`` | ++--------------+------------------------------------------------------------------------+ +| Mac | ``$HOME/Library/Application\ Support/openmw/saves`` | ++--------------+---------------+--------------------------------------------------------+ +| Windows | File Explorer | ``%USERPROFILE%\Documents\My Games\OpenMW\saves`` | +| | | | +| | PowerShell | ``"$env:USERPROFILE\Documents\My Games\OpenMW\saves"`` | +| | | | +| | Example | ``C:\Users\Username\Documents\My Games\OpenMW\saves`` | ++--------------+---------------+--------------------------------------------------------+ Screenshots ----------- -:Linux: ``$HOME/.local/share/openmw`` -:Mac: ``$HOME/Library/Application Support/openmw`` -:Windows: ``C:\Users\Username\Documents\my games\openmw`` ++--------------+-------------------------------------------------------------------------+ +| OS | Location | ++==============+=========================================================================+ +| Linux | ``$HOME/.local/share/openmw`` | ++--------------+-------------------------------------------------------------------------+ +| Mac | ``$HOME/Library/Application\ Support/openmw`` | ++--------------+---------------+---------------------------------------------------------+ +| Windows | File Explorer | ``%USERPROFILE%\Documents\My Games\OpenMW\OpenMW`` | +| | | | +| | PowerShell | ``"$env:USERPROFILE\Documents\My Games\OpenMW\OpenMW"`` | +| | | | +| | Example | ``C:\Users\Username\Documents\My Games\OpenMW\OpenMW`` | ++--------------+---------------+---------------------------------------------------------+ From c8d2107b802d516b5794dba53c0adca326203de5 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 096/106] Do not store Viewer reference in the ViewData --- components/terrain/viewdata.cpp | 2 -- components/terrain/viewdata.hpp | 6 +----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 2f9cb6641..3d09e7ba0 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -118,7 +118,6 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer) if (found == mViews.end()) { ViewData* vd = createOrReuseView(); - vd->setViewer(viewer); mViews[viewer] = vd; return vd; } @@ -148,7 +147,6 @@ void ViewDataMap::clearUnusedViews(unsigned int frame) ViewData* vd = it->second; if (vd->getFrameLastUsed() + 2 < frame) { - vd->setViewer(nullptr); vd->clear(); mUnusedViews.push_back(vd); mViews.erase(it++); diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index aaf5b788f..53bcf42c0 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -44,9 +44,6 @@ namespace Terrain Entry& getEntry(unsigned int i); - osg::Object* getViewer() const { return mViewer.get(); } - void setViewer(osg::Object* viewer) { mViewer = viewer; } - unsigned int getFrameLastUsed() const { return mFrameLastUsed; } /// @return Have any nodes changed since the last frame @@ -62,7 +59,6 @@ namespace Terrain unsigned int mNumEntries; unsigned int mFrameLastUsed; bool mChanged; - osg::ref_ptr mViewer; osg::Vec3f mEyePoint; bool mHasEyePoint; }; @@ -85,7 +81,7 @@ namespace Terrain private: std::list mViewVector; - typedef std::map Map; + typedef std::map, ViewData*> Map; Map mViews; std::deque mUnusedViews; From 6363cc883960c876acb6871797211f8484e44342 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Thu, 28 Mar 2019 20:48:54 +0100 Subject: [PATCH 097/106] Relax CMake version requirements on macOS --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fbebe99e7..bf489d99c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -796,8 +796,8 @@ endif() # Apple bundling if (OPENMW_OSX_DEPLOYMENT AND APPLE AND DESIRED_QT_VERSION MATCHES 5) - if (${CMAKE_MAJOR_VERSION} STREQUAL "3" AND ${CMAKE_MINOR_VERSION} STREQUAL "13") - message(FATAL_ERROR "macOS packaging is broken in CMake 3.13.*, see https://gitlab.com/OpenMW/openmw/issues/4767. Please use an older version like 3.12.4") + if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND CMAKE_VERSION VERSION_LESS 3.13.4) + message(FATAL_ERROR "macOS packaging is broken in early CMake 3.13 releases, see https://gitlab.com/OpenMW/openmw/issues/4767. Please use at least 3.13.4 or an older version like 3.12.4") endif () get_property(QT_COCOA_PLUGIN_PATH TARGET Qt5::QCocoaIntegrationPlugin PROPERTY LOCATION_RELEASE) From 17dce28705008cf21acf3ac2ded52d5d6525e1a2 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 29 Mar 2019 00:59:26 +0300 Subject: [PATCH 098/106] Improve compiler messages --- components/compiler/controlparser.cpp | 8 +++---- components/compiler/declarationparser.cpp | 8 +++---- components/compiler/exception.hpp | 6 ++--- components/compiler/exprparser.cpp | 18 +++++++-------- components/compiler/fileparser.cpp | 2 +- components/compiler/junkparser.cpp | 4 ++-- components/compiler/lineparser.cpp | 27 +++++++++++------------ components/compiler/locals.cpp | 4 ++-- components/compiler/scanner.cpp | 6 ++--- 9 files changed, 41 insertions(+), 42 deletions(-) diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index 6f9fad35f..ebadcbbc6 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -125,7 +125,7 @@ namespace Compiler if (loop.size()!=loop2.size()) throw std::logic_error ( - "internal compiler error: failed to generate a while loop"); + "Internal compiler error: failed to generate a while loop"); std::copy (loop2.begin(), loop2.end(), std::back_inserter (mCode)); @@ -181,7 +181,7 @@ namespace Compiler } else if (mState==IfElseJunkState) { - getErrorHandler().warning ("Ignoring extra text after else", loc); + getErrorHandler().warning ("Extra text after else", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); mState = IfElseBodyState; @@ -227,7 +227,7 @@ namespace Compiler } else if (mState==IfElseJunkState) { - getErrorHandler().warning ("Ignoring extra text after else", loc); + getErrorHandler().warning ("Extra text after else", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); mState = IfElseBodyState; @@ -267,7 +267,7 @@ namespace Compiler } else if (mState==IfElseJunkState) { - getErrorHandler().warning ("Ignoring extra text after else", loc); + getErrorHandler().warning ("Extra text after else", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); mState = IfElseBodyState; diff --git a/components/compiler/declarationparser.cpp b/components/compiler/declarationparser.cpp index 1e3bc0585..1c64aaade 100644 --- a/components/compiler/declarationparser.cpp +++ b/components/compiler/declarationparser.cpp @@ -22,7 +22,7 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke char type = mLocals.getType (name2); if (type!=' ') - getErrorHandler().warning ("ignoring local variable re-declaration", loc); + getErrorHandler().warning ("Local variable re-declaration", loc); else mLocals.declare (mType, name2); @@ -31,7 +31,7 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke } else if (mState==State_End) { - getErrorHandler().warning ("Ignoring extra text after local variable declaration", loc); + getErrorHandler().warning ("Extra text after local variable declaration", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return false; @@ -65,7 +65,7 @@ bool Compiler::DeclarationParser::parseKeyword (int keyword, const TokenLoc& loc } else if (mState==State_End) { - getErrorHandler().warning ("Ignoring extra text after local variable declaration", loc); + getErrorHandler().warning ("Extra text after local variable declaration", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return false; @@ -80,7 +80,7 @@ bool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, S { if (code!=Scanner::S_newline) { - getErrorHandler().warning ("Ignoring extra text after local variable declaration", loc); + getErrorHandler().warning ("Extra text after local variable declaration", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); } diff --git a/components/compiler/exception.hpp b/components/compiler/exception.hpp index a28663115..33bad7590 100644 --- a/components/compiler/exception.hpp +++ b/components/compiler/exception.hpp @@ -11,7 +11,7 @@ namespace Compiler { public: - virtual const char *what() const throw() { return "compile error";} + virtual const char *what() const throw() { return "Compile error";} ///< Return error message }; @@ -21,7 +21,7 @@ namespace Compiler { public: - virtual const char *what() const throw() { return "can't read file"; } + virtual const char *what() const throw() { return "Can't read file"; } ///< Return error message }; @@ -31,7 +31,7 @@ namespace Compiler { public: - virtual const char *what() const throw() { return "end of file"; } + virtual const char *what() const throw() { return "End of file"; } ///< Return error message }; } diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index b3fd1e5ae..e3a306b8f 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -99,7 +99,7 @@ namespace Compiler else if (t1=='f' || t2=='f') mOperands.push_back ('f'); else - throw std::logic_error ("failed to determine result operand type"); + throw std::logic_error ("Failed to determine result operand type"); } void ExprParser::pop() @@ -158,7 +158,7 @@ namespace Compiler default: - throw std::logic_error ("unknown operator"); + throw std::logic_error ("Unknown operator"); } } @@ -287,7 +287,7 @@ namespace Compiler else { mExplicit.clear(); - getErrorHandler().warning ("Ignoring stray explicit reference", loc); + getErrorHandler().warning ("Stray explicit reference", loc); } } @@ -430,7 +430,7 @@ namespace Compiler { if (!hasExplicit) { - getErrorHandler().warning ("ignoring stray explicit reference", loc); + getErrorHandler().warning ("Stray explicit reference", loc); mExplicit.clear(); } @@ -735,13 +735,13 @@ namespace Compiler { if (mOperands.empty() && mOperators.empty()) { - getErrorHandler().error ("missing expression", mTokenLoc); + getErrorHandler().error ("Missing expression", mTokenLoc); return 'l'; } if (mNextOperand || mOperands.empty()) { - getErrorHandler().error ("syntax error in expression", mTokenLoc); + getErrorHandler().error ("Syntax error in expression", mTokenLoc); return 'l'; } @@ -799,7 +799,7 @@ namespace Compiler ++optionalCount; } else - getErrorHandler().warning ("ignoring extra argument", + getErrorHandler().warning ("Extra argument", stringParser.getTokenLoc()); } else if (*iter=='X') @@ -813,7 +813,7 @@ namespace Compiler if (parser.isEmpty()) break; else - getErrorHandler().warning("ignoring extra argument", parser.getTokenLoc()); + getErrorHandler().warning("Extra argument", parser.getTokenLoc()); } else if (*iter=='z') { @@ -825,7 +825,7 @@ namespace Compiler if (discardParser.isEmpty()) break; else - getErrorHandler().warning("ignoring extra argument", discardParser.getTokenLoc()); + getErrorHandler().warning("Extra argument", discardParser.getTokenLoc()); } else if (*iter=='j') { diff --git a/components/compiler/fileparser.cpp b/components/compiler/fileparser.cpp index 8b2f1fe08..c7459c2ae 100644 --- a/components/compiler/fileparser.cpp +++ b/components/compiler/fileparser.cpp @@ -98,7 +98,7 @@ namespace Compiler if (mState == BeginState) { if (code != Scanner::S_newline) - reportWarning ("Ignoring stray special character before begin statement", loc); + reportWarning ("Stray special character before begin statement", loc); return true; } diff --git a/components/compiler/junkparser.cpp b/components/compiler/junkparser.cpp index 5910cca43..5df551535 100644 --- a/components/compiler/junkparser.cpp +++ b/components/compiler/junkparser.cpp @@ -29,7 +29,7 @@ bool Compiler::JunkParser::parseName (const std::string& name, const TokenLoc& l bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (keyword==mIgnoreKeyword) - reportWarning ("ignoring found junk", loc); + reportWarning ("Ignoring found junk", loc); else scanner.putbackKeyword (keyword, loc); @@ -39,7 +39,7 @@ bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scann bool Compiler::JunkParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_member) - reportWarning ("ignoring found junk", loc); + reportWarning ("Ignoring found junk", loc); else scanner.putbackSpecial (code, loc); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 1cfa70f73..1c3d1a067 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -48,7 +48,7 @@ namespace Compiler default: - throw std::runtime_error ("unknown expression result type"); + throw std::runtime_error ("Unknown expression result type"); } } @@ -88,7 +88,7 @@ namespace Compiler { if (mState==PotentialEndState) { - getErrorHandler().warning ("ignoring stray string argument", loc); + getErrorHandler().warning ("Stray string argument", loc); mState = EndState; return true; } @@ -132,7 +132,7 @@ namespace Compiler return true; } - getErrorHandler().error ("unknown variable", loc); + getErrorHandler().error ("Unknown variable", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return false; @@ -233,7 +233,7 @@ namespace Compiler if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to) { - getErrorHandler().warning ("unknown variable, ignoring set instruction", loc); + getErrorHandler().warning ("Unknown variable", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return false; @@ -286,7 +286,7 @@ namespace Compiler { if (!hasExplicit && mState==ExplicitState) { - getErrorHandler().warning ("ignoring stray explicit reference", loc); + getErrorHandler().warning ("Stray explicit reference", loc); mExplicit.clear(); } @@ -344,7 +344,7 @@ namespace Compiler { if (!hasExplicit && !mExplicit.empty()) { - getErrorHandler().warning ("ignoring stray explicit reference", loc); + getErrorHandler().warning ("Stray explicit reference", loc); mExplicit.clear(); } @@ -360,7 +360,7 @@ namespace Compiler if (mState==ExplicitState) { // drop stray explicit reference - getErrorHandler().warning ("ignoring stray explicit reference", loc); + getErrorHandler().warning ("Stray explicit reference", loc); mState = BeginState; mExplicit.clear(); } @@ -375,8 +375,7 @@ namespace Compiler { if (!getContext().canDeclareLocals()) { - getErrorHandler().error ( - "local variables can't be declared in this context", loc); + getErrorHandler().error("Local variables cannot be declared in this context", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return true; @@ -412,19 +411,19 @@ namespace Compiler case Scanner::K_else: - getErrorHandler().warning ("ignoring stray else", loc); + getErrorHandler().warning ("Stray else", loc); mState = EndState; return true; case Scanner::K_endif: - getErrorHandler().warning ("ignoring stray endif", loc); + getErrorHandler().warning ("Stray endif", loc); mState = EndState; return true; case Scanner::K_begin: - getErrorHandler().warning ("ignoring stray begin", loc); + getErrorHandler().warning ("Stray begin", loc); mState = EndState; return true; } @@ -491,7 +490,7 @@ namespace Compiler { if (mState==EndState && code==Scanner::S_open) { - getErrorHandler().warning ("ignoring stray '[' or '(' at the end of the line", + getErrorHandler().warning ("Stray '[' or '(' at the end of the line", loc); return true; } @@ -508,7 +507,7 @@ namespace Compiler if (code==Scanner::S_ref && mState==SetPotentialMemberVarState) { - getErrorHandler().warning ("Ignoring stray explicit reference", loc); + getErrorHandler().warning ("Stray explicit reference", loc); mState = SetState; return true; } diff --git a/components/compiler/locals.cpp b/components/compiler/locals.cpp index 2b485abec..a7102c38d 100644 --- a/components/compiler/locals.cpp +++ b/components/compiler/locals.cpp @@ -18,7 +18,7 @@ namespace Compiler case 'f': return mFloats; } - throw std::logic_error ("unknown variable type"); + throw std::logic_error ("Unknown variable type"); } int Locals::searchIndex (char type, const std::string& name) const @@ -48,7 +48,7 @@ namespace Compiler case 'f': return mFloats; } - throw std::logic_error ("unknown variable type"); + throw std::logic_error ("Unknown variable type"); } char Locals::getType (const std::string& name) const diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index c5ef483f3..65c050ce0 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -158,7 +158,7 @@ namespace Compiler TokenLoc loc (mLoc); mLoc.mLiteral.clear(); - mErrorHandler.error ("syntax error", loc); + mErrorHandler.error ("Syntax error", loc); throw SourceException(); } @@ -521,7 +521,7 @@ namespace Compiler else if (c == '<' || c == '>') // Treat <> and << as < { special = S_cmpLT; - mErrorHandler.warning (std::string("invalid operator <") + c + ", treating it as <", mLoc); + mErrorHandler.warning ("Invalid operator, treating it as <", mLoc); } else { @@ -549,7 +549,7 @@ namespace Compiler else if (c == '<' || c == '>') // Treat >< and >> as > { special = S_cmpGT; - mErrorHandler.warning (std::string("invalid operator >") + c + ", treating it as >", mLoc); + mErrorHandler.warning ("Invalid operator, treating it as >", mLoc); } else { From 7ef7555c198bed942d15cfc0f15d78b74c321dc0 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 30 Mar 2019 01:06:01 +0300 Subject: [PATCH 099/106] Ignore extra arguments to PlayLoopSound3D too --- components/compiler/extensions0.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index fdb670748..92b647288 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -356,7 +356,7 @@ namespace Compiler opcodePlaySound3DExplicit); extensions.registerInstruction ("playsound3dvp", "cff", opcodePlaySound3DVP, opcodePlaySound3DVPExplicit); - extensions.registerInstruction ("playloopsound3d", "c", opcodePlayLoopSound3D, + extensions.registerInstruction ("playloopsound3d", "cXX", opcodePlayLoopSound3D, opcodePlayLoopSound3DExplicit); extensions.registerInstruction ("playloopsound3dvp", "cff", opcodePlayLoopSound3DVP, opcodePlayLoopSound3DVPExplicit); From 4b6fc5f72099b0319241c88a4119105f7d8e015f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 30 Mar 2019 20:09:30 +0300 Subject: [PATCH 100/106] Make sure the record hasn't ended in getHString hack (bug #4938) --- CHANGELOG.md | 1 + components/esm/esmreader.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index deb5c88d1..3e13cc363 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5 Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used. Bug #4922: Werewolves can not attack if the transformation happens during attack + Bug #4938: Strings from subrecords with actually empty headers can't be empty Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Feature #3610: Option to invert X axis diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 2f4f4917c..278a1541a 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -127,7 +127,7 @@ std::string ESMReader::getHString() // them. For some reason, they break the rules, and contain a byte // (value 0) even if the header says there is no data. If // Morrowind accepts it, so should we. - if (mCtx.leftSub == 0) + if (mCtx.leftSub == 0 && mCtx.leftRec != 0) { // Skip the following zero byte mCtx.leftRec--; From 3a0e374dc6c0dab3eb24c1876817ab082299be35 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 31 Mar 2019 14:02:24 +0300 Subject: [PATCH 101/106] Replicate vanilla Position/SetPos behavior more closely (bug #3109) --- CHANGELOG.md | 1 + apps/openmw/mwbase/world.hpp | 2 +- .../mwscript/transformationextensions.cpp | 8 ++++---- apps/openmw/mwworld/worldimp.cpp | 19 ++++++++++--------- apps/openmw/mwworld/worldimp.hpp | 4 ++-- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index deb5c88d1..ac6d6483c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Bug #2969: Scripted items can stack Bug #2987: Editor: some chance and AI data fields can overflow Bug #3006: 'else if' operator breaks script compilation + Bug #3109: SetPos/Position handles actors differently Bug #3282: Unintended behaviour when assigning F3 and Windows keys Bug #3623: Fix HiDPI on Windows Bug #3733: Normal maps are inverted on mirrored UVs diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d3bdb419e..f5ca91dc6 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -278,7 +278,7 @@ namespace MWBase virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0; - virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; + virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z, bool moveToActive=false) = 0; ///< @return an updated Ptr in case the Ptr's cell changes virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 0d0d6f29e..93645d5fb 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -226,11 +226,11 @@ namespace MWScript MWWorld::Ptr updated = ptr; if(axis == "x") { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr,pos,ay,az); + updated = MWBase::Environment::get().getWorld()->moveObject(ptr,pos,ay,az,true); } else if(axis == "y") { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,pos,az); + updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,pos,az,true); } else if(axis == "z") { @@ -245,7 +245,7 @@ namespace MWScript pos = terrainHeight; } - updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos); + updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos,true); } else throw std::runtime_error ("invalid axis: " + axis); @@ -388,7 +388,7 @@ namespace MWScript } else { - ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z); + ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z, true); } dynamic_cast(runtime.getContext()).updatePtr(base,ptr); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1d33d039e..7455bd28e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1307,23 +1307,24 @@ namespace MWWorld return newPtr; } - MWWorld::Ptr World::moveObjectImp(const Ptr& ptr, float x, float y, float z, bool movePhysics) + MWWorld::Ptr World::moveObjectImp(const Ptr& ptr, float x, float y, float z, bool movePhysics, bool moveToActive) { - CellStore *cell = ptr.getCell(); + int cellX, cellY; + positionToIndex(x, y, cellX, cellY); - if (cell->isExterior()) { - int cellX, cellY; - positionToIndex(x, y, cellX, cellY); + CellStore* cell = ptr.getCell(); + CellStore* newCell = getExterior(cellX, cellY); + bool isCellActive = getPlayerPtr().getCell()->isExterior() && mWorldScene->isCellActive(*newCell); - cell = getExterior(cellX, cellY); - } + if (cell->isExterior() || (moveToActive && isCellActive && ptr.getClass().isActor())) + cell = newCell; return moveObject(ptr, cell, x, y, z, movePhysics); } - MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z) + MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z, bool moveToActive) { - return moveObjectImp(ptr, x, y, z); + return moveObjectImp(ptr, x, y, z, true, moveToActive); } void World::scaleObject (const Ptr& ptr, float scale) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d3e445cff..8eae2a0ef 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -130,7 +130,7 @@ namespace MWWorld void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, bool adjust); - Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z, bool movePhysics=true); + Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false); ///< @return an updated Ptr in case the Ptr's cell changes Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos); @@ -382,7 +382,7 @@ namespace MWWorld void undeleteObject (const Ptr& ptr) override; - MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z) override; + MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z, bool moveToActive=false) override; ///< @return an updated Ptr in case the Ptr's cell changes MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override; From a11c391231ef4438ff89bd34d21c54c8debacd0c Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 30 Mar 2019 09:57:38 +0100 Subject: [PATCH 102/106] Enable ccache for macOS CI --- CI/before_install.osx.sh | 1 + CI/before_script.osx.sh | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 895b07e1e..3e47d11a1 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -6,6 +6,7 @@ brew install https://gist.githubusercontent.com/nikolaykasyanov/f36da224bdef4202 brew switch cmake 3.12.4 brew outdated pkgconfig || brew upgrade pkgconfig brew install qt +brew install ccache curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-110f3d3.zip -o ~/openmw-deps.zip unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index d3994a7ae..5967756ab 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -10,6 +10,8 @@ cd build cmake \ -D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \ +-D CMAKE_C_COMPILER_LAUNCHER=/usr/local/opt/ccache/bin/ccache \ +-D CMAKE_CXX_COMPILER_LAUNCHER=/usr/local/opt/ccache/bin/ccache \ -D CMAKE_OSX_DEPLOYMENT_TARGET="10.9" \ -D CMAKE_OSX_SYSROOT="macosx10.14" \ -D CMAKE_BUILD_TYPE=Release \ From 46b4aa4c4fe302dc0920ee6e4b85aa384324839d Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 30 Mar 2019 12:27:36 +0100 Subject: [PATCH 103/106] Bump Xcode version while I'm at it --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d87b0f01c..c93795241 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,9 +41,9 @@ addons: build_command: "make VERBOSE=1 -j3" matrix: include: - - name: OpenMW (all) on macOS Xcode 10.1 + - name: OpenMW (all) on macOS Xcode 10.2 os: osx - osx_image: xcode10.1 + osx_image: xcode10.2 if: branch != coverity_scan - name: OpenMW (all) on Ubuntu Xenial GCC-5 os: linux From 95ae9c4ac863c65b1b7a31cfda48030742667f49 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 30 Mar 2019 12:36:29 +0100 Subject: [PATCH 104/106] Don't downgrade CMake --- CI/before_install.osx.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 3e47d11a1..0a4c7313c 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -1,9 +1,6 @@ #!/bin/sh -e brew update -brew unlink cmake || true -brew install https://gist.githubusercontent.com/nikolaykasyanov/f36da224bdef42025e480f99fa21a82d/raw/7dd8b5ed2750198757f81c6bc6456e03541999bd/cmake.rb -brew switch cmake 3.12.4 brew outdated pkgconfig || brew upgrade pkgconfig brew install qt brew install ccache From d305e1933abf0427b12ac84fe160249b61ecb88c Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 31 Mar 2019 17:41:16 +0200 Subject: [PATCH 105/106] Get ccache path from Homebrew Also, don't use legacy syntax to get Qt path. --- CI/before_script.osx.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index 5967756ab..1c3a71bdd 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -4,14 +4,15 @@ export CXX=clang++ export CC=clang DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps" -QT_PATH=`brew --prefix qt` +QT_PATH=$(brew --prefix qt) +CCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache mkdir build cd build cmake \ -D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \ --D CMAKE_C_COMPILER_LAUNCHER=/usr/local/opt/ccache/bin/ccache \ --D CMAKE_CXX_COMPILER_LAUNCHER=/usr/local/opt/ccache/bin/ccache \ +-D CMAKE_C_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE" \ +-D CMAKE_CXX_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE" \ -D CMAKE_OSX_DEPLOYMENT_TARGET="10.9" \ -D CMAKE_OSX_SYSROOT="macosx10.14" \ -D CMAKE_BUILD_TYPE=Release \ From 80a65e81dd63a66fcefa91647ae2e7cf0de78fbf Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 31 Mar 2019 22:07:24 +0300 Subject: [PATCH 106/106] Only set attack type randomly if "best attack" is on (bug #4942) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d674a9672..d33c02fef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used. Bug #4922: Werewolves can not attack if the transformation happens during attack Bug #4938: Strings from subrecords with actually empty headers can't be empty + Bug #4942: Hand-to-Hand attack type is chosen randomly when "always use best attack" is turned off Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Feature #3610: Option to invert X axis diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ffbf5dd09..2fa0404d5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1631,20 +1631,22 @@ bool CharacterController::updateWeaponState(CharacterState& idle) { if(mPtr == getPlayer()) { - if (isWeapon) + if (Settings::Manager::getBool("best attack", "Game")) { - if (Settings::Manager::getBool("best attack", "Game")) + if (isWeapon) { MWWorld::ConstContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight); mAttackType = getBestAttack(weapon->get()->mBase); } else - setAttackTypeBasedOnMovement(); + { + // There is no "best attack" for Hand-to-Hand + setAttackTypeRandomly(mAttackType); + } } else { - // There is no "best attack" for Hand-to-Hand - setAttackTypeRandomly(mAttackType); + setAttackTypeBasedOnMovement(); } } // else if (mPtr != getPlayer()) use mAttackType set by AiCombat