From ae729a1ac74ff2e1891bfd770f13e746f2de1eca Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 29 Apr 2020 11:46:52 +0200 Subject: [PATCH 01/81] add ability to set the type of near far method to be used in shadow calculation; default to bounding volumes; cleaned up code while there and re-ordered items --- apps/launcher/graphicspage.cpp | 11 +- apps/launcher/graphicspage.hpp | 4 +- components/sceneutil/shadow.cpp | 8 +- .../reference/modding/settings/shadows.rst | 14 +- files/settings-default.cfg | 4 +- files/ui/graphicspage.ui | 184 ++++++++++-------- 6 files changed, 131 insertions(+), 94 deletions(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 6bc22bad6..421691a55 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -1,6 +1,5 @@ #include "graphicspage.hpp" -#include #include #include #include @@ -145,6 +144,10 @@ bool Launcher::GraphicsPage::loadSettings() if (mEngineSettings.getBool("enable indoor shadows", "Shadows")) indoorShadowsCheckBox->setCheckState(Qt::Checked); + shadowNearFarComputationComboBox->setCurrentIndex( + shadowNearFarComputationComboBox->findText( + QString(tr(mEngineSettings.getString("near far computation", "Shadows").c_str())))); + int shadowDistLimit = mEngineSettings.getInt("maximum shadow map distance", "Shadows"); if (shadowDistLimit > 0) { @@ -231,7 +234,7 @@ void Launcher::GraphicsPage::saveSettings() bool cPlayerShadows = playerShadowsCheckBox->checkState(); if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows) { - if (mEngineSettings.getBool("enable shadows", "Shadows") != true) + if (!mEngineSettings.getBool("enable shadows", "Shadows")) mEngineSettings.setBool("enable shadows", "Shadows", true); if (mEngineSettings.getBool("actor shadows", "Shadows") != cActorShadows) mEngineSettings.setBool("actor shadows", "Shadows", cActorShadows); @@ -263,6 +266,10 @@ void Launcher::GraphicsPage::saveSettings() int cShadowRes = shadowResolutionComboBox->currentText().toInt(); if (cShadowRes != mEngineSettings.getInt("shadow map resolution", "Shadows")) mEngineSettings.setInt("shadow map resolution", "Shadows", cShadowRes); + + auto cShadowNearFarMode = shadowNearFarComputationComboBox->currentText().toStdString(); + if (cShadowNearFarMode != mEngineSettings.getString("near far computation", "Shadows")) + mEngineSettings.setString("near far computation", "Shadows", cShadowNearFarMode); } QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index b230625fc..4ce5b584f 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -38,8 +38,8 @@ namespace Launcher Files::ConfigurationManager &mCfgMgr; Settings::Manager &mEngineSettings; - QStringList getAvailableResolutions(int screen); - QRect getMaximumResolution(); + static QStringList getAvailableResolutions(int screen); + static QRect getMaximumResolution(); bool setupSDL(); }; diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index a1cd1d660..7c2dfd767 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -2,6 +2,7 @@ #include +#include #include namespace SceneUtil @@ -38,11 +39,14 @@ namespace SceneUtil } mShadowSettings->setMinimumShadowMapNearFarRatio(Settings::Manager::getFloat("minimum lispsm near far ratio", "Shadows")); - if (Settings::Manager::getBool("compute tight scene bounds", "Shadows")) + + std::string computeNearFarMode = Settings::Manager::getString("near far computation", "Shadows"); + if (Misc::StringUtils::lowerCase(computeNearFarMode) == "primitives") mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES); - else + else if (Misc::StringUtils::lowerCase(computeNearFarMode) == "bounding volumes") mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); + int mapres = Settings::Manager::getInt("shadow map resolution", "Shadows"); mShadowSettings->setTextureSize(osg::Vec2s(mapres, mapres)); diff --git a/docs/source/reference/modding/settings/shadows.rst b/docs/source/reference/modding/settings/shadows.rst index d0d92a6e2..861cc03ef 100644 --- a/docs/source/reference/modding/settings/shadows.rst +++ b/docs/source/reference/modding/settings/shadows.rst @@ -80,15 +80,15 @@ enable debug overlay Enable or disable the debug overlay to see the area covered by each shadow map. This setting is only recommended for developers, bug reporting and advanced users performing fine-tuning of shadow settings. -compute tight scene bounds --------------------------- +near far computation +-------------------- -:Type: boolean -:Range: True/False -:Default: True +:Type: string +:Range: bounding volumes|primitives +:Default: bounding volumes -With this setting enabled, attempt to better use the shadow map(s) by making them cover a smaller area. -May have a minor to major performance impact. +Two different ways to make better use of shadow map(s) by making them cover a smaller area. +While primitives give better results at expense of more CPU, bounding volumes gives better performance overall but with lower quality shadows. shadow map resolution --------------------- diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 6703e7732..7fc2db728 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -805,8 +805,8 @@ enable debug hud = false # Enable the debug overlay to see where each shadow map affects. enable debug overlay = false -# Attempt to better use the shadow map by making them cover a smaller area. May have a minor to major performance impact. -compute tight scene bounds = true +# Used to set type of tight scene bound calculation that makes use the shadow map by making them cover a smaller area. "bounding volumes" (default) is less precise shadows but lower CPU cost or "primitives" for more precise shadows at expense of CPU. +near far computation = bounding volumes # How large to make the shadow map(s). Higher values increase GPU load, but can produce better-looking results. Power-of-two values may turn out to be faster on some GPU/driver combinations. shadow map resolution = 1024 diff --git a/files/ui/graphicspage.ui b/files/ui/graphicspage.ui index 18d220797..1b11632e5 100644 --- a/files/ui/graphicspage.ui +++ b/files/ui/graphicspage.ui @@ -201,49 +201,92 @@ - - + + + - <html><head/><body><p>The distance from the camera at which shadows completely disappear.</p></body></html> + <html><head/><body><p>Enable shadows exclusively for the player character. May have a very minor performance impact.</p></body></html> - Shadow Distance Limit: + Enable Player Shadows - - - - false - + + - <html><head/><body><p>64 game units is 1 real life yard or about 0.9 m</p></body></html> + <html><head/><body><p>Enable shadows for NPCs and creatures besides the player character. May have a minor performance impact.</p></body></html> - - unit(s) - - - 512 - - - 81920 - - - 8192 + + Enable Actor Shadows - - + + - <html><head/><body><p>The fraction of the limit above at which shadows begin to gradually fade away.</p></body></html> + <html><head/><body><p>Enable shadows for primarily inanimate objects. May have a significant performance impact.</p></body></html> - Fade Start Multiplier: + Enable Object Shadows + + + + + + + <html><head/><body><p>Enable shadows for the terrain including distant terrain. May have a significant performance and shadow quality impact.</p></body></html> + + + Enable Terrain Shadows + + + + + + + <html><head/><body><p>Due to limitations with Morrowind's data, only actors can cast shadows indoors, which some might feel is distracting.</p><p>Has no effect if actor/player shadows are not enabled.</p></body></html> + + + Enable Indoor Shadows + + + + + + + <html><head/><body><p>Type of "near far plane" computation method to be used. Bounding Volumes (default) for better performance, Primitives for better looking shadows or none.</p></body></html> + + + Shadow Near Far Computation Method: + + + + bounding volumes + + + + + primitives + + + + + + + + <html><head/><body><p>The resolution of each individual shadow map. Increasing it significantly improves shadow quality but may have a minor performance impact.</p></body></html> + + + Shadow Map Resolution: + + + + @@ -267,27 +310,49 @@ - - + + - <html><head/><body><p>Enable shadows for NPCs and creatures besides the player character. May have a minor performance impact.</p></body></html> + <html><head/><body><p>The distance from the camera at which shadows completely disappear.</p></body></html> - Enable Actor Shadows - - - - - - - <html><head/><body><p>Enable shadows for the terrain including distant terrain. May have a significant performance and shadow quality impact.</p></body></html> - - - Enable Terrain Shadows + Shadow Distance Limit: + + + false + + + <html><head/><body><p>64 game units is 1 real life yard or about 0.9 m</p></body></html> + + + unit(s) + + + 512 + + + 81920 + + + 8192 + + + + + + + <html><head/><body><p>The fraction of the limit above at which shadows begin to gradually fade away.</p></body></html> + + + Fade Start Multiplier: + + + + false @@ -306,46 +371,7 @@ - - - - <html><head/><body><p>Enable shadows exclusively for the player character. May have a very minor performance impact.</p></body></html> - - - Enable Player Shadows - - - - - - - <html><head/><body><p>The resolution of each individual shadow map. Increasing it significantly improves shadow quality but may have a minor performance impact.</p></body></html> - - - Shadow Map Resolution: - - - - - - - <html><head/><body><p>Enable shadows for primarily inanimate objects. May have a significant performance impact.</p></body></html> - - - Enable Object Shadows - - - - - - - <html><head/><body><p>Due to limitations with Morrowind's data, only actors can cast shadows indoors, which some might feel is distracting.</p><p>Has no effect if actor/player shadows are not enabled.</p></body></html> - - - Enable Indoor Shadows - - - + From d38c3e971c71fe6de9227cc00b2d6c9371ee617d Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 29 Apr 2020 17:05:08 +0200 Subject: [PATCH 02/81] remove extra line --- components/sceneutil/shadow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index 7c2dfd767..f6858fca0 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -46,7 +46,6 @@ namespace SceneUtil else if (Misc::StringUtils::lowerCase(computeNearFarMode) == "bounding volumes") mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); - int mapres = Settings::Manager::getInt("shadow map resolution", "Shadows"); mShadowSettings->setTextureSize(osg::Vec2s(mapres, mapres)); From 980525cba47ffc7aabcd339e308d808bed844c9c Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 29 Apr 2020 17:08:12 +0200 Subject: [PATCH 03/81] better said --- files/settings-default.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 7fc2db728..44252b3bd 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -805,7 +805,7 @@ enable debug hud = false # Enable the debug overlay to see where each shadow map affects. enable debug overlay = false -# Used to set type of tight scene bound calculation that makes use the shadow map by making them cover a smaller area. "bounding volumes" (default) is less precise shadows but lower CPU cost or "primitives" for more precise shadows at expense of CPU. +# Used to set the type of tight scene bound calculation method to be used by the shadow map that covers a smaller area. "bounding volumes" (default) is less precise shadows but better performance or "primitives" for more precise shadows at expense of CPU. near far computation = bounding volumes # How large to make the shadow map(s). Higher values increase GPU load, but can produce better-looking results. Power-of-two values may turn out to be faster on some GPU/driver combinations. From bb5fe13e1395775dfe66f587a3bba01371b6feef Mon Sep 17 00:00:00 2001 From: psi29a Date: Wed, 29 Apr 2020 23:06:44 +0000 Subject: [PATCH 04/81] Make sure it is either one or the other with the default to bounding volumes. --- components/sceneutil/shadow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index f6858fca0..0476768f8 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -43,7 +43,7 @@ namespace SceneUtil std::string computeNearFarMode = Settings::Manager::getString("near far computation", "Shadows"); if (Misc::StringUtils::lowerCase(computeNearFarMode) == "primitives") mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES); - else if (Misc::StringUtils::lowerCase(computeNearFarMode) == "bounding volumes") + else mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); int mapres = Settings::Manager::getInt("shadow map resolution", "Shadows"); From aca223f6c80f33e22e3fb1e85785616408030f87 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Thu, 30 Apr 2020 17:41:26 +0200 Subject: [PATCH 05/81] fix unrelated rst issue; rename to bounds --- docs/source/reference/modding/settings/shadows.rst | 6 +++--- files/settings-default.cfg | 4 ++-- files/ui/graphicspage.ui | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/reference/modding/settings/shadows.rst b/docs/source/reference/modding/settings/shadows.rst index 861cc03ef..746e9b0df 100644 --- a/docs/source/reference/modding/settings/shadows.rst +++ b/docs/source/reference/modding/settings/shadows.rst @@ -71,7 +71,7 @@ Enable or disable the debug hud to see what the shadow map(s) contain. This setting is only recommended for developers, bug reporting and advanced users performing fine-tuning of shadow settings. enable debug overlay ----------------- +-------------------- :Type: boolean :Range: True/False @@ -84,11 +84,11 @@ near far computation -------------------- :Type: string -:Range: bounding volumes|primitives +:Range: primitives|bounds :Default: bounding volumes Two different ways to make better use of shadow map(s) by making them cover a smaller area. -While primitives give better results at expense of more CPU, bounding volumes gives better performance overall but with lower quality shadows. +While primitives give better shadows at expense of more CPU, bounds gives better performance overall but with lower quality shadows. shadow map resolution --------------------- diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 44252b3bd..5a0d787f9 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -805,8 +805,8 @@ enable debug hud = false # Enable the debug overlay to see where each shadow map affects. enable debug overlay = false -# Used to set the type of tight scene bound calculation method to be used by the shadow map that covers a smaller area. "bounding volumes" (default) is less precise shadows but better performance or "primitives" for more precise shadows at expense of CPU. -near far computation = bounding volumes +# Used to set the type of tight scene bound calculation method to be used by the shadow map that covers a smaller area. "bounds" (default) is less precise shadows but better performance or "primitives" for more precise shadows at expense of CPU. +near far computation = bounds # How large to make the shadow map(s). Higher values increase GPU load, but can produce better-looking results. Power-of-two values may turn out to be faster on some GPU/driver combinations. shadow map resolution = 1024 diff --git a/files/ui/graphicspage.ui b/files/ui/graphicspage.ui index 1b11632e5..9a7d986a7 100644 --- a/files/ui/graphicspage.ui +++ b/files/ui/graphicspage.ui @@ -266,7 +266,7 @@ - bounding volumes + bounds From 5f0f2f0f169e373b43825eb7d138f3ec6892c4d5 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 1 May 2020 00:34:31 +0200 Subject: [PATCH 06/81] rename to better reflect what is going on per AnyOldName3 comment; added none option --- apps/launcher/graphicspage.cpp | 12 ++++++------ components/sceneutil/shadow.cpp | 6 +++--- docs/source/reference/modding/settings/shadows.rst | 6 +++--- files/settings-default.cfg | 2 +- files/ui/graphicspage.ui | 13 +++++++++---- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 421691a55..d48e627e1 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -144,9 +144,9 @@ bool Launcher::GraphicsPage::loadSettings() if (mEngineSettings.getBool("enable indoor shadows", "Shadows")) indoorShadowsCheckBox->setCheckState(Qt::Checked); - shadowNearFarComputationComboBox->setCurrentIndex( - shadowNearFarComputationComboBox->findText( - QString(tr(mEngineSettings.getString("near far computation", "Shadows").c_str())))); + shadowComputeSceneBoundsComboBox->setCurrentIndex( + shadowComputeSceneBoundsComboBox->findText( + QString(tr(mEngineSettings.getString("compute scene bounds", "Shadows").c_str())))); int shadowDistLimit = mEngineSettings.getInt("maximum shadow map distance", "Shadows"); if (shadowDistLimit > 0) @@ -267,9 +267,9 @@ void Launcher::GraphicsPage::saveSettings() if (cShadowRes != mEngineSettings.getInt("shadow map resolution", "Shadows")) mEngineSettings.setInt("shadow map resolution", "Shadows", cShadowRes); - auto cShadowNearFarMode = shadowNearFarComputationComboBox->currentText().toStdString(); - if (cShadowNearFarMode != mEngineSettings.getString("near far computation", "Shadows")) - mEngineSettings.setString("near far computation", "Shadows", cShadowNearFarMode); + auto cComputeSceneBounds = shadowComputeSceneBoundsComboBox->currentText().toStdString(); + if (cComputeSceneBounds != mEngineSettings.getString("compute scene bounds", "Shadows")) + mEngineSettings.setString("compute scene bounds", "Shadows", cComputeSceneBounds); } QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index 0476768f8..1e14fbbb1 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -40,10 +40,10 @@ namespace SceneUtil mShadowSettings->setMinimumShadowMapNearFarRatio(Settings::Manager::getFloat("minimum lispsm near far ratio", "Shadows")); - std::string computeNearFarMode = Settings::Manager::getString("near far computation", "Shadows"); - if (Misc::StringUtils::lowerCase(computeNearFarMode) == "primitives") + std::string computeSceneBounds = Settings::Manager::getString("compute scene bounds", "Shadows"); + if (Misc::StringUtils::lowerCase(computeSceneBounds) == "primitives") mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES); - else + else if (Misc::StringUtils::lowerCase(computeSceneBounds) == "bounds") mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); int mapres = Settings::Manager::getInt("shadow map resolution", "Shadows"); diff --git a/docs/source/reference/modding/settings/shadows.rst b/docs/source/reference/modding/settings/shadows.rst index 746e9b0df..0670a8109 100644 --- a/docs/source/reference/modding/settings/shadows.rst +++ b/docs/source/reference/modding/settings/shadows.rst @@ -80,15 +80,15 @@ enable debug overlay Enable or disable the debug overlay to see the area covered by each shadow map. This setting is only recommended for developers, bug reporting and advanced users performing fine-tuning of shadow settings. -near far computation +compute scene bounds -------------------- :Type: string -:Range: primitives|bounds +:Range: primitives|bounds|none :Default: bounding volumes Two different ways to make better use of shadow map(s) by making them cover a smaller area. -While primitives give better shadows at expense of more CPU, bounds gives better performance overall but with lower quality shadows. +While primitives give better shadows at expense of more CPU, bounds gives better performance overall but with lower quality shadows. There is also the ability to disable this computation with none. shadow map resolution --------------------- diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 5a0d787f9..91c0443ec 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -806,7 +806,7 @@ enable debug hud = false enable debug overlay = false # Used to set the type of tight scene bound calculation method to be used by the shadow map that covers a smaller area. "bounds" (default) is less precise shadows but better performance or "primitives" for more precise shadows at expense of CPU. -near far computation = bounds +compute scene bounds = bounds # How large to make the shadow map(s). Higher values increase GPU load, but can produce better-looking results. Power-of-two values may turn out to be faster on some GPU/driver combinations. shadow map resolution = 1024 diff --git a/files/ui/graphicspage.ui b/files/ui/graphicspage.ui index 9a7d986a7..cfa2c800c 100644 --- a/files/ui/graphicspage.ui +++ b/files/ui/graphicspage.ui @@ -253,17 +253,17 @@ - + - <html><head/><body><p>Type of "near far plane" computation method to be used. Bounding Volumes (default) for better performance, Primitives for better looking shadows or none.</p></body></html> + <html><head/><body><p>Type of "compute scene bounds" computation method to be used. Bounds (default) for good balance between performance and shadow quality, primitives for better looking shadows or none for no computation.</p></body></html> Shadow Near Far Computation Method: - - + compute scene bounds + bounds @@ -274,6 +274,11 @@ primitives + + + none + + From 947f3cf13ce8bec733f0c8cca84b62865752d602 Mon Sep 17 00:00:00 2001 From: fredzio Date: Mon, 13 May 2019 22:32:46 +0200 Subject: [PATCH 07/81] Optionally change color of keywords in the dialogue window based on the next answer: - if the answer was already heard, apply "color topic exhaused" setting - if the answer was never heard, and the current actor is specified in the dialog, apply "color topic specific" - otherwise, do nothing special --- CHANGELOG.md | 1 + apps/openmw/mwbase/dialoguemanager.hpp | 9 +++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 55 ++++++++++++++++--- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 5 ++ apps/openmw/mwdialogue/filter.cpp | 12 ---- apps/openmw/mwdialogue/filter.hpp | 3 - apps/openmw/mwgui/dialogue.cpp | 30 ++++++++-- apps/openmw/mwgui/dialogue.hpp | 2 + .../source/reference/modding/settings/GUI.rst | 26 +++++++++ files/settings-default.cfg | 8 +++ 10 files changed, 123 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77b126d85..da480ab67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -225,6 +225,7 @@ Bug #5350: An attempt to launch magic bolt causes "AL error invalid value" error Bug #5352: Light source items' duration is decremented while they aren't visible Feature #1724: Handle AvoidNode + Feature #2159: "Graying out" exhausted dialogue topics Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls Feature #3442: Default values for fallbacks from ini file diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 2ecab1c4c..e25762f32 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -53,6 +53,8 @@ namespace MWBase virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback) = 0; + virtual bool inJournal (const std::string& topicId, const std::string& infoId) = 0; + virtual void addTopic (const std::string& topic) = 0; virtual void addChoice (const std::string& text,int choice) = 0; @@ -68,7 +70,14 @@ namespace MWBase virtual void goodbyeSelected() = 0; virtual void questionAnswered (int answer, ResponseCallback* callback) = 0; + enum TopicType + { + Specific = 1, + Exhausted = 2 + }; + virtual std::list getAvailableTopics() = 0; + virtual int getTopicFlag(const std::string&) = 0; virtual bool checkServiceRefused (ResponseCallback* callback) = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 21bc067d7..3e0cbbad2 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -228,6 +228,30 @@ namespace MWDialogue } } + bool DialogueManager::inJournal (const std::string& topicId, const std::string& infoId) + { + const MWDialogue::Topic *topicHistory = nullptr; + MWBase::Journal *journal = MWBase::Environment::get().getJournal(); + for (auto it = journal->topicBegin(); it != journal->topicEnd(); ++it) + { + if (it->first == topicId) + { + topicHistory = &it->second; + break; + } + } + + if (!topicHistory) + return false; + + for(const auto& topic : *topicHistory) + { + if (topic.mInfoId == infoId) + return true; + } + return false; + } + void DialogueManager::executeTopic (const std::string& topic, ResponseCallback* callback) { Filter filter (mActor, mChoice, mTalkedTo); @@ -300,22 +324,34 @@ namespace MWDialogue mActorKnownTopics.clear(); - const MWWorld::Store &dialogs = - MWBase::Environment::get().getWorld()->getStore().get(); + const auto& dialogs = MWBase::Environment::get().getWorld()->getStore().get(); Filter filter (mActor, -1, mTalkedTo); - for (MWWorld::Store::iterator iter = dialogs.begin(); iter != dialogs.end(); ++iter) + for (const auto& dialog : dialogs) { - if (iter->mType == ESM::Dialogue::Topic) + if (dialog.mType == ESM::Dialogue::Topic) { - if (filter.responseAvailable (*iter)) + const auto* answer = filter.search(dialog, true); + auto topicId = Misc::StringUtils::lowerCase(dialog.mId); + + if (answer != nullptr) { - mActorKnownTopics.insert (iter->mId); + int flag = 0; + if(!inJournal(topicId, answer->mId)) + { + // Does this dialogue contains some actor-specific answer? + if (answer->mActor == mActor.getCellRef().getRefId()) + flag |= MWBase::DialogueManager::TopicType::Specific; + } + else + flag |= MWBase::DialogueManager::TopicType::Exhausted; + mActorKnownTopics.insert (dialog.mId); + mActorKnownTopicsFlag[dialog.mId] = flag; } + } } - } std::list DialogueManager::getAvailableTopics() @@ -336,6 +372,11 @@ namespace MWDialogue return keywordList; } + int DialogueManager::getTopicFlag(const std::string& topicId) + { + return mActorKnownTopicsFlag[topicId]; + } + void DialogueManager::keywordSelected (const std::string& keyword, ResponseCallback* callback) { if(!mIsInChoice) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index fad086183..23dd68e91 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -30,6 +31,7 @@ namespace MWDialogue ModFactionReactionMap mChangedFactionReaction; std::set mActorKnownTopics; + std::unordered_map mActorKnownTopicsFlag; Translation::Storage& mTranslationDataStorage; MWScript::CompilerContext mCompilerContext; @@ -71,6 +73,9 @@ namespace MWDialogue virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback); std::list getAvailableTopics(); + int getTopicFlag(const std::string& topicId) final; + + bool inJournal (const std::string& topicId, const std::string& infoId) final; virtual void addTopic (const std::string& topic); diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 042ccb019..a3c326ab8 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -681,15 +681,3 @@ std::vector MWDialogue::Filter::list (const ESM::Dialogue return infos; } - -bool MWDialogue::Filter::responseAvailable (const ESM::Dialogue& dialogue) const -{ - for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); - iter!=dialogue.mInfo.end(); ++iter) - { - if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) - return true; - } - - return false; -} diff --git a/apps/openmw/mwdialogue/filter.hpp b/apps/openmw/mwdialogue/filter.hpp index 4e2ebe6e5..d2747d59a 100644 --- a/apps/openmw/mwdialogue/filter.hpp +++ b/apps/openmw/mwdialogue/filter.hpp @@ -66,9 +66,6 @@ namespace MWDialogue const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; ///< Get a matching response for the requested dialogue. /// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition. - - bool responseAvailable (const ESM::Dialogue& dialogue) const; - ///< Does a matching response exist? (disposition is ignored for this check) }; } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index bb40bea33..1dcd8d5ea 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -339,6 +339,7 @@ namespace MWGui mTopicsList->adjustSize(); updateHistory(); + updateTopicFormat(); mCurrentWindowSize = _sender->getSize(); } @@ -448,7 +449,6 @@ namespace MWGui setTitle(mPtr.getClass().getName(mPtr)); updateTopics(); - updateTopicsPane(); // force update for new services updateDisposition(); restock(); @@ -489,8 +489,6 @@ namespace MWGui return; mIsCompanion = isCompanion(); mKeywords = keyWords; - - updateTopicsPane(); } void DialogueWindow::updateTopicsPane() @@ -540,15 +538,16 @@ namespace MWGui mTopicsList->addSeparator(); - for(std::string& keyword : mKeywords) + for(const auto& keyword : mKeywords) { + std::string topicId = Misc::StringUtils::lowerCase(keyword); mTopicsList->addItem(keyword); Topic* t = new Topic(keyword); t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated); - mTopicLinks[Misc::StringUtils::lowerCase(keyword)] = t; + mTopicLinks[topicId] = t; - mKeywordSearch.seed(Misc::StringUtils::lowerCase(keyword), intptr_t(t)); + mKeywordSearch.seed(topicId, intptr_t(t)); } mTopicsList->adjustSize(); @@ -734,9 +733,28 @@ namespace MWGui updateHistory(); } + void DialogueWindow::updateTopicFormat() + { + std::string specialColour = Settings::Manager::getString("color topic specific", "GUI"); + std::string oldColour = Settings::Manager::getString("color topic exhausted", "GUI"); + + for (const std::string& keyword : mKeywords) + { + int flag = MWBase::Environment::get().getDialogueManager()->getTopicFlag(keyword); + MyGUI::Button* button = mTopicsList->getItemWidget(keyword); + + if (!specialColour.empty() && flag & MWBase::DialogueManager::TopicType::Specific) + button->getSubWidgetText()->setTextColour(MyGUI::Colour::parse(specialColour)); + else if (!oldColour.empty() && flag & MWBase::DialogueManager::TopicType::Exhausted) + button->getSubWidgetText()->setTextColour(MyGUI::Colour::parse(oldColour)); + } + } + void DialogueWindow::updateTopics() { setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics()); + updateTopicsPane(); + updateTopicFormat(); } bool DialogueWindow::isCompanion() diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 2c3fb1a44..d9c26ef20 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -186,6 +186,8 @@ namespace MWGui std::unique_ptr mCallback; std::unique_ptr mGreetingCallback; + + void updateTopicFormat(); }; } #endif diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index c8f4e16f8..b881221b3 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -140,3 +140,29 @@ The alpha value is currently ignored. This setting can only be configured by editing the settings configuration file. This setting has no effect if the crosshair setting in the HUD Settings Section is false. This setting has no effect if the show owned setting in the Game Settings Section is false. + +color topic specific +-------------------- + +:Type: RGBA floating point +:Range: 0.0 to 1.0 +:Default: empty + +This setting overrides the color of keywords in the dialogue topic window. +The value is composed of four floating point values representing the red, green, blue and alpha channels. +The alpha value is currently ignored. + +The color is overriden if the actor is about to give an answer that is unique to him (that is, dialogue with their object ID in the Actor field) that wasn't seen yet. + +color topic exhausted +--------------------- + +:Type: RGBA floating point +:Range: 0.0 to 1.0 +:Default: empty + +This setting overrides the color of keywords in the dialogue topic window. +The value is composed of four floating point values representing the red, green, blue and alpha channels. +The alpha value is currently ignored. + +The color is overridden if the next actor responses to the topic keyword has already been seen by the player. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 6703e7732..3203d6743 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -190,6 +190,14 @@ color crosshair owned = 1.0 0.15 0.15 1.0 # Controls whether Arrow keys, Movement keys, Tab/Shift-Tab and Spacebar/Enter/Activate may be used to navigate GUI buttons. keyboard navigation = true +# The color of dialogue topic keywords that gives unique actor responses +# Format R G B A or empty for default +color topic specific = + +# The color of dialogue topic keywords that gives already read responses +# Format R G B A or empty for default +color topic exhausted = + [HUD] # Displays the crosshair or reticle when not in GUI mode. From d6e4fbe0850bec85dffa26f86551b5ddccaecefe Mon Sep 17 00:00:00 2001 From: laikh Date: Wed, 6 May 2020 12:14:26 +0800 Subject: [PATCH 08/81] Fix mingw Windows build --- CMakeLists.txt | 5 ++++- components/CMakeLists.txt | 3 --- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cc9ccd55..5db93ddd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,7 +304,10 @@ endif() set(BOOST_COMPONENTS system filesystem program_options iostreams) if(WIN32) - set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale zlib) + set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) + if(MSVC) + set(BOOST_COMPONENTS ${BOOST_COMPONENTS} zlib) + endif(MSVC) endif(WIN32) IF(BOOST_STATIC) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f626fe714..e7fc8b2c2 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -267,9 +267,6 @@ endif (GIT_CHECKOUT) if (WIN32) target_link_libraries(components shlwapi) - if(MINGW) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNOGDI") - endif(MINGW) endif() # Fix for not visible pthreads functions for linker with glibc 2.15 From 82893c30f41a40226c83c86c3c649ad5874f1093 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 21:52:16 +0200 Subject: [PATCH 09/81] Store package type id as enum except ESM --- apps/openmw/mwmechanics/actors.cpp | 22 +++---- apps/openmw/mwmechanics/aiactivate.hpp | 2 +- apps/openmw/mwmechanics/aiavoiddoor.cpp | 2 +- apps/openmw/mwmechanics/aiavoiddoor.hpp | 2 +- apps/openmw/mwmechanics/aibreathe.hpp | 2 +- apps/openmw/mwmechanics/aicast.hpp | 2 +- apps/openmw/mwmechanics/aicombat.hpp | 2 +- apps/openmw/mwmechanics/aiescort.hpp | 2 +- apps/openmw/mwmechanics/aiface.hpp | 2 +- apps/openmw/mwmechanics/aifollow.hpp | 2 +- apps/openmw/mwmechanics/aipackage.cpp | 8 +-- apps/openmw/mwmechanics/aipackage.hpp | 27 ++------ apps/openmw/mwmechanics/aipackagetypeid.hpp | 28 ++++++++ apps/openmw/mwmechanics/aipursue.hpp | 2 +- apps/openmw/mwmechanics/aisequence.cpp | 65 ++++++++++--------- apps/openmw/mwmechanics/aisequence.hpp | 13 ++-- apps/openmw/mwmechanics/aitravel.hpp | 4 +- apps/openmw/mwmechanics/aiwander.hpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 14 ++-- apps/openmw/mwscript/aiextensions.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 4 +- 21 files changed, 111 insertions(+), 98 deletions(-) create mode 100644 apps/openmw/mwmechanics/aipackagetypeid.hpp diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index aa5aac80a..b82d08964 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -115,7 +115,7 @@ void adjustCommandedActor (const MWWorld::Ptr& actor) auto it = stats.getAiSequence().begin(); for (; it != stats.getAiSequence().end(); ++it) { - if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && + if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Follow && static_cast(it->get())->isCommanded()) { hasCommandPackage = true; @@ -453,7 +453,7 @@ namespace MWMechanics return; const MWMechanics::AiSequence& seq = stats.getAiSequence(); - if (seq.isInCombat() || seq.hasPackage(AiPackage::TypeIdFollow) || seq.hasPackage(AiPackage::TypeIdEscort)) + if (seq.isInCombat() || seq.hasPackage(AiPackageTypeId::Follow) || seq.hasPackage(AiPackageTypeId::Escort)) return; const osg::Vec3f playerPos(getPlayer().getRefData().getPosition().asVec3()); @@ -497,11 +497,11 @@ namespace MWMechanics CreatureStats &stats = actor.getClass().getCreatureStats(actor); const MWMechanics::AiSequence& seq = stats.getAiSequence(); - int packageId = seq.getTypeId(); + const auto packageId = seq.getTypeId(); if (seq.isInCombat() || MWBase::Environment::get().getWorld()->isSwimming(actor) || - (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1)) + (packageId != AiPackageTypeId::Wander && packageId != AiPackageTypeId::Travel && packageId != AiPackageTypeId::None)) { actorState.setTurningToPlayer(false); actorState.setGreetingTimer(0); @@ -724,7 +724,7 @@ namespace MWMechanics followerOrEscorter = true; break; } - else if (package->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + else if (package->getTypeId() != MWMechanics::AiPackageTypeId::Combat) break; } if (!followerOrEscorter) @@ -1259,7 +1259,7 @@ namespace MWMechanics if (!isPlayer && stats.getTimeToStartDrowning() < fHoldBreathTime / 2) { AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); - if (seq.getTypeId() != AiPackage::TypeIdBreathe) //Only add it once + if (seq.getTypeId() != AiPackageTypeId::Breathe) //Only add it once seq.stack(AiBreathe(), ptr); } @@ -1410,7 +1410,7 @@ namespace MWMechanics if (player.getClass().getNpcStats(player).isWerewolf()) return; - if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.getAiSequence().isInCombat() + if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackageTypeId::Pursue && !creatureStats.getAiSequence().isInCombat() && creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -1773,7 +1773,7 @@ namespace MWMechanics // 3. Player character does not use headtracking in the 1st-person view if (!stats.getKnockedDown() && !stats.getAiSequence().isInCombat() && - !stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue) && + !stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue) && !firstPersonPlayer) { for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) @@ -2276,7 +2276,7 @@ namespace MWMechanics } break; } - else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) + else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander) break; } } @@ -2302,7 +2302,7 @@ namespace MWMechanics { if (package->followTargetThroughDoors() && package->getTarget() == actor) list.push_back(iteratedActor); - else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) + else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander) break; } } @@ -2368,7 +2368,7 @@ namespace MWMechanics list.push_back(static_cast(package.get())->getFollowIndex()); break; } - else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) + else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander) break; } } diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index b263e74a6..c53744e88 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -30,7 +30,7 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - static constexpr TypeId getTypeId() { return TypeIdActivate; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Activate; } void writeState(ESM::AiSequence::AiSequence& sequence) const final; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index d8517c5c9..47f72efce 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -63,7 +63,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont for(std::vector::iterator it = actors.begin(); it != actors.end(); ++it) { if(*it != getPlayer()) { //Not the player MWMechanics::AiSequence& seq = it->getClass().getCreatureStats(*it).getAiSequence(); - if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) { //Only add it once + if(seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor) { //Only add it once seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr),*it); } } diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index 72cde1026..cd0718e2e 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -24,7 +24,7 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - static constexpr TypeId getTypeId() { return TypeIdAvoidDoor; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::AvoidDoor; } static constexpr Options makeDefaultOptions() { diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp index 2a04ab2ad..7e9ac69da 100644 --- a/apps/openmw/mwmechanics/aibreathe.hpp +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -12,7 +12,7 @@ namespace MWMechanics public: bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - static constexpr TypeId getTypeId() { return TypeIdBreathe; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Breathe; } static constexpr Options makeDefaultOptions() { diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index 22575c7bc..1175dccd2 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -17,7 +17,7 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - static constexpr TypeId getTypeId() { return TypeIdCast; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Cast; } MWWorld::Ptr getTarget() const final; diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index ef8782ae1..ff8a0e081 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -104,7 +104,7 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - static constexpr TypeId getTypeId() { return TypeIdCombat; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Combat; } static constexpr Options makeDefaultOptions() { diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 42558bf7c..edfd0ca3f 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -32,7 +32,7 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - static constexpr TypeId getTypeId() { return TypeIdEscort; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Escort; } static constexpr Options makeDefaultOptions() { diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index 3a9a482c0..a5ee67a14 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -12,7 +12,7 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - static constexpr TypeId getTypeId() { return TypeIdFace; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Face; } static constexpr Options makeDefaultOptions() { diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index b4cf88be8..d0c2cec97 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -55,7 +55,7 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - static constexpr TypeId getTypeId() { return TypeIdFollow; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Follow; } static constexpr Options makeDefaultOptions() { diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 66b41db54..01440ca93 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -24,7 +24,7 @@ #include -MWMechanics::AiPackage::AiPackage(TypeId typeId, const Options& options) : +MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) : mTypeId(typeId), mOptions(options), mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild @@ -216,7 +216,7 @@ namespace void MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor) { // note: AiWander currently does not open doors - if (getTypeId() == TypeIdWander) + if (getTypeId() == AiPackageTypeId::Wander) return; if (mPathFinder.getPathSize() == 0) @@ -391,13 +391,13 @@ DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld:: const MWWorld::Class& actorClass = actor.getClass(); DetourNavigator::Flags result = DetourNavigator::Flag_none; - if (actorClass.isPureWaterCreature(actor) || (getTypeId() != TypeIdWander && actorClass.canSwim(actor))) + if (actorClass.isPureWaterCreature(actor) || (getTypeId() != AiPackageTypeId::Wander && actorClass.canSwim(actor))) result |= DetourNavigator::Flag_swim; if (actorClass.canWalk(actor)) result |= DetourNavigator::Flag_walk; - if (actorClass.isBipedal(actor) && getTypeId() != TypeIdWander) + if (actorClass.isBipedal(actor) && getTypeId() != AiPackageTypeId::Wander) result |= DetourNavigator::Flag_openDoor; return result; diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index c32fb93aa..3078e31fc 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -8,6 +8,7 @@ #include "pathfinding.hpp" #include "obstacle.hpp" #include "aistate.hpp" +#include "aipackagetypeid.hpp" namespace MWWorld { @@ -35,26 +36,6 @@ namespace MWMechanics class AiPackage { public: - ///Enumerates the various AITypes available - enum TypeId { - TypeIdNone = -1, - TypeIdWander = 0, - TypeIdTravel = 1, - TypeIdEscort = 2, - TypeIdFollow = 3, - TypeIdActivate = 4, - - // These 5 are not really handled as Ai Packages in the MW engine - // For compatibility do *not* return these in the getCurrentAiPackage script function.. - TypeIdCombat = 5, - TypeIdPursue = 6, - TypeIdAvoidDoor = 7, - TypeIdFace = 8, - TypeIdBreathe = 9, - TypeIdInternalTravel = 10, - TypeIdCast = 11 - }; - struct Options { unsigned int mPriority = 0; @@ -79,7 +60,7 @@ namespace MWMechanics } }; - AiPackage(TypeId typeId, const Options& options); + AiPackage(AiPackageTypeId typeId, const Options& options); virtual ~AiPackage() = default; @@ -97,7 +78,7 @@ namespace MWMechanics /// Returns the TypeID of the AiPackage /// \see enum TypeId - TypeId getTypeId() const { return mTypeId; } + AiPackageTypeId getTypeId() const { return mTypeId; } /// Higher number is higher priority (0 being the lowest) unsigned int getPriority() const { return mOptions.mPriority; } @@ -167,7 +148,7 @@ namespace MWMechanics DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; - const TypeId mTypeId; + const AiPackageTypeId mTypeId; const Options mOptions; // TODO: all this does not belong here, move into temporary storage diff --git a/apps/openmw/mwmechanics/aipackagetypeid.hpp b/apps/openmw/mwmechanics/aipackagetypeid.hpp new file mode 100644 index 000000000..2b9c4fe9c --- /dev/null +++ b/apps/openmw/mwmechanics/aipackagetypeid.hpp @@ -0,0 +1,28 @@ +#ifndef GAME_MWMECHANICS_AIPACKAGETYPEID_H +#define GAME_MWMECHANICS_AIPACKAGETYPEID_H + +namespace MWMechanics +{ + ///Enumerates the various AITypes available + enum class AiPackageTypeId + { + None = -1, + Wander = 0, + Travel = 1, + Escort = 2, + Follow = 3, + Activate = 4, + + // These 5 are not really handled as Ai Packages in the MW engine + // For compatibility do *not* return these in the getCurrentAiPackage script function.. + Combat = 5, + Pursue = 6, + AvoidDoor = 7, + Face = 8, + Breathe = 9, + InternalTravel = 10, + Cast = 11 + }; +} + +#endif diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 6031f84fb..64465f530 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -28,7 +28,7 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - static constexpr TypeId getTypeId() { return TypeIdPursue; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Pursue; } static constexpr Options makeDefaultOptions() { diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 4a23dc788..f747b16f2 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -33,7 +33,7 @@ void AiSequence::copy (const AiSequence& sequence) sequence.mAiState.copy(mAiState); } -AiSequence::AiSequence() : mDone (false), mRepeat(false), mLastAiPackage(-1) {} +AiSequence::AiSequence() : mDone (false), mRepeat(false), mLastAiPackage(AiPackageTypeId::None) {} AiSequence::AiSequence (const AiSequence& sequence) { @@ -61,17 +61,17 @@ AiSequence::~AiSequence() clear(); } -int AiSequence::getTypeId() const +AiPackageTypeId AiSequence::getTypeId() const { if (mPackages.empty()) - return -1; + return AiPackageTypeId::None; return mPackages.front()->getTypeId(); } bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const { - if (getTypeId() != AiPackage::TypeIdCombat) + if (getTypeId() != AiPackageTypeId::Combat) return false; targetActor = mPackages.front()->getTarget(); @@ -83,7 +83,7 @@ bool AiSequence::getCombatTargets(std::vector &targetActors) const { for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { - if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCombat) + if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Combat) targetActors.push_back((*it)->getTarget()); } @@ -118,7 +118,7 @@ bool AiSequence::isInCombat() const { for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { - if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + if ((*it)->getTypeId() == AiPackageTypeId::Combat) return true; } return false; @@ -128,7 +128,7 @@ bool AiSequence::isEngagedWithActor() const { for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { - if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + if ((*it)->getTypeId() == AiPackageTypeId::Combat) { MWWorld::Ptr target2 = (*it)->getTarget(); if (!target2.isEmpty() && target2.getClass().isNpc()) @@ -138,7 +138,7 @@ bool AiSequence::isEngagedWithActor() const return false; } -bool AiSequence::hasPackage(int typeId) const +bool AiSequence::hasPackage(AiPackageTypeId typeId) const { for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { @@ -152,7 +152,7 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const { for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { - if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + if ((*it)->getTypeId() == AiPackageTypeId::Combat) { if ((*it)->getTarget() == actor) return true; @@ -165,7 +165,7 @@ void AiSequence::stopCombat() { for(auto it = mPackages.begin(); it != mPackages.end(); ) { - if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + if ((*it)->getTypeId() == AiPackageTypeId::Combat) { it = mPackages.erase(it); } @@ -178,7 +178,7 @@ void AiSequence::stopPursuit() { for(auto it = mPackages.begin(); it != mPackages.end(); ) { - if ((*it)->getTypeId() == AiPackage::TypeIdPursue) + if ((*it)->getTypeId() == AiPackageTypeId::Pursue) { it = mPackages.erase(it); } @@ -192,10 +192,13 @@ bool AiSequence::isPackageDone() const return mDone; } -bool isActualAiPackage(int packageTypeId) +namespace { - return (packageTypeId >= AiPackage::TypeIdWander && - packageTypeId <= AiPackage::TypeIdActivate); + bool isActualAiPackage(AiPackageTypeId packageTypeId) + { + return (packageTypeId >= AiPackageTypeId::Wander && + packageTypeId <= AiPackageTypeId::Activate); + } } void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration, bool outOfRange) @@ -204,7 +207,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac { if (mPackages.empty()) { - mLastAiPackage = -1; + mLastAiPackage = AiPackageTypeId::None; return; } @@ -213,12 +216,12 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac if (!package->alwaysActive() && outOfRange) return; - int packageTypeId = package->getTypeId(); + auto packageTypeId = package->getTypeId(); // workaround ai packages not being handled as in the vanilla engine if (isActualAiPackage(packageTypeId)) mLastAiPackage = packageTypeId; // if active package is combat one, choose nearest target - if (packageTypeId == AiPackage::TypeIdCombat) + if (packageTypeId == AiPackageTypeId::Combat) { auto itActualCombat = mPackages.end(); @@ -229,7 +232,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac for (auto it = mPackages.begin(); it != mPackages.end();) { - if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; + if ((*it)->getTypeId() != AiPackageTypeId::Combat) break; MWWorld::Ptr target = (*it)->getTarget(); @@ -320,16 +323,16 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo // We should return a wandering actor back after combat, casting or pursuit. // The same thing for actors without AI packages. // Also there is no point to stack return packages. - int currentTypeId = getTypeId(); - int newTypeId = package.getTypeId(); - if (currentTypeId <= MWMechanics::AiPackage::TypeIdWander - && !hasPackage(MWMechanics::AiPackage::TypeIdInternalTravel) - && (newTypeId <= MWMechanics::AiPackage::TypeIdCombat - || newTypeId == MWMechanics::AiPackage::TypeIdPursue - || newTypeId == MWMechanics::AiPackage::TypeIdCast)) + const auto currentTypeId = getTypeId(); + const auto newTypeId = package.getTypeId(); + if (currentTypeId <= MWMechanics::AiPackageTypeId::Wander + && !hasPackage(MWMechanics::AiPackageTypeId::InternalTravel) + && (newTypeId <= MWMechanics::AiPackageTypeId::Combat + || newTypeId == MWMechanics::AiPackageTypeId::Pursue + || newTypeId == MWMechanics::AiPackageTypeId::Cast)) { osg::Vec3f dest; - if (currentTypeId == MWMechanics::AiPackage::TypeIdWander) + if (currentTypeId == MWMechanics::AiPackageTypeId::Wander) { dest = getActivePackage().getDestination(actor); } @@ -361,8 +364,8 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { // We should keep current AiCast package, if we try to add a new one. - if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast && - package.getTypeId() == MWMechanics::AiPackage::TypeIdCast) + if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Cast && + package.getTypeId() == MWMechanics::AiPackageTypeId::Cast) { continue; } @@ -444,7 +447,7 @@ void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const for (const auto& package : mPackages) package->writeState(sequence); - sequence.mLastAiPackage = mLastAiPackage; + sequence.mLastAiPackage = static_cast(mLastAiPackage); } void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) @@ -457,7 +460,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) for (std::vector::const_iterator it = sequence.mPackages.begin(); it != sequence.mPackages.end(); ++it) { - if (isActualAiPackage(it->mType)) + if (isActualAiPackage(static_cast(it->mType))) count++; } @@ -520,7 +523,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) mPackages.push_back(std::move(package)); } - mLastAiPackage = sequence.mLastAiPackage; + mLastAiPackage = static_cast(sequence.mLastAiPackage); } void AiSequence::fastForward(const MWWorld::Ptr& actor) diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 12b837d87..645524d38 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -5,6 +5,7 @@ #include #include "aistate.hpp" +#include "aipackagetypeid.hpp" #include @@ -49,7 +50,7 @@ namespace MWMechanics void copy (const AiSequence& sequence); /// The type of AI package that ran last - int mLastAiPackage; + AiPackageTypeId mLastAiPackage; AiState mAiState; public: @@ -71,14 +72,14 @@ namespace MWMechanics void erase(std::list>::const_iterator package); /// Returns currently executing AiPackage type - /** \see enum AiPackage::TypeId **/ - int getTypeId() const; + /** \see enum class AiPackageTypeId **/ + AiPackageTypeId getTypeId() const; /// Get the typeid of the Ai package that ran last /** NOT the currently "active" Ai package that will be run in the next frame. This difference is important when an Ai package has just finished and been removed. - \see enum AiPackage::TypeId **/ - int getLastRunTypeId() const { return mLastAiPackage; } + \see enum class AiPackageTypeId **/ + AiPackageTypeId getLastRunTypeId() const { return mLastAiPackage; } /// Return true and assign target if combat package is currently active, return false otherwise bool getCombatTarget (MWWorld::Ptr &targetActor) const; @@ -93,7 +94,7 @@ namespace MWMechanics bool isEngagedWithActor () const; /// Does this AI sequence have the given package type? - bool hasPackage(int typeId) const; + bool hasPackage(AiPackageTypeId typeId) const; /// Are we in combat with this particular actor? bool isInCombat (const MWWorld::Ptr& actor) const; diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 3049801cd..0c19572d2 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -34,7 +34,7 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - static constexpr TypeId getTypeId() { return TypeIdTravel; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Travel; } static constexpr Options makeDefaultOptions() { @@ -60,7 +60,7 @@ namespace MWMechanics explicit AiInternalTravel(const ESM::AiSequence::AiTravel* travel); - static constexpr TypeId getTypeId() { return TypeIdInternalTravel; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::InternalTravel; } std::unique_ptr clone() const final; }; diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 8171107d9..4138c3dea 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -93,7 +93,7 @@ namespace MWMechanics bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - static constexpr TypeId getTypeId() { return TypeIdWander; } + static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Wander; } static constexpr Options makeDefaultOptions() { diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5f5e7d13b..4150f18f2 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1277,7 +1277,7 @@ namespace MWMechanics { bool reported = false; if (victim.getClass().isClass(victim, "guard") - && !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) + && !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackageTypeId::Pursue)) reported = reportCrime(player, victim, type, std::string(), arg); if (!reported) @@ -1300,7 +1300,7 @@ namespace MWMechanics return false; // Player's followers should not attack player, or try to arrest him - if (actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackage::TypeIdFollow)) + if (actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackageTypeId::Follow)) { if (playerFollowers.find(actor) != playerFollowers.end()) return false; @@ -1417,7 +1417,7 @@ namespace MWMechanics // once the bounty has been paid. actor.getClass().getNpcStats(actor).setCrimeId(id); - if (!actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) + if (!actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackageTypeId::Pursue)) { actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiPursue(player), actor); } @@ -1495,7 +1495,7 @@ namespace MWMechanics { // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. // Note: accidental or collateral damage attacks are ignored. - if (!victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) + if (!victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackageTypeId::Pursue)) startCombat(victim, player); // Set the crime ID, which we will use to calm down participants @@ -1541,7 +1541,7 @@ namespace MWMechanics { // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. // Note: accidental or collateral damage attacks are ignored. - if (!target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) + if (!target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackageTypeId::Pursue)) { // If an actor has OnPCHitMe declared in his script, his Fight = 0 and the attacker is player, // he will attack the player only if we will force him (e.g. via StartCombat console command) @@ -1566,7 +1566,7 @@ namespace MWMechanics const MWMechanics::AiSequence& seq = target.getClass().getCreatureStats(target).getAiSequence(); return target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker) && !isAggressive(target, attacker) && !seq.isEngagedWithActor() - && !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue); + && !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackageTypeId::Pursue); } void MechanicsManager::actorKilled(const MWWorld::Ptr &victim, const MWWorld::Ptr &attacker) @@ -1702,7 +1702,7 @@ namespace MWMechanics if (iter->first.getClass().isClass(iter->first, "Guard")) { MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence(); - if (aiSeq.getTypeId() == MWMechanics::AiPackage::TypeIdPursue) + if (aiSeq.getTypeId() == MWMechanics::AiPackageTypeId::Pursue) { aiSeq.stopPursuit(); aiSeq.stack(MWMechanics::AiCombat(target), ptr); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 2918ffa36..2e426701c 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -362,7 +362,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).getAiSequence().getLastRunTypeId(); + const auto value = static_cast(ptr.getClass().getCreatureStats (ptr).getAiSequence().getLastRunTypeId()); runtime.push (value); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c51266bab..7d615c22f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1559,7 +1559,7 @@ namespace MWWorld if(ptr != getPlayerPtr() ) { MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); - if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once + if(seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor) //Only add it once seq.stack(MWMechanics::AiAvoidDoor(door),ptr); } @@ -2948,7 +2948,7 @@ namespace MWWorld { for (const auto& package : stats.getAiSequence()) { - if (package->getTypeId() == MWMechanics::AiPackage::TypeIdCast) + if (package->getTypeId() == MWMechanics::AiPackageTypeId::Cast) { target = package->getTarget(); break; From 095a45c714d568561f811b8b8305b48c77c71bd7 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 11 Jun 2020 21:43:51 +0200 Subject: [PATCH 10/81] Remove unused PathgridGraph::mIsExterior --- apps/openmw/mwmechanics/pathgrid.cpp | 2 -- apps/openmw/mwmechanics/pathgrid.hpp | 1 - 2 files changed, 3 deletions(-) diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index 7bcdc8926..ee1de3b5a 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -52,7 +52,6 @@ namespace MWMechanics PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell) : mCell(nullptr) , mPathgrid(nullptr) - , mIsExterior(0) , mGraph(0) , mIsGraphConstructed(false) , mSCCId(0) @@ -106,7 +105,6 @@ namespace MWMechanics return true; mCell = cell->getCell(); - mIsExterior = cell->getCell()->isExterior(); mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell->getCell()); if(!mPathgrid) return false; diff --git a/apps/openmw/mwmechanics/pathgrid.hpp b/apps/openmw/mwmechanics/pathgrid.hpp index 6b67bb507..050504617 100644 --- a/apps/openmw/mwmechanics/pathgrid.hpp +++ b/apps/openmw/mwmechanics/pathgrid.hpp @@ -44,7 +44,6 @@ namespace MWMechanics const ESM::Cell *mCell; const ESM::Pathgrid *mPathgrid; - bool mIsExterior; struct ConnectedPoint // edge { From c4cd3b2c4f488335355ae583e61ebdebd2a85c03 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 11 Jun 2020 23:23:30 +0200 Subject: [PATCH 11/81] Add pathgrid to navmesh as off mesh connection --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwmechanics/aicombat.cpp | 4 +- apps/openmw/mwmechanics/aipackage.cpp | 4 +- apps/openmw/mwmechanics/aiwander.cpp | 6 +- .../mwmechanics/coordinateconverter.cpp | 44 ------------ .../mwmechanics/coordinateconverter.hpp | 37 ---------- apps/openmw/mwmechanics/pathfinding.cpp | 8 ++- apps/openmw/mwrender/pathgrid.cpp | 4 +- apps/openmw/mwworld/scene.cpp | 9 ++- components/detournavigator/areatype.hpp | 2 + components/detournavigator/flags.hpp | 5 +- components/detournavigator/makenavmesh.cpp | 47 ++++++++++--- components/detournavigator/navigator.hpp | 10 +++ components/detournavigator/navigatorimpl.cpp | 29 +++++++- components/detournavigator/navigatorimpl.hpp | 7 ++ components/detournavigator/navigatorstub.hpp | 4 ++ components/detournavigator/navmeshmanager.cpp | 20 ++---- components/detournavigator/navmeshmanager.hpp | 4 +- .../detournavigator/offmeshconnection.hpp | 3 + .../offmeshconnectionsmanager.hpp | 47 ++++++------- components/misc/convert.hpp | 7 ++ components/misc/coordinateconverter.hpp | 68 +++++++++++++++++++ 22 files changed, 224 insertions(+), 147 deletions(-) delete mode 100644 apps/openmw/mwmechanics/coordinateconverter.cpp delete mode 100644 apps/openmw/mwmechanics/coordinateconverter.hpp create mode 100644 components/misc/coordinateconverter.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 99bd1c2cd..31ac64e9e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -86,7 +86,7 @@ add_openmw_dir (mwmechanics drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning - character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype spellutil tickableeffects + character actors objects aistate trading weaponpriority spellpriority weapontype spellutil tickableeffects spellabsorption linkedeffects ) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 883a8cc1c..a77539a1f 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -1,6 +1,7 @@ #include "aicombat.hpp" #include +#include #include @@ -21,7 +22,6 @@ #include "movement.hpp" #include "character.hpp" #include "aicombataction.hpp" -#include "coordinateconverter.hpp" #include "actorutil.hpp" namespace @@ -302,7 +302,7 @@ namespace MWMechanics if (pathgrid && !actor.getClass().isPureWaterCreature(actor)) { ESM::Pathgrid::PointList points; - CoordinateConverter coords(storage.mCell->getCell()); + Misc::CoordinateConverter coords(storage.mCell->getCell()); osg::Vec3f localPos = actor.getRefData().getPosition().asVec3(); coords.toLocal(localPos); diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 66b41db54..0c06697dc 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -20,7 +21,6 @@ #include "movement.hpp" #include "steering.hpp" #include "actorutil.hpp" -#include "coordinateconverter.hpp" #include @@ -341,7 +341,7 @@ bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position) if (playerCell->isExterior()) { // get actor's distance from origin of center cell - CoordinateConverter(playerCell).toLocal(position); + Misc::CoordinateConverter(playerCell).toLocal(position); // currently assumes 3 x 3 grid for exterior cells, with player at center cell. // ToDo: (Maybe) use "exterior cell load distance" setting to get count of actual active cells diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index fd978717e..015859c4b 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -21,7 +22,6 @@ #include "pathgrid.hpp" #include "creaturestats.hpp" #include "movement.hpp" -#include "coordinateconverter.hpp" #include "actorutil.hpp" namespace MWMechanics @@ -566,7 +566,7 @@ namespace MWMechanics void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell) { - CoordinateConverter(cell).toWorld(point); + Misc::CoordinateConverter(cell).toWorld(point); } void AiWander::trimAllowedNodes(std::vector& nodes, @@ -767,7 +767,7 @@ namespace MWMechanics { // get NPC's position in local (i.e. cell) coordinates osg::Vec3f npcPos(mInitialActorPosition); - CoordinateConverter(cell).toLocal(npcPos); + Misc::CoordinateConverter(cell).toLocal(npcPos); // Find closest pathgrid point int closestPointIndex = PathFinder::getClosestPoint(pathgrid, npcPos); diff --git a/apps/openmw/mwmechanics/coordinateconverter.cpp b/apps/openmw/mwmechanics/coordinateconverter.cpp deleted file mode 100644 index 04155ea49..000000000 --- a/apps/openmw/mwmechanics/coordinateconverter.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "coordinateconverter.hpp" - -#include -#include - -namespace MWMechanics -{ - CoordinateConverter::CoordinateConverter(const ESM::Cell* cell) - : mCellX(0), mCellY(0) - { - if (cell->isExterior()) - { - mCellX = cell->mData.mX * ESM::Land::REAL_SIZE; - mCellY = cell->mData.mY * ESM::Land::REAL_SIZE; - } - } - - void CoordinateConverter::toWorld(ESM::Pathgrid::Point& point) - { - point.mX += mCellX; - point.mY += mCellY; - } - - void CoordinateConverter::toWorld(osg::Vec3f& point) - { - point.x() += static_cast(mCellX); - point.y() += static_cast(mCellY); - } - - void CoordinateConverter::toLocal(osg::Vec3f& point) - { - point.x() -= static_cast(mCellX); - point.y() -= static_cast(mCellY); - } - - osg::Vec3f CoordinateConverter::toLocalVec3(const osg::Vec3f& point) - { - return osg::Vec3f( - point.x() - static_cast(mCellX), - point.y() - static_cast(mCellY), - point.z() - ); - } -} diff --git a/apps/openmw/mwmechanics/coordinateconverter.hpp b/apps/openmw/mwmechanics/coordinateconverter.hpp deleted file mode 100644 index f7dda33cb..000000000 --- a/apps/openmw/mwmechanics/coordinateconverter.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef GAME_MWMECHANICS_COORDINATECONVERTER_H -#define GAME_MWMECHANICS_COORDINATECONVERTER_H - -#include -#include - -namespace ESM -{ - struct Cell; -} - -namespace MWMechanics -{ - /// \brief convert coordinates between world and local cell - class CoordinateConverter - { - public: - CoordinateConverter(const ESM::Cell* cell); - - /// in-place conversion from local to world - void toWorld(ESM::Pathgrid::Point& point); - - /// in-place conversion from local to world - void toWorld(osg::Vec3f& point); - - /// in-place conversion from world to local - void toLocal(osg::Vec3f& point); - - osg::Vec3f toLocalVec3(const osg::Vec3f& point); - - private: - int mCellX; - int mCellY; - }; -} - -#endif diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index b072f55f8..824100c60 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -17,7 +18,6 @@ #include "../mwworld/class.hpp" #include "pathgrid.hpp" -#include "coordinateconverter.hpp" #include "actorutil.hpp" namespace @@ -164,7 +164,7 @@ namespace MWMechanics } // NOTE: getClosestPoint expects local coordinates - CoordinateConverter converter(mCell->getCell()); + Misc::CoordinateConverter converter(mCell->getCell()); // NOTE: It is possible that getClosestPoint returns a pathgrind point index // that is unreachable in some situations. e.g. actor is standing @@ -331,6 +331,10 @@ namespace MWMechanics if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor)) buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); + if (mPath.empty()) + buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, + flags | DetourNavigator::Flag_usePathgrid, std::back_inserter(mPath)); + if (mPath.empty()) buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath)); diff --git a/apps/openmw/mwrender/pathgrid.cpp b/apps/openmw/mwrender/pathgrid.cpp index 797794457..c20e81bb2 100644 --- a/apps/openmw/mwrender/pathgrid.cpp +++ b/apps/openmw/mwrender/pathgrid.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/pathfinding.hpp" -#include "../mwmechanics/coordinateconverter.hpp" #include "vismask.hpp" @@ -105,7 +105,7 @@ void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store) if (!pathgrid) return; osg::Vec3f cellPathGridPos(0, 0, 0); - MWMechanics::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos); + Misc::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos); osg::ref_ptr cellPathGrid = new osg::PositionAttitudeTransform; cellPathGrid->setPosition(cellPathGridPos); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index a366f9a75..1e1d39bec 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -365,6 +365,9 @@ namespace MWWorld if ((*iter)->getCell()->hasWater()) navigator->removeWater(osg::Vec2i(cellX, cellY)); + if (const auto pathgrid = world->getStore().get().search(*(*iter)->getCell())) + navigator->removePathgrid(*pathgrid); + const auto player = world->getPlayerPtr(); navigator->update(player.getRefData().getPosition().asVec3()); @@ -393,7 +396,8 @@ namespace MWWorld float verts = ESM::Land::LAND_SIZE; float worldsize = ESM::Land::REAL_SIZE; - const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); + const auto world = MWBase::Environment::get().getWorld(); + const auto navigator = world->getNavigator(); const int cellX = cell->getCell()->getGridX(); const int cellY = cell->getCell()->getGridY(); @@ -419,6 +423,9 @@ namespace MWWorld heightField->getCollisionObject()->getWorldTransform()); } + if (const auto pathgrid = world->getStore().get().search(*cell->getCell())) + navigator->addPathgrid(*cell->getCell(), *pathgrid); + // register local scripts // do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell); diff --git a/components/detournavigator/areatype.hpp b/components/detournavigator/areatype.hpp index 0daa524ca..962a67847 100644 --- a/components/detournavigator/areatype.hpp +++ b/components/detournavigator/areatype.hpp @@ -9,6 +9,8 @@ namespace DetourNavigator { AreaType_null = RC_NULL_AREA, AreaType_water, + AreaType_door, + AreaType_pathgrid, AreaType_ground = RC_WALKABLE_AREA, }; } diff --git a/components/detournavigator/flags.hpp b/components/detournavigator/flags.hpp index 684d4fbba..887fd4264 100644 --- a/components/detournavigator/flags.hpp +++ b/components/detournavigator/flags.hpp @@ -13,6 +13,7 @@ namespace DetourNavigator Flag_walk = 1 << 0, Flag_swim = 1 << 1, Flag_openDoor = 1 << 2, + Flag_usePathgrid = 1 << 3, }; inline std::ostream& operator <<(std::ostream& stream, const Flag value) @@ -27,6 +28,8 @@ namespace DetourNavigator return stream << "swim"; case Flag_openDoor: return stream << "openDoor"; + case Flag_usePathgrid: + return stream << "usePathgrid"; } return stream; @@ -45,7 +48,7 @@ namespace DetourNavigator else { bool first = true; - for (const auto flag : {Flag_walk, Flag_swim, Flag_openDoor}) + for (const auto flag : {Flag_walk, Flag_swim, Flag_openDoor, Flag_usePathgrid}) { if (value.mValue & flag) { diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index ae26ced7e..beee95113 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -98,6 +98,42 @@ namespace return result; } + Flag getFlag(AreaType areaType) + { + switch (areaType) + { + case AreaType_null: + return Flag_none; + case AreaType_ground: + return Flag_walk; + case AreaType_water: + return Flag_swim; + case AreaType_door: + return Flag_openDoor; + case AreaType_pathgrid: + return Flag_usePathgrid; + } + return Flag_none; + } + + std::vector getOffMeshConAreas(const std::vector& connections) + { + std::vector result; + result.reserve(connections.size()); + std::transform(connections.begin(), connections.end(), std::back_inserter(result), + [] (const OffMeshConnection& v) { return v.mAreaType; }); + return result; + } + + std::vector getOffMeshFlags(const std::vector& connections) + { + std::vector result; + result.reserve(connections.size()); + std::transform(connections.begin(), connections.end(), std::back_inserter(result), + [] (const OffMeshConnection& v) { return getFlag(v.mAreaType); }); + return result; + } + rcConfig makeConfig(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, const Settings& settings) { @@ -334,12 +370,7 @@ namespace void setPolyMeshFlags(rcPolyMesh& polyMesh) { for (int i = 0; i < polyMesh.npolys; ++i) - { - if (polyMesh.areas[i] == AreaType_ground) - polyMesh.flags[i] = Flag_walk; - else if (polyMesh.areas[i] == AreaType_water) - polyMesh.flags[i] = Flag_swim; - } + polyMesh.flags[i] = getFlag(static_cast(polyMesh.areas[i])); } bool fillPolyMesh(rcContext& context, const rcConfig& config, rcHeightfield& solid, rcPolyMesh& polyMesh, @@ -395,8 +426,8 @@ namespace const auto offMeshConVerts = getOffMeshVerts(offMeshConnections); const std::vector offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents)); const std::vector offMeshConDir(offMeshConnections.size(), DT_OFFMESH_CON_BIDIR); - const std::vector offMeshConAreas(offMeshConnections.size(), AreaType_ground); - const std::vector offMeshConFlags(offMeshConnections.size(), Flag_openDoor); + const std::vector offMeshConAreas = getOffMeshConAreas(offMeshConnections); + const std::vector offMeshConFlags = getOffMeshFlags(offMeshConnections); dtNavMeshCreateParams params; params.verts = polyMesh.verts; diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 99f1e258d..cfdf92232 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -9,6 +9,12 @@ #include "recastmesh.hpp" #include "recastmeshtiles.hpp" +namespace ESM +{ + struct Cell; + struct Pathgrid; +} + namespace DetourNavigator { struct ObjectShapes @@ -139,6 +145,10 @@ namespace DetourNavigator */ virtual bool removeWater(const osg::Vec2i& cellPosition) = 0; + virtual void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) = 0; + + virtual void removePathgrid(const ESM::Pathgrid& pathgrid) = 0; + /** * @brief update start background navmesh update using current scene state. * @param playerPosition setup initial point to order build tiles of navmesh. diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 3ecfd8b51..d7889629e 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -2,6 +2,9 @@ #include "debug.hpp" #include "settingsutils.hpp" +#include +#include + #include namespace DetourNavigator @@ -54,7 +57,8 @@ namespace DetourNavigator mNavMeshManager.addOffMeshConnection( id, toNavMeshCoordinates(mSettings, shapes.mConnectionStart), - toNavMeshCoordinates(mSettings, shapes.mConnectionEnd) + toNavMeshCoordinates(mSettings, shapes.mConnectionEnd), + AreaType_door ); return true; } @@ -95,7 +99,7 @@ namespace DetourNavigator const auto water = mWaterIds.find(id); if (water != mWaterIds.end()) result = mNavMeshManager.removeObject(water->second) || result; - mNavMeshManager.removeOffMeshConnection(id); + mNavMeshManager.removeOffMeshConnections(id); return result; } @@ -111,6 +115,27 @@ namespace DetourNavigator return mNavMeshManager.removeWater(cellPosition); } + void NavigatorImpl::addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) + { + Misc::CoordinateConverter converter(&cell); + for (auto edge : pathgrid.mEdges) + { + const auto src = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV0])); + const auto dst = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV1])); + mNavMeshManager.addOffMeshConnection( + ObjectId(&pathgrid), + toNavMeshCoordinates(mSettings, src), + toNavMeshCoordinates(mSettings, dst), + AreaType_pathgrid + ); + } + } + + void NavigatorImpl::removePathgrid(const ESM::Pathgrid& pathgrid) + { + mNavMeshManager.removeOffMeshConnections(ObjectId(&pathgrid)); + } + void NavigatorImpl::update(const osg::Vec3f& playerPosition) { removeUnusedNavMeshes(); diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index be291f501..66a4d8bb3 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -4,6 +4,8 @@ #include "navigator.hpp" #include "navmeshmanager.hpp" +#include + namespace DetourNavigator { class NavigatorImpl final : public Navigator @@ -38,6 +40,10 @@ namespace DetourNavigator bool removeWater(const osg::Vec2i& cellPosition) override; + void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) final; + + void removePathgrid(const ESM::Pathgrid& pathgrid) final; + void update(const osg::Vec3f& playerPosition) override; void wait() override; @@ -58,6 +64,7 @@ namespace DetourNavigator std::map mAgents; std::unordered_map mAvoidIds; std::unordered_map mWaterIds; + std::multimap mOffMeshConnectionIds; void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId); void updateWaterShapeId(const ObjectId id, const ObjectId waterId); diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index ee23e67be..9c379338f 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -60,6 +60,10 @@ namespace DetourNavigator return false; } + void addPathgrid(const ESM::Cell& /*cell*/, const ESM::Pathgrid& /*pathgrid*/) final {} + + void removePathgrid(const ESM::Pathgrid& /*pathgrid*/) final {} + void update(const osg::Vec3f& /*playerPosition*/) override {} void wait() override {} diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index d88d9706a..43d330648 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -110,10 +110,9 @@ namespace DetourNavigator return true; } - void NavMeshManager::addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end) + void NavMeshManager::addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end, const AreaType areaType) { - if (!mOffMeshConnectionsManager.add(id, OffMeshConnection {start, end})) - return; + mOffMeshConnectionsManager.add(id, OffMeshConnection {start, end, areaType}); const auto startTilePosition = getTilePosition(mSettings, start); const auto endTilePosition = getTilePosition(mSettings, end); @@ -124,18 +123,11 @@ namespace DetourNavigator addChangedTile(endTilePosition, ChangeType::add); } - void NavMeshManager::removeOffMeshConnection(const ObjectId id) + void NavMeshManager::removeOffMeshConnections(const ObjectId id) { - if (const auto connection = mOffMeshConnectionsManager.remove(id)) - { - const auto startTilePosition = getTilePosition(mSettings, connection->mStart); - const auto endTilePosition = getTilePosition(mSettings, connection->mEnd); - - addChangedTile(startTilePosition, ChangeType::remove); - - if (startTilePosition != endTilePosition) - addChangedTile(endTilePosition, ChangeType::remove); - } + const auto changedTiles = mOffMeshConnectionsManager.remove(id); + for (const auto& tile : changedTiles) + addChangedTile(tile, ChangeType::update); } void NavMeshManager::update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents) diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index a6bdca09b..f3861f8f2 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -39,9 +39,9 @@ namespace DetourNavigator bool reset(const osg::Vec3f& agentHalfExtents); - void addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end); + void addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end, const AreaType areaType); - void removeOffMeshConnection(const ObjectId id); + void removeOffMeshConnections(const ObjectId id); void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents); diff --git a/components/detournavigator/offmeshconnection.hpp b/components/detournavigator/offmeshconnection.hpp index 60e8ecbbb..ca999dbdb 100644 --- a/components/detournavigator/offmeshconnection.hpp +++ b/components/detournavigator/offmeshconnection.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_OFFMESHCONNECTION_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_OFFMESHCONNECTION_H +#include "areatype.hpp" + #include namespace DetourNavigator @@ -9,6 +11,7 @@ namespace DetourNavigator { osg::Vec3f mStart; osg::Vec3f mEnd; + AreaType mAreaType; }; } diff --git a/components/detournavigator/offmeshconnectionsmanager.hpp b/components/detournavigator/offmeshconnectionsmanager.hpp index 155ce3296..de707f3a8 100644 --- a/components/detournavigator/offmeshconnectionsmanager.hpp +++ b/components/detournavigator/offmeshconnectionsmanager.hpp @@ -11,14 +11,11 @@ #include -#include - #include #include -#include -#include #include #include +#include namespace DetourNavigator { @@ -29,12 +26,11 @@ namespace DetourNavigator : mSettings(settings) {} - bool add(const ObjectId id, const OffMeshConnection& value) + void add(const ObjectId id, const OffMeshConnection& value) { const auto values = mValues.lock(); - if (!values->mById.insert(std::make_pair(id, value)).second) - return false; + values->mById.insert(std::make_pair(id, value)); const auto startTilePosition = getTilePosition(mSettings, value.mStart); const auto endTilePosition = getTilePosition(mSettings, value.mEnd); @@ -43,32 +39,32 @@ namespace DetourNavigator if (startTilePosition != endTilePosition) values->mByTilePosition[endTilePosition].insert(id); - - return true; } - boost::optional remove(const ObjectId id) + std::set remove(const ObjectId id) { const auto values = mValues.lock(); - const auto itById = values->mById.find(id); + const auto byId = values->mById.equal_range(id); - if (itById == values->mById.end()) - return boost::none; + if (byId.first == byId.second) { + return {}; + } - const auto result = itById->second; + std::set removed; - values->mById.erase(itById); + std::for_each(byId.first, byId.second, [&] (const auto& v) { + const auto startTilePosition = getTilePosition(mSettings, v.second.mStart); + const auto endTilePosition = getTilePosition(mSettings, v.second.mEnd); - const auto startTilePosition = getTilePosition(mSettings, result.mStart); - const auto endTilePosition = getTilePosition(mSettings, result.mEnd); + removed.emplace(startTilePosition); + if (startTilePosition != endTilePosition) + removed.emplace(endTilePosition); + }); - removeByTilePosition(values->mByTilePosition, startTilePosition, id); + values->mById.erase(byId.first, byId.second); - if (startTilePosition != endTilePosition) - removeByTilePosition(values->mByTilePosition, endTilePosition, id); - - return result; + return removed; } std::vector get(const TilePosition& tilePosition) @@ -85,9 +81,8 @@ namespace DetourNavigator std::for_each(itByTilePosition->second.begin(), itByTilePosition->second.end(), [&] (const ObjectId v) { - const auto itById = values->mById.find(v); - if (itById != values->mById.end()) - result.push_back(itById->second); + const auto byId = values->mById.equal_range(v); + std::for_each(byId.first, byId.second, [&] (const auto& v) { result.push_back(v.second); }); }); return result; @@ -96,7 +91,7 @@ namespace DetourNavigator private: struct Values { - std::unordered_map mById; + std::multimap mById; std::map> mByTilePosition; }; diff --git a/components/misc/convert.hpp b/components/misc/convert.hpp index c5671f016..c5784d33a 100644 --- a/components/misc/convert.hpp +++ b/components/misc/convert.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_COMPONENTS_MISC_CONVERT_H #define OPENMW_COMPONENTS_MISC_CONVERT_H +#include + #include #include #include @@ -21,6 +23,11 @@ namespace Convert return osg::Vec3f(value.x(), value.y(), value.z()); } + inline osg::Vec3f makeOsgVec3f(const ESM::Pathgrid::Point& value) + { + return osg::Vec3f(value.mX, value.mY, value.mZ); + } + inline btVector3 toBullet(const osg::Vec3f& vec) { return btVector3(vec.x(), vec.y(), vec.z()); diff --git a/components/misc/coordinateconverter.hpp b/components/misc/coordinateconverter.hpp new file mode 100644 index 000000000..190641415 --- /dev/null +++ b/components/misc/coordinateconverter.hpp @@ -0,0 +1,68 @@ +#ifndef OPENMW_COMPONENTS_MISC_COORDINATECONVERTER_H +#define OPENMW_COMPONENTS_MISC_COORDINATECONVERTER_H + +#include +#include +#include +#include + +namespace Misc +{ + /// \brief convert coordinates between world and local cell + class CoordinateConverter + { + public: + CoordinateConverter(bool exterior, int cellX, int cellY) + : mCellX(exterior ? cellX * ESM::Land::REAL_SIZE : 0), + mCellY(exterior ? cellY * ESM::Land::REAL_SIZE : 0) + { + } + + explicit CoordinateConverter(const ESM::Cell* cell) + : CoordinateConverter(cell->isExterior(), cell->mData.mX, cell->mData.mY) + { + } + + /// in-place conversion from local to world + void toWorld(ESM::Pathgrid::Point& point) const + { + point.mX += mCellX; + point.mY += mCellY; + } + + ESM::Pathgrid::Point toWorldPoint(ESM::Pathgrid::Point point) const + { + toWorld(point); + return point; + } + + /// in-place conversion from local to world + void toWorld(osg::Vec3f& point) const + { + point.x() += static_cast(mCellX); + point.y() += static_cast(mCellY); + } + + /// in-place conversion from world to local + void toLocal(osg::Vec3f& point) const + { + point.x() -= static_cast(mCellX); + point.y() -= static_cast(mCellY); + } + + osg::Vec3f toLocalVec3(const osg::Vec3f& point) const + { + return osg::Vec3f( + point.x() - static_cast(mCellX), + point.y() - static_cast(mCellY), + point.z() + ); + } + + private: + int mCellX; + int mCellY; + }; +} + +#endif From d863267d5cb5f4062937f86c37af3b0c8f9479cf Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 12 Jun 2020 21:04:11 +0200 Subject: [PATCH 12/81] Do not fallback to direct path without pathgrid Assume this might happen only due buildPath call when navmesh can't provide path. --- apps/openmw/mwmechanics/pathfinding.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 824100c60..d00f2615e 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -158,10 +158,7 @@ namespace MWMechanics // Maybe there is no pathgrid for this cell. Just go to destination and let // physics take care of any blockages. if(!pathgrid || pathgrid->mPoints.empty()) - { - *out++ = endPoint; return; - } // NOTE: getClosestPoint expects local coordinates Misc::CoordinateConverter converter(mCell->getCell()); @@ -179,6 +176,9 @@ namespace MWMechanics endPointInLocalCoords, startNode); + if (!endNode.second) + return; + // if it's shorter for actor to travel from start to end, than to travel from either // start or end to nearest pathgrid point, just travel from start to end. float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2(); @@ -249,8 +249,7 @@ namespace MWMechanics // unreachable pathgrid point. // // The AI routines will have to deal with such situations. - if(endNode.second) - *out++ = endPoint; + *out++ = endPoint; } float PathFinder::getZAngleToNext(float x, float y) const @@ -328,16 +327,21 @@ namespace MWMechanics mPath.clear(); mCell = cell; - if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor)) - buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); + bool hasNavMesh = false; - if (mPath.empty()) + if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor)) + hasNavMesh = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); + + if (hasNavMesh && mPath.empty()) buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags | DetourNavigator::Flag_usePathgrid, std::back_inserter(mPath)); if (mPath.empty()) buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath)); + if (!hasNavMesh && mPath.empty()) + mPath.push_back(endPoint); + mConstructed = true; } From d684f1a78f165ad51643cceef49626da2810709f Mon Sep 17 00:00:00 2001 From: bzzt Date: Thu, 13 Jun 2019 13:37:00 +0000 Subject: [PATCH 13/81] terrainbased objectpaging Signed-off-by: Bret Curtis --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/objectpaging.cpp | 302 ++++++++++++++++++++++ apps/openmw/mwrender/objectpaging.hpp | 44 ++++ apps/openmw/mwrender/renderingmanager.cpp | 13 + apps/openmw/mwrender/renderingmanager.hpp | 4 + apps/openmw/mwworld/cellpreloader.cpp | 8 +- apps/openmw/mwworld/cellpreloader.hpp | 6 +- apps/openmw/mwworld/esmstore.cpp | 4 + apps/openmw/mwworld/esmstore.hpp | 10 + apps/openmw/mwworld/scene.cpp | 80 +++--- apps/openmw/mwworld/scene.hpp | 13 +- apps/openmw/mwworld/store.cpp | 25 ++ apps/openmw/mwworld/store.hpp | 2 + components/resource/scenemanager.cpp | 11 +- components/resource/scenemanager.hpp | 2 +- components/sceneutil/optimizer.cpp | 92 ++++--- components/sceneutil/optimizer.hpp | 27 +- components/sceneutil/riggeometry.cpp | 2 +- components/sceneutil/riggeometry.hpp | 2 +- components/terrain/chunkmanager.cpp | 14 +- components/terrain/chunkmanager.hpp | 12 +- components/terrain/quadtreeworld.cpp | 53 +++- components/terrain/quadtreeworld.hpp | 13 +- components/terrain/terraingrid.cpp | 14 +- components/terrain/viewdata.cpp | 12 +- components/terrain/viewdata.hpp | 6 +- components/terrain/world.cpp | 2 +- components/terrain/world.hpp | 6 +- files/settings-default.cfg | 9 + 29 files changed, 648 insertions(+), 142 deletions(-) create mode 100644 apps/openmw/mwrender/objectpaging.cpp create mode 100644 apps/openmw/mwrender/objectpaging.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 31ac64e9e..a9f3d24e2 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -21,7 +21,7 @@ add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation - renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager + renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp new file mode 100644 index 000000000..6453889bc --- /dev/null +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -0,0 +1,302 @@ +#include "objectpaging.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "apps/openmw/mwworld/esmstore.hpp" +#include "apps/openmw/mwbase/environment.hpp" +#include "apps/openmw/mwbase/world.hpp" + +#include "vismask.hpp" + +namespace MWRender +{ + + bool typeFilter(int type, bool far) + { + switch (type) + { + case ESM::REC_STAT: + case ESM::REC_ACTI: + case ESM::REC_DOOR: + return true; + case ESM::REC_CONT: + return far ? false : true; + default: + return false; + } + } + + const std::string& getModel(int type, const std::string& id, const MWWorld::ESMStore& store) + { + switch (type) + { + case ESM::REC_STAT: + return store.get().searchStatic(id)->mModel; + case ESM::REC_ACTI: + return store.get().searchStatic(id)->mModel; + case ESM::REC_DOOR: + return store.get().searchStatic(id)->mModel; + case ESM::REC_CONT: + return store.get().searchStatic(id)->mModel; + default: throw std::exception(); + } + } + + osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) + { + if (!far)return nullptr; + ChunkId id = std::make_tuple(center, size); + + osg::ref_ptr obj = mCache->getRefFromObjectCache(id); + if (obj) + return obj->asNode(); + else + { + osg::ref_ptr node = createChunk(size, center, viewPoint); + mCache->addEntryToObjectCache(id, node.get()); + return node; + } + } + + class CanOptimizeCallback : public SceneUtil::Optimizer::IsOperationPermissibleForObjectCallback + { + public: + virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Drawable* node,unsigned int option) const + { + return true; + } + virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Node* node,unsigned int option) const + { + return true; + } + }; + + class CopyOp : public osg::CopyOp + { + public: + CopyOp() : mDistance(0.f) { + setCopyFlags(osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES + #if OSG_MIN_VERSION_REQUIRED(3,5,6) + |osg::CopyOp::DEEP_COPY_ARRAYS|osg::CopyOp::DEEP_COPY_PRIMITIVES // damned vbogarbage racing + #endif + ); + } + + float mDistance; + + virtual osg::Node* operator() (const osg::Node* node) const + { + if (dynamic_cast(node)) + return nullptr; + if (dynamic_cast(node)) + return nullptr; + + if (const osg::Switch* sw = node->asSwitch()) + { + osg::Group* n = new osg::Group; + for (unsigned int i=0; igetNumChildren(); ++i) + if (sw->getValue(i)) + n->addChild(operator()(sw->getChild(i))); + n->setDataVariance(osg::Object::STATIC); + return n; + } + if (const osg::LOD* lod = dynamic_cast(node)) + { + osg::Group* n = new osg::Group; + for (unsigned int i=0; igetNumChildren(); ++i) + if (lod->getMinRange(i) <= mDistance && mDistance < lod->getMaxRange(i)) + n->addChild(operator()(lod->getChild(i))); + n->setDataVariance(osg::Object::STATIC); + return n; + } + + osg::Node* n = osg::CopyOp::operator()(node); + if (n) { + n->setDataVariance(osg::Object::STATIC); + n->setUserDataContainer(nullptr); + n->setName(""); + } + return n; + } + virtual osg::Drawable* operator() (const osg::Drawable* drawable) const + { + if (dynamic_cast(drawable)) + return nullptr; + + if (const SceneUtil::RigGeometry* rig = dynamic_cast(drawable)) + return osg::CopyOp::operator()(rig->getSourceGeometry()); + if (const SceneUtil::MorphGeometry* morph = dynamic_cast(drawable)) + return osg::CopyOp::operator()(morph->getSourceGeometry()); + + return osg::CopyOp::operator()(drawable); + } + virtual osg::Callback* operator() (const osg::Callback* callback) const + { + return nullptr; + } + }; + + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) + : GenericResourceManager(nullptr) + , mSceneManager(sceneManager) + { + mMergeGeometry = Settings::Manager::getBool("object paging merge geometry", "Terrain"); + mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); + } + + osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint) + { + osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f)); + + osg::ref_ptr group = new osg::Group; + + osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE; + osg::Vec3f relativeViewPoint = viewPoint - worldCenter; + + std::vector refs; + std::vector esm; + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + + for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX) + { + for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY) + { + const ESM::Cell* cell = store.get().searchStatic(cellX, cellY); + if (!cell) continue; + for (size_t i=0; imContextList.size(); ++i) + { + try + { + unsigned int index = cell->mContextList.at(i).index; + if (esm.size()<=index) + esm.resize(index+1); + cell->restore(esm[index], i); + ESM::CellRef ref; + ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile; + bool deleted = false; + while(cell->getNextRef(esm[index], ref, deleted)) + { + if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; + int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + if (!typeFilter(type,size>=2)) continue; + if (deleted) continue; + refs.push_back(ref); + } + } + catch (std::exception& e) + { + continue; + } + } + for (ESM::CellRefTracker::const_iterator it = cell->mLeasedRefs.begin(); it != cell->mLeasedRefs.end(); ++it) + { + ESM::CellRef ref = it->first; + bool deleted = it->second; + if (deleted) continue; + int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + if (!typeFilter(type,size>=2)) continue; + refs.push_back(ref); + } + } + } + + osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); + osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); + for (const ESM::CellRef& ref : refs) + { + std::string id = Misc::StringUtils::lowerCase(ref.mRefID); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player + + int type = store.findStatic(id); + std::string model = "meshes/" + getModel(type, id, store); +/* + bool useAnim = type != ESM::REC_STAT; + if (useAnim) + model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); +*/ + if (model.empty()) continue; + + osg::Vec3f pos = ref.mPos.asVec3(); + if (size < 1.f) + { + osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE; + cellPos.x() = std::max(cellPos.x(), std::floor(minBound.x())); + cellPos.x() = std::min(cellPos.x(), std::ceil(maxBound.x())); + cellPos.y() = std::max(cellPos.y(), std::floor(minBound.y())); + cellPos.y() = std::min(cellPos.y(), std::ceil(maxBound.y())); + if (cellPos.x() < minBound.x() || cellPos.x() > maxBound.x() || cellPos.y() < minBound.y() || cellPos.y() > maxBound.y()) + continue; + } + + osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); + + float d = (viewPoint - pos).length(); + + if (cnode->getBound().radius() * ref.mScale < d*mMinSize) + continue; + + CopyOp co = CopyOp(); + co.mDistance = d; + osg::ref_ptr node = osg::clone(cnode.get(), co); + node->setUserDataContainer(nullptr); + + osg::Matrixf matrix; + matrix.preMultTranslate(pos - worldCenter); + matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * + osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * + osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); + matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); + osg::ref_ptr trans = new osg::MatrixTransform(matrix); + trans->addChild(node); + trans->setDataVariance(osg::Object::STATIC); + + group->addChild(trans); + } + + if (mMergeGeometry) + { + SceneUtil::Optimizer optimizer; + if ((relativeViewPoint - group->getBound().center()).length2() > group->getBound().radius2()) + { + optimizer.setViewPoint(relativeViewPoint); + optimizer.setMergeAlphaBlending(true); + } + optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback); + unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY; + optimizer.optimize(group, options); + } + + auto ico = mSceneManager->getIncrementalCompileOperation(); + if (ico) ico->add(group); + else group->getBound(); + + group->setNodeMask(Mask_Static); + + return group; + } + + unsigned int ObjectPaging::getNodeMask() + { + return Mask_Static; + } + +} diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp new file mode 100644 index 000000000..6d66d078d --- /dev/null +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -0,0 +1,44 @@ +#ifndef OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H +#define OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H + +#include +#include +#include + +namespace Resource +{ + class SceneManager; +} +namespace MWWorld +{ + class ESMStore; +} + +namespace MWRender +{ + + typedef std::tuple ChunkId; // Center, Size + + class ObjectPaging : public Resource::GenericResourceManager, public Terrain::QuadTreeWorld::ChunkManager + { + public: + ObjectPaging(Resource::SceneManager* sceneManager); + ~ObjectPaging() = default; + + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) override; + + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint); + + virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } + + virtual unsigned int getNodeMask() override; + + private: + Resource::SceneManager* mSceneManager; + bool mMergeGeometry; + float mMinSize; + }; + +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 462f9fbb6..9c92da767 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -70,6 +70,8 @@ #include "actorspaths.hpp" #include "recastmesh.hpp" #include "fogmanager.hpp" +#include "objectpaging.hpp" + namespace MWRender { @@ -286,6 +288,12 @@ namespace MWRender mTerrain.reset(new Terrain::QuadTreeWorld( sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug, compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize)); + if (Settings::Manager::getBool("object paging", "Terrain")) + { + mObjectPaging.reset(new ObjectPaging(mResourceSystem->getSceneManager())); + static_cast(mTerrain.get())->addChunkManager(mObjectPaging.get()); + mResourceSystem->addResourceManager(mObjectPaging.get()); + } } else mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug)); @@ -1467,4 +1475,9 @@ namespace MWRender mRecastMesh->update(mNavigator.getRecastMeshTiles(), mNavigator.getSettings()); } + + void RenderingManager::setActiveGrid(const osg::Vec4i &grid) + { + mTerrain->setActiveGrid(grid); + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 13f09b359..dff76f95e 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -84,6 +84,7 @@ namespace MWRender class NavMesh; class ActorsPaths; class RecastMesh; + class ObjectPaging; class RenderingManager : public MWRender::RenderingInterface { @@ -237,6 +238,8 @@ namespace MWRender void setNavMeshNumber(const std::size_t value); + void setActiveGrid(const osg::Vec4i &grid); + private: void updateProjectionMatrix(); void updateTextureFiltering(); @@ -275,6 +278,7 @@ namespace MWRender std::unique_ptr mWater; std::unique_ptr mTerrain; TerrainStorage* mTerrainStorage; + std::unique_ptr mObjectPaging; std::unique_ptr mSky; std::unique_ptr mFog; std::unique_ptr mEffectManager; diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 9a96e9806..60b9a3220 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -172,7 +172,7 @@ namespace MWWorld class TerrainPreloadItem : public SceneUtil::WorkItem { public: - TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) + TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) : mAbort(false) , mTerrainViews(views) , mWorld(world) @@ -191,7 +191,7 @@ namespace MWWorld for (unsigned int i=0; ireset(); - mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort); + mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort); } } @@ -204,7 +204,7 @@ namespace MWWorld std::atomic mAbort; std::vector > mTerrainViews; Terrain::World* mWorld; - std::vector mPreloadPositions; + std::vector mPreloadPositions; }; /// Worker thread item: update the resource system's cache, effectively deleting unused entries. @@ -415,7 +415,7 @@ namespace MWWorld mUnrefQueue = unrefQueue; } - void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) + void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) return; diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 0501a4f9b..ef2817019 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include namespace Resource @@ -68,7 +69,8 @@ namespace MWWorld void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue); - void setTerrainPreloadPositions(const std::vector& positions); + typedef std::pair PositionCellGrid; + void setTerrainPreloadPositions(const std::vector& positions); private: Resource::ResourceSystem* mResourceSystem; @@ -105,7 +107,7 @@ namespace MWWorld PreloadMap mPreloadCells; std::vector > mTerrainViews; - std::vector mTerrainPreloadPositions; + std::vector mTerrainPreloadPositions; osg::ref_ptr mTerrainPreloadItem; osg::ref_ptr mUpdateCacheItem; }; diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index f86226640..278b8532e 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -136,6 +136,10 @@ void ESMStore::setUp(bool validateRecords) mIds[*record] = storeIt->first; } } + + if (mStaticIds.empty()) + mStaticIds = mIds; + mSkills.setUp(); mMagicEffects.setUp(); mAttributes.setUp(); diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index fe1cfc708..24364bfb1 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -68,6 +68,8 @@ namespace MWWorld // Lookup of all IDs. Makes looking up references faster. Just // maps the id name to the record type. std::map mIds; + std::map mStaticIds; + std::map mStores; ESM::NPC mPlayerTemplate; @@ -99,6 +101,14 @@ namespace MWWorld } return it->second; } + int findStatic(const std::string &id) const + { + std::map::const_iterator it = mStaticIds.find(id); + if (it == mStaticIds.end()) { + return 0; + } + return it->second; + } ESMStore() : mDynamicCount(0) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 1e1d39bec..127b56c94 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -286,28 +286,6 @@ namespace MWWorld ::updateObjectScale(ptr, *mPhysics, mRendering); } - void Scene::getGridCenter(int &cellX, int &cellY) - { - int maxX = std::numeric_limits::min(); - int maxY = std::numeric_limits::min(); - int minX = std::numeric_limits::max(); - int minY = std::numeric_limits::max(); - CellStoreCollection::iterator iter = mActiveCells.begin(); - while (iter!=mActiveCells.end()) - { - assert ((*iter)->getCell()->isExterior()); - int x = (*iter)->getCell()->getGridX(); - int y = (*iter)->getCell()->getGridY(); - maxX = std::max(x, maxX); - maxY = std::max(y, maxY); - minX = std::min(x, minX); - minY = std::min(y, minY); - ++iter; - } - cellX = (minX + maxX) / 2; - cellY = (minY + maxY) / 2; - } - void Scene::update (float duration, bool paused) { mPreloadTimer += duration; @@ -488,6 +466,27 @@ namespace MWWorld mPreloader->clear(); } + osg::Vec4i Scene::gridCenterToBounds(const osg::Vec2i& centerCell) const + { + return osg::Vec4i(centerCell.x()-mHalfGridSize,centerCell.y()-mHalfGridSize,centerCell.x()+mHalfGridSize+1,centerCell.y()+mHalfGridSize+1); + } + + osg::Vec2i Scene::getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i* currentGridCenter) const + { + if (currentGridCenter) + { + float centerX, centerY; + MWBase::Environment::get().getWorld()->indexToPosition(currentGridCenter->x(), currentGridCenter->y(), centerX, centerY, true); + float distance = std::max(std::abs(centerY-pos.x()), std::abs(centerY-pos.y())); + const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold + if (distance <= maxDistance) + return *currentGridCenter; + } + osg::Vec2i newCenter; + MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newCenter.x(), newCenter.y()); + return newCenter; + } + void Scene::playerMoved(const osg::Vec3f &pos) { const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); @@ -497,19 +496,9 @@ namespace MWWorld if (!mCurrentCell || !mCurrentCell->isExterior()) return; - // figure out the center of the current cell grid (*not* necessarily mCurrentCell, which is the cell the player is in) - int cellX, cellY; - getGridCenter(cellX, cellY); - float centerX, centerY; - MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); - const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold - float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); - if (distance > maxDistance) - { - int newX, newY; - MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newX, newY); - changeCellGrid(newX, newY); - } + osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter); + if (newCell != mCurrentGridCenter) + changeCellGrid(newCell.x(), newCell.y()); } void Scene::changeCellGrid (int playerCellX, int playerCellY, bool changeEvent) @@ -612,6 +601,9 @@ namespace MWWorld CellStore* current = MWBase::Environment::get().getWorld()->getExterior(playerCellX, playerCellY); MWBase::Environment::get().getWindowManager()->changeCell(current); + mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); + mRendering.setActiveGrid(gridCenterToBounds(mCurrentGridCenter)); + if (changeEvent) mCellChanged = true; } @@ -983,7 +975,7 @@ namespace MWWorld void Scene::preloadCells(float dt) { - std::vector exteriorPositions; + std::vector exteriorPositions; const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); @@ -991,7 +983,7 @@ namespace MWWorld osg::Vec3f predictedPos = playerPos + moved / dt * mPredictionTime; if (mCurrentCell->isExterior()) - exteriorPositions.push_back(predictedPos); + exteriorPositions.emplace_back(predictedPos, gridCenterToBounds(getNewGridCenter(predictedPos, &mCurrentGridCenter))); mLastPlayerPos = playerPos; @@ -1008,7 +1000,7 @@ namespace MWWorld mPreloader->setTerrainPreloadPositions(exteriorPositions); } - void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions) + void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions) { std::vector teleportDoors; for (const MWWorld::CellStore* cellStore : mActiveCells) @@ -1042,7 +1034,7 @@ namespace MWWorld int x,y; MWBase::Environment::get().getWorld()->positionToIndex (pos.x(), pos.y(), x, y); preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); - exteriorPositions.push_back(pos); + exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); } } catch (std::exception& e) @@ -1062,7 +1054,7 @@ namespace MWWorld int cellX,cellY; - getGridCenter(cellX,cellY); + cellX = mCurrentGridCenter.x(); cellY = mCurrentGridCenter.y(); float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); @@ -1110,8 +1102,8 @@ namespace MWWorld void Scene::preloadTerrain(const osg::Vec3f &pos) { - std::vector vec; - vec.push_back(pos); + std::vector vec; + vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); mPreloader->setTerrainPreloadPositions(vec); } @@ -1145,7 +1137,7 @@ namespace MWWorld std::vector mList; }; - void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/, std::vector& exteriorPositions) // ignore predictedPos here since opening dialogue with travel service takes extra time + void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/, std::vector& exteriorPositions) // ignore predictedPos here since opening dialogue with travel service takes extra time { const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3()); @@ -1166,7 +1158,7 @@ namespace MWWorld int x,y; MWBase::Environment::get().getWorld()->positionToIndex( pos.x(), pos.y(), x, y); preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); - exteriorPositions.push_back(pos); + exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); } } } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index da795f84b..9b8403c38 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -1,6 +1,9 @@ #ifndef GAME_MWWORLD_SCENE_H #define GAME_MWWORLD_SCENE_H +#include +#include + #include "ptr.hpp" #include "globals.hpp" @@ -86,16 +89,20 @@ namespace MWWorld osg::Vec3f mLastPlayerPos; void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false); + osg::Vec2i mCurrentGridCenter; // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center void changeCellGrid (int playerCellX, int playerCellY, bool changeEvent = true); - void getGridCenter(int& cellX, int& cellY); + typedef std::pair PositionCellGrid; void preloadCells(float dt); - void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); - void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + + osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; + osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; public: diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index a414585ef..93f11d4fd 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -151,6 +151,19 @@ namespace MWWorld return 0; } + template + const T *Store::searchStatic(const std::string &id) const + { + std::string idLower = Misc::StringUtils::lowerCase(id); + typename std::map::const_iterator it = mStatic.find(idLower); + + if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + return &(it->second); + } + + return 0; + } + template bool Store::isDynamic(const std::string &id) const { @@ -582,6 +595,18 @@ namespace MWWorld return 0; } + const ESM::Cell *Store::searchStatic(int x, int y) const + { + ESM::Cell cell; + cell.mData.mX = x, cell.mData.mY = y; + + std::pair key(x, y); + DynamicExt::const_iterator it = mExt.find(key); + if (it != mExt.end()) { + return &(it->second); + } + return 0; + } const ESM::Cell *Store::searchOrCreate(int x, int y) { std::pair key(x, y); diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 64c01589e..f119fa928 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -167,6 +167,7 @@ namespace MWWorld void setUp(); const T *search(const std::string &id) const; + const T *searchStatic(const std::string &id) const; /** * Does the record with this ID come from the dynamic store? @@ -297,6 +298,7 @@ namespace MWWorld const ESM::Cell *search(const std::string &id) const; const ESM::Cell *search(int x, int y) const; + const ESM::Cell *searchStatic(int x, int y) const; const ESM::Cell *searchOrCreate(int x, int y); const ESM::Cell *find(const std::string &id) const; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 61a40ee4b..1709a3d14 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -466,7 +466,7 @@ namespace Resource return options; } - osg::ref_ptr SceneManager::getTemplate(const std::string &name) + osg::ref_ptr SceneManager::getTemplate(const std::string &name, bool compile) { std::string normalized = name; mVFS->normalizeFilename(normalized); @@ -529,7 +529,7 @@ namespace Resource optimizer.optimize(loaded, options); } - if (mIncrementalCompileOperation) + if (compile && mIncrementalCompileOperation) mIncrementalCompileOperation->add(loaded); else loaded->getBound(); @@ -713,6 +713,13 @@ namespace Resource mSharedStateMutex.lock(); mSharedStateManager->prune(); mSharedStateMutex.unlock(); + + if (mIncrementalCompileOperation) + { + OpenThreads::ScopedLock lock(*mIncrementalCompileOperation->getToCompiledMutex()); + while (mIncrementalCompileOperation->getToCompile().size() > 1000) + mIncrementalCompileOperation->getToCompile().pop_front(); + } } void SceneManager::clearCache() diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 1c1c60a58..bbe88c9a0 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -81,7 +81,7 @@ namespace Resource /// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead. /// If even the error marker mesh can not be found, an exception is thrown. /// @note Thread safe. - osg::ref_ptr getTemplate(const std::string& name); + osg::ref_ptr getTemplate(const std::string& name, bool compile=true); /// Create an instance of the given scene template and cache it for later use, so that future calls to getInstance() can simply /// return this cached object instead of creating a new one. diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 487126627..7bcccdbe3 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -103,7 +103,9 @@ void Optimizer::optimize(osg::Node* node, unsigned int options) osg::Timer_t startTick = osg::Timer::instance()->tick(); MergeGeometryVisitor mgv(this); - mgv.setTargetMaximumNumberOfVertices(10000); + mgv.setTargetMaximumNumberOfVertices(1000000); + mgv.setMergeAlphaBlending(_mergeAlphaBlending); + mgv.setViewPoint(_viewPoint); node->accept(mgv); osg::Timer_t endTick = osg::Timer::instance()->tick(); @@ -988,6 +990,17 @@ struct LessGeometry } }; +struct LessGeometryViewPoint +{ + osg::Vec3f _viewPoint; + bool operator() (const osg::ref_ptr& lhs,const osg::ref_ptr& rhs) const + { + float len1 = (lhs->getBoundingBox().center() - _viewPoint).length2(); + float len2 = (rhs->getBoundingBox().center() - _viewPoint).length2(); + return len2 < len1; + } +}; + struct LessGeometryPrimitiveType { bool operator() (const osg::ref_ptr& lhs,const osg::ref_ptr& rhs) const @@ -1055,16 +1068,16 @@ bool isAbleToMerge(const osg::Geometry& g1, const osg::Geometry& g2) void Optimizer::MergeGeometryVisitor::pushStateSet(osg::StateSet *stateSet) { _stateSetStack.push_back(stateSet); - checkAllowedToMerge(); + checkAlphaBlendingActive(); } void Optimizer::MergeGeometryVisitor::popStateSet() { _stateSetStack.pop_back(); - checkAllowedToMerge(); + checkAlphaBlendingActive(); } -void Optimizer::MergeGeometryVisitor::checkAllowedToMerge() +void Optimizer::MergeGeometryVisitor::checkAlphaBlendingActive() { int renderingHint = 0; bool override = false; @@ -1080,7 +1093,7 @@ void Optimizer::MergeGeometryVisitor::checkAllowedToMerge() override = true; } // Can't merge Geometry that are using a transparent sorting bin as that would cause the sorting to break. - _allowedToMerge = renderingHint != osg::StateSet::TRANSPARENT_BIN; + _alphaBlendingActive = renderingHint == osg::StateSet::TRANSPARENT_BIN; } void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) @@ -1088,7 +1101,7 @@ void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) if (group.getStateSet()) pushStateSet(group.getStateSet()); - if (_allowedToMerge) + if (!_alphaBlendingActive || _mergeAlphaBlending) mergeGroup(group); traverse(group); @@ -1097,6 +1110,14 @@ void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) popStateSet(); } +osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps) +{ + if (ps->referenceCount() <= 1) + return ps; + ps = dynamic_cast(ps->clone(osg::CopyOp::DEEP_COPY_ALL)); + return ps; +} + bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (!isOperationPermissibleForObject(&group)) return false; @@ -1120,7 +1141,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) osg::Geometry* geom = child->asGeometry(); if (geom) { - if (!geometryContainsSharedArrays(*geom) && + if ( geom->getDataVariance()!=osg::Object::DYNAMIC && isOperationPermissibleForObject(geom)) { @@ -1254,6 +1275,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) DuplicateList& duplicateList = *mitr; if (!duplicateList.empty()) { + if (_alphaBlendingActive) + { + LessGeometryViewPoint lgvp; + lgvp._viewPoint = _viewPoint; + std::sort(duplicateList.begin(), duplicateList.end(), lgvp); + } DuplicateList::iterator ditr = duplicateList.begin(); osg::ref_ptr lhs = *ditr++; group.addChild(lhs.get()); @@ -1290,10 +1317,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (prim->getNumIndices()==3) { + prim = clonePrimitive(prim); (*itr) = prim; prim->setMode(osg::PrimitiveSet::TRIANGLES); } else if (prim->getNumIndices()==4) { + prim = clonePrimitive(prim); (*itr) = prim; prim->setMode(osg::PrimitiveSet::QUADS); } } @@ -1320,6 +1349,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) #if 1 bool doneCombine = false; + std::set toremove; + osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList(); unsigned int lhsNo=0; unsigned int rhsNo=1; @@ -1348,6 +1379,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (combine) { + lhs = clonePrimitive(lhs); + primitives[lhsNo] = lhs; switch(lhs->getType()) { @@ -1375,7 +1408,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (combine) { // make this primitive set as invalid and needing cleaning up. - rhs->setMode(0xffffff); + toremove.insert(rhs); doneCombine = true; ++rhsNo; } @@ -1390,7 +1423,6 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (doneCombine) { // now need to clean up primitiveset so it no longer contains the rhs combined primitives. - // first swap with a empty primitiveSet to empty it completely. osg::Geometry::PrimitiveSetList oldPrimitives; primitives.swap(oldPrimitives); @@ -1400,7 +1432,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) pitr != oldPrimitives.end(); ++pitr) { - if ((*pitr)->getMode()!=0xffffff) primitives.push_back(*pitr); + if (!toremove.count(*pitr)) primitives.push_back(*pitr); } } #endif @@ -1479,34 +1511,6 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) return false; } -bool Optimizer::MergeGeometryVisitor::geometryContainsSharedArrays(osg::Geometry& geom) -{ - if (geom.getVertexArray() && geom.getVertexArray()->referenceCount()>1) return true; - if (geom.getNormalArray() && geom.getNormalArray()->referenceCount()>1) return true; - if (geom.getColorArray() && geom.getColorArray()->referenceCount()>1) return true; - if (geom.getSecondaryColorArray() && geom.getSecondaryColorArray()->referenceCount()>1) return true; - if (geom.getFogCoordArray() && geom.getFogCoordArray()->referenceCount()>1) return true; - - - for(unsigned int unit=0;unitreferenceCount()>1) return true; - } - - // shift the indices of the incoming primitives to account for the pre existing geometry. - for(osg::Geometry::PrimitiveSetList::iterator primItr=geom.getPrimitiveSetList().begin(); - primItr!=geom.getPrimitiveSetList().end(); - ++primItr) - { - if ((*primItr)->referenceCount()>1) return true; - } - - - return false; -} - - class MergeArrayVisitor : public osg::ArrayVisitor { protected: @@ -1574,6 +1578,8 @@ class MergeArrayVisitor : public osg::ArrayVisitor bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs) { + if (lhs.containsSharedArrays()) + lhs.duplicateSharedArrays(); MergeArrayVisitor merger; @@ -1661,7 +1667,6 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom } } - // shift the indices of the incoming primitives to account for the pre existing geometry. osg::Geometry::PrimitiveSetList::iterator primItr; for(primItr=rhs.getPrimitiveSetList().begin(); primItr!=rhs.getPrimitiveSetList().end(); ++primItr) @@ -1697,7 +1702,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom } else { - primitive->offsetIndices(base); + (*primItr) = clonePrimitive(primitive); + (*primItr)->offsetIndices(base); } } break; @@ -1722,7 +1728,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom } else { - primitive->offsetIndices(base); + (*primItr) = clonePrimitive(primitive); + (*primItr)->offsetIndices(base); } } break; @@ -1731,7 +1738,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType): case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType): default: - primitive->offsetIndices(base); + (*primItr) = clonePrimitive(primitive); + (*primItr)->offsetIndices(base); break; } } diff --git a/components/sceneutil/optimizer.hpp b/components/sceneutil/optimizer.hpp index 6dd4394d1..d7c83e898 100644 --- a/components/sceneutil/optimizer.hpp +++ b/components/sceneutil/optimizer.hpp @@ -65,7 +65,7 @@ class Optimizer public: - Optimizer() {} + Optimizer() : _mergeAlphaBlending(false) {} virtual ~Optimizer() {} enum OptimizationOptions @@ -118,6 +118,9 @@ class Optimizer STATIC_OBJECT_DETECTION }; + void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; } + void setViewPoint(const osg::Vec3f& viewPoint) { _viewPoint = viewPoint; } + /** Reset internal data to initial state - the getPermissibleOptionsMap is cleared.*/ void reset(); @@ -252,6 +255,9 @@ class Optimizer typedef std::map PermissibleOptimizationsMap; PermissibleOptimizationsMap _permissibleOptimizationsMap; + osg::Vec3f _viewPoint; + bool _mergeAlphaBlending; + public: /** Flatten Static Transform nodes by applying their transform to the @@ -371,7 +377,16 @@ class Optimizer /// default to traversing all children. MergeGeometryVisitor(Optimizer* optimizer=0) : BaseOptimizerVisitor(optimizer, MERGE_GEOMETRY), - _targetMaximumNumberOfVertices(10000), _allowedToMerge(true) {} + _targetMaximumNumberOfVertices(10000), _alphaBlendingActive(false), _mergeAlphaBlending(false) {} + + void setMergeAlphaBlending(bool merge) + { + _mergeAlphaBlending = merge; + } + void setViewPoint(const osg::Vec3f& viewPoint) + { + _viewPoint = viewPoint; + } void setTargetMaximumNumberOfVertices(unsigned int num) { @@ -385,15 +400,13 @@ class Optimizer void pushStateSet(osg::StateSet* stateSet); void popStateSet(); - void checkAllowedToMerge(); + void checkAlphaBlendingActive(); virtual void apply(osg::Group& group); virtual void apply(osg::Billboard&) { /* don't do anything*/ } bool mergeGroup(osg::Group& group); - static bool geometryContainsSharedArrays(osg::Geometry& geom); - static bool mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs); static bool mergePrimitive(osg::DrawArrays& lhs,osg::DrawArrays& rhs); @@ -406,7 +419,9 @@ class Optimizer unsigned int _targetMaximumNumberOfVertices; std::vector _stateSetStack; - bool _allowedToMerge; + bool _alphaBlendingActive; + bool _mergeAlphaBlending; + osg::Vec3f _viewPoint; }; }; diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 97c8e1d39..627353b99 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -106,7 +106,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) } } -osg::ref_ptr RigGeometry::getSourceGeometry() +osg::ref_ptr RigGeometry::getSourceGeometry() const { return mSourceGeometry; } diff --git a/components/sceneutil/riggeometry.hpp b/components/sceneutil/riggeometry.hpp index a393530ec..801c172b3 100644 --- a/components/sceneutil/riggeometry.hpp +++ b/components/sceneutil/riggeometry.hpp @@ -44,7 +44,7 @@ namespace SceneUtil /// @note The source geometry will not be modified. void setSourceGeometry(osg::ref_ptr sourceGeom); - osg::ref_ptr getSourceGeometry(); + osg::ref_ptr getSourceGeometry() const; virtual void accept(osg::NodeVisitor &nv); virtual bool supports(const osg::PrimitiveFunctor&) const { return true; } diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 95c1ca491..1dc62cfb8 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include "terraindrawable.hpp" @@ -35,7 +34,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T } -osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags) +osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) { ChunkId id = std::make_tuple(center, lod, lodFlags); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); @@ -163,10 +162,6 @@ std::vector > ChunkManager::createPasses(float chunk 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); - transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f)); - osg::ref_ptr positions (new osg::Vec3Array); osg::ref_ptr normals (new osg::Vec3Array); osg::ref_ptr colors (new osg::Vec4ubArray); @@ -224,16 +219,15 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->setPasses(createPasses(chunkSize, chunkCenter, false)); } - transform->addChild(geometry); - transform->getBound(); - geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts); if (mSceneManager->getIncrementalCompileOperation()) { mSceneManager->getIncrementalCompileOperation()->add(geometry); } - return transform; + geometry->setNodeMask(mNodeMask); + + return geometry; } } diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index be83e158c..d6f4dd98e 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -6,6 +6,7 @@ #include #include "buffercache.hpp" +#include "quadtreeworld.hpp" namespace osg { @@ -29,23 +30,28 @@ namespace Terrain typedef std::tuple ChunkId; // Center, Lod, Lod Flags /// @brief Handles loading and caching of terrain chunks - class ChunkManager : public Resource::GenericResourceManager + class ChunkManager : public Resource::GenericResourceManager, public QuadTreeWorld::ChunkManager { public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint); void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; } void setCompositeMapLevel(float level) { mCompositeMapLevel = level; } void setMaxCompositeGeometrySize(float maxCompGeometrySize) { mMaxCompGeometrySize = maxCompGeometrySize; } + void setNodeMask(unsigned int mask) { mNodeMask = mask; } + virtual unsigned int getNodeMask() override { return mNodeMask; } + void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; void clearCache() override; void releaseGLObjects(osg::State* state) override; + virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } + private: osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); @@ -61,6 +67,8 @@ namespace Terrain CompositeMapRenderer* mCompositeMapRenderer; BufferCache mBufferCache; + unsigned int mNodeMask; + unsigned int mCompositeMapSize; float mCompositeMapLevel; float mMaxCompGeometrySize; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index c43a9a21b..842dced20 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -9,6 +9,7 @@ #include #include +#include #include "quadtreenode.hpp" #include "storage.hpp" @@ -63,6 +64,12 @@ public: int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); + if (node->getSize()>1 && dist < (8192+1024)*1.41421356237) + { + // to prevent making chunks who will cross the activegrid border + return false; + } + return nativeLodLevel <= lodLevel; } @@ -231,6 +238,7 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour mChunkManager->setCompositeMapSize(compMapResolution); mChunkManager->setCompositeMapLevel(compMapLevel); mChunkManager->setMaxCompositeGeometrySize(maxCompGeometrySize); + mChunkManagers.push_back(mChunkManager.get()); } QuadTreeWorld::~QuadTreeWorld() @@ -289,7 +297,7 @@ unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewD return lodFlags; } -void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, ChunkManager* chunkManager) +void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector& chunkManagers) { if (!vd->hasChanged() && entry.mRenderingNode) return; @@ -308,7 +316,20 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, C } if (!entry.mRenderingNode) - entry.mRenderingNode = chunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags); + { + auto pat = new SceneUtil::PositionAttitudeTransform; + pat->setPosition(osg::Vec3f(entry.mNode->getCenter().x()*cellWorldSize, entry.mNode->getCenter().y()*cellWorldSize, 0.f)); + + const osg::Vec2f& center = entry.mNode->getCenter(); + bool far = (center.x() <= gridbounds.x() || center.y() <= gridbounds.y() || center.x() >= gridbounds.z() || center.y() >= gridbounds.w()); + + for (QuadTreeWorld::ChunkManager* m : chunkManagers) + { + osg::Node* n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint()); + if (n) pat->addChild(n); + } + entry.mRenderingNode = pat; + } } void updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil::CullVisitor* cv, float cellworldsize, bool outofworld) @@ -385,7 +406,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) bool needsUpdate = true; ViewData* vd = nullptr; if (isCullVisitor) - vd = mViewDataMap->getViewData(static_cast(&nv)->getCurrentCamera(), nv.getViewPoint(), needsUpdate); + vd = mViewDataMap->getViewData(static_cast(&nv)->getCurrentCamera(), nv.getViewPoint(), mActiveGrid, needsUpdate); else { static ViewData sIntersectionViewData; @@ -432,12 +453,12 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) } } + const float cellWorldSize = mStorage->getCellWorldSize(); + for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - - loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); - + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers); entry.mRenderingNode->accept(nv); } @@ -490,13 +511,17 @@ void QuadTreeWorld::enable(bool enabled) void QuadTreeWorld::cacheCell(View *view, int x, int y) { ensureQuadTreeBuilt(); + osg::Vec4i grid (x,y,x+1,y+1); ViewData* vd = static_cast(view); + vd->setActiveGrid(grid); mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5f,y+0.5f)); + const float cellWorldSize = mStorage->getCellWorldSize(); + for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); } } @@ -505,18 +530,21 @@ View* QuadTreeWorld::createView() return new ViewData; } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic &abort) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort) { ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); + vd->setActiveGrid(grid); mRootNode->traverseNodes(vd, viewPoint, mLodCallback, mViewDistance); + const float cellWorldSize = mStorage->getCellWorldSize(); + for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); } vd->markUnchanged(); } @@ -526,7 +554,7 @@ void QuadTreeWorld::storeView(const View* view, double referenceTime) osg::ref_ptr dummy = new osg::DummyObject; const ViewData* vd = static_cast(view); bool needsUpdate = false; - ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), needsUpdate); + ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), vd->getActiveGrid(), needsUpdate); stored->copyFrom(*vd); stored->setLastUsageTimeStamp(referenceTime); } @@ -556,5 +584,10 @@ void QuadTreeWorld::unloadCell(int x, int y) World::unloadCell(x,y); } +void QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m) +{ + mChunkManagers.push_back(m); + mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask()); +} } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index bcb671ee1..7634ea868 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -38,11 +38,20 @@ namespace Terrain virtual void unloadCell(int x, int y); View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint, std::atomic& abort); + void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort); void storeView(const View* view, double referenceTime); void reportStats(unsigned int frameNumber, osg::Stats* stats); + class ChunkManager + { + public: + virtual ~ChunkManager(){} + virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) = 0; + virtual unsigned int getNodeMask() { return 0; } + }; + void addChunkManager(ChunkManager*); + private: void ensureQuadTreeBuilt(); @@ -51,6 +60,8 @@ namespace Terrain osg::ref_ptr mViewDataMap; osg::ref_ptr mLodCallback; + std::vector mChunkManagers; + OpenThreads::Mutex mQuadTreeMutex; bool mQuadTreeBuilt; float mLodFactor; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index a0e5e4718..4398d1a14 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -5,9 +5,10 @@ #include #include +#include #include "chunkmanager.hpp" #include "compositemaprenderer.hpp" - +#include "storage.hpp" namespace Terrain { @@ -57,12 +58,17 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu } else { - osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0); + osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f()); if (!node) return nullptr; + + const float cellWorldSize = mStorage->getCellWorldSize(); + osg::ref_ptr pat = new SceneUtil::PositionAttitudeTransform; + pat->setPosition(osg::Vec3f(chunkCenter.x()*cellWorldSize, chunkCenter.y()*cellWorldSize, 0.f)); + pat->addChild(node); if (parent) - parent->addChild(node); - return node; + parent->addChild(pat); + return pat; } } diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index d07a0e356..6399425a4 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -24,6 +24,7 @@ void ViewData::copyFrom(const ViewData& other) mChanged = other.mChanged; mHasViewPoint = other.mHasViewPoint; mViewPoint = other.mViewPoint; + mActiveGrid = other.mActiveGrid; } void ViewData::add(QuadTreeNode *node) @@ -118,12 +119,12 @@ bool ViewData::Entry::set(QuadTreeNode *node) } } -bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist) +bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist, const osg::Vec4i& activeGrid) { - return vd->hasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist; + return vd->hasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist && vd->getActiveGrid() == activeGrid; } -ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, bool& needsUpdate) +ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate) { Map::const_iterator found = mViews.find(viewer); ViewData* vd = nullptr; @@ -135,11 +136,11 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo else vd = found->second; - if (!suitable(vd, viewPoint, mReuseDistance)) + if (!suitable(vd, viewPoint, mReuseDistance, activeGrid)) { for (Map::const_iterator other = mViews.begin(); other != mViews.end(); ++other) { - if (suitable(other->second, viewPoint, mReuseDistance) && other->second->getNumEntries()) + if (suitable(other->second, viewPoint, mReuseDistance, activeGrid) && other->second->getNumEntries()) { vd->copyFrom(*other->second); needsUpdate = false; @@ -147,6 +148,7 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo } } vd->setViewPoint(viewPoint); + vd->setActiveGrid(activeGrid); needsUpdate = true; } else diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 7f9f14af5..87b45f57b 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -57,6 +57,9 @@ namespace Terrain void setViewPoint(const osg::Vec3f& viewPoint); const osg::Vec3f& getViewPoint() const; + void setActiveGrid(const osg::Vec4i &grid) { if (grid != mActiveGrid) {mActiveGrid = grid;mEntries.clear();mNumEntries=0;} } + const osg::Vec4i &getActiveGrid() const { return mActiveGrid;} + private: std::vector mEntries; unsigned int mNumEntries; @@ -64,6 +67,7 @@ namespace Terrain bool mChanged; osg::Vec3f mViewPoint; bool mHasViewPoint; + osg::Vec4i mActiveGrid; }; class ViewDataMap : public osg::Referenced @@ -75,7 +79,7 @@ namespace Terrain , mExpiryDelay(1.f) {} - ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, bool& needsUpdate); + ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate); ViewData* createOrReuseView(); diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 2d53f4090..b57fa1cef 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -23,7 +23,6 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); - mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); osg::ref_ptr material (new osg::Material); material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); @@ -48,6 +47,7 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer)); + mChunkManager->setNodeMask(nodeMask); mCellBorder.reset(new CellBorder(this,mTerrainRoot.get(),borderMask)); mResourceSystem->addResourceManager(mChunkManager.get()); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 618095a60..e55932169 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -147,7 +147,7 @@ namespace Terrain /// @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& viewPoint, std::atomic& abort) {} + virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort) {} /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// @note Not thread safe. @@ -161,6 +161,8 @@ namespace Terrain osg::Callback* getHeightCullCallback(float highz, unsigned int mask); + void setActiveGrid(const osg::Vec4i &grid) { mActiveGrid = grid; } + protected: Storage* mStorage; @@ -181,6 +183,8 @@ namespace Terrain std::set> mLoadedCells; osg::ref_ptr mHeightCullCallback; + + osg::Vec4i mActiveGrid; }; } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 5b587776c..2653f2fc4 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -106,6 +106,15 @@ composite map resolution = 512 # Controls the maximum size of composite geometry, should be >= 1.0. With low values there will be many small chunks, with high values - lesser count of bigger chunks. max composite geometry size = 4.0 +# Load far objects on terrain +object paging = true + +# Turn off to save memory but worse FPS +object paging merge geometry = true + +# Cull objects smaller than this size divided by distance +object paging min size = 0.01 + [Fog] # If true, use extended fog parameters for distant terrain not controlled by From c0f128bcb3b084e101c167836a14b6015367a4ea Mon Sep 17 00:00:00 2001 From: bzzt Date: Sat, 3 Aug 2019 13:37:00 +0000 Subject: [PATCH 14/81] disablesupprort Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 31 +++++++++++--- apps/openmw/mwrender/objectpaging.hpp | 8 ++++ apps/openmw/mwrender/renderingmanager.cpp | 7 ++++ apps/openmw/mwrender/renderingmanager.hpp | 3 ++ apps/openmw/mwworld/cellstore.cpp | 49 +++++++++++++---------- apps/openmw/mwworld/worldimp.cpp | 22 ++++++---- 6 files changed, 84 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 6453889bc..8a8015372 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -171,7 +171,7 @@ namespace MWRender osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE; osg::Vec3f relativeViewPoint = viewPoint - worldCenter; - std::vector refs; + std::map refs; std::vector esm; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); @@ -197,8 +197,8 @@ namespace MWRender if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); if (!typeFilter(type,size>=2)) continue; - if (deleted) continue; - refs.push_back(ref); + if (deleted) { refs.erase(ref.mRefNum); continue; } + refs[ref.mRefNum] = ref; } } catch (std::exception& e) @@ -210,18 +210,25 @@ namespace MWRender { ESM::CellRef ref = it->first; bool deleted = it->second; - if (deleted) continue; + if (deleted) { refs.erase(ref.mRefNum); continue; } int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); if (!typeFilter(type,size>=2)) continue; - refs.push_back(ref); + refs[ref.mRefNum] = ref; } } } + { + OpenThreads::ScopedLock lock(mDisabledMutex); + for (auto disabled : mDisabled) + refs.erase(disabled); + } + osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); - for (const ESM::CellRef& ref : refs) + for (const auto& pair : refs) { + const ESM::CellRef& ref = pair.second; std::string id = Misc::StringUtils::lowerCase(ref.mRefID); if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player @@ -299,4 +306,16 @@ namespace MWRender return Mask_Static; } + void ObjectPaging::enableObject(const ESM::RefNum & refnum, bool enabled) + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (enabled) mDisabled.erase(refnum); + else mDisabled.insert(refnum); + } + + void ObjectPaging::clear() + { + OpenThreads::ScopedLock lock(mDisabledMutex); + mDisabled.clear(); + } } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 6d66d078d..8c3c2b493 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -5,6 +5,8 @@ #include #include +#include + namespace Resource { class SceneManager; @@ -33,10 +35,16 @@ namespace MWRender virtual unsigned int getNodeMask() override; + void enableObject(const ESM::RefNum & refnum, bool enabled); + void clear(); + private: Resource::SceneManager* mSceneManager; bool mMergeGeometry; float mMinSize; + + OpenThreads::Mutex mDisabledMutex; + std::set mDisabled; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9c92da767..41cfc56a4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1119,6 +1119,8 @@ namespace MWRender mSky->setMoonColour(false); notifyWorldSpaceChanged(); + if (mObjectPaging) + mObjectPaging->clear(); } MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) @@ -1480,4 +1482,9 @@ namespace MWRender { mTerrain->setActiveGrid(grid); } + void RenderingManager::pagingEnableObject(const ESM::RefNum & refnum, bool enabled) + { + if (mObjectPaging) + mObjectPaging->enableObject(refnum, enabled); + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index dff76f95e..6bf122232 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -42,6 +42,7 @@ namespace osgViewer namespace ESM { struct Cell; + struct RefNum; } namespace Terrain @@ -240,6 +241,8 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); + void pagingEnableObject(const ESM::RefNum & refnum, bool enabled); + private: void updateProjectionMatrix(); void updateTextureFiltering(); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 599f345b8..6b737f202 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -106,7 +106,7 @@ namespace template void readReferenceCollection (ESM::ESMReader& reader, - MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap) + MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap, MWWorld::CellStore* cellstore) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -142,6 +142,11 @@ namespace { // overwrite existing reference iter->load (state); + if (!iter->mData.isEnabled()) + { + iter->mData.enable(); + MWBase::Environment::get().getWorld()->disable(MWWorld::Ptr(&*iter, cellstore)); + } return; } @@ -809,107 +814,107 @@ namespace MWWorld { case ESM::REC_ACTI: - readReferenceCollection (reader, mActivators, cref, contentFileMap); + readReferenceCollection (reader, mActivators, cref, contentFileMap, this); break; case ESM::REC_ALCH: - readReferenceCollection (reader, mPotions, cref, contentFileMap); + readReferenceCollection (reader, mPotions, cref, contentFileMap, this); break; case ESM::REC_APPA: - readReferenceCollection (reader, mAppas, cref, contentFileMap); + readReferenceCollection (reader, mAppas, cref, contentFileMap, this); break; case ESM::REC_ARMO: - readReferenceCollection (reader, mArmors, cref, contentFileMap); + readReferenceCollection (reader, mArmors, cref, contentFileMap, this); break; case ESM::REC_BOOK: - readReferenceCollection (reader, mBooks, cref, contentFileMap); + readReferenceCollection (reader, mBooks, cref, contentFileMap, this); break; case ESM::REC_CLOT: - readReferenceCollection (reader, mClothes, cref, contentFileMap); + readReferenceCollection (reader, mClothes, cref, contentFileMap, this); break; case ESM::REC_CONT: - readReferenceCollection (reader, mContainers, cref, contentFileMap); + readReferenceCollection (reader, mContainers, cref, contentFileMap, this); break; case ESM::REC_CREA: - readReferenceCollection (reader, mCreatures, cref, contentFileMap); + readReferenceCollection (reader, mCreatures, cref, contentFileMap, this); break; case ESM::REC_DOOR: - readReferenceCollection (reader, mDoors, cref, contentFileMap); + readReferenceCollection (reader, mDoors, cref, contentFileMap, this); break; case ESM::REC_INGR: - readReferenceCollection (reader, mIngreds, cref, contentFileMap); + readReferenceCollection (reader, mIngreds, cref, contentFileMap, this); break; case ESM::REC_LEVC: - readReferenceCollection (reader, mCreatureLists, cref, contentFileMap); + readReferenceCollection (reader, mCreatureLists, cref, contentFileMap, this); break; case ESM::REC_LEVI: - readReferenceCollection (reader, mItemLists, cref, contentFileMap); + readReferenceCollection (reader, mItemLists, cref, contentFileMap, this); break; case ESM::REC_LIGH: - readReferenceCollection (reader, mLights, cref, contentFileMap); + readReferenceCollection (reader, mLights, cref, contentFileMap, this); break; case ESM::REC_LOCK: - readReferenceCollection (reader, mLockpicks, cref, contentFileMap); + readReferenceCollection (reader, mLockpicks, cref, contentFileMap, this); break; case ESM::REC_MISC: - readReferenceCollection (reader, mMiscItems, cref, contentFileMap); + readReferenceCollection (reader, mMiscItems, cref, contentFileMap, this); break; case ESM::REC_NPC_: - readReferenceCollection (reader, mNpcs, cref, contentFileMap); + readReferenceCollection (reader, mNpcs, cref, contentFileMap, this); break; case ESM::REC_PROB: - readReferenceCollection (reader, mProbes, cref, contentFileMap); + readReferenceCollection (reader, mProbes, cref, contentFileMap, this); break; case ESM::REC_REPA: - readReferenceCollection (reader, mRepairs, cref, contentFileMap); + readReferenceCollection (reader, mRepairs, cref, contentFileMap, this); break; case ESM::REC_STAT: - readReferenceCollection (reader, mStatics, cref, contentFileMap); + readReferenceCollection (reader, mStatics, cref, contentFileMap, this); break; case ESM::REC_WEAP: - readReferenceCollection (reader, mWeapons, cref, contentFileMap); + readReferenceCollection (reader, mWeapons, cref, contentFileMap, this); break; case ESM::REC_BODY: - readReferenceCollection (reader, mBodyParts, cref, contentFileMap); + readReferenceCollection (reader, mBodyParts, cref, contentFileMap, this); break; default: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c51266bab..0817a4226 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -814,6 +814,9 @@ namespace MWWorld if(mWorldScene->getActiveCells().find (reference.getCell()) != mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->addObjectToScene (reference); + + if (reference.getCellRef().getRefNum().hasContentFile()) + mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), true); } } @@ -838,20 +841,23 @@ namespace MWWorld void World::disable (const Ptr& reference) { + if (!reference.getRefData().isEnabled()) + return; + // disable is a no-op for items in containers if (!reference.isInCell()) return; - if (reference.getRefData().isEnabled()) - { - if (reference == getPlayerPtr()) - throw std::runtime_error("can not disable player object"); + if (reference == getPlayerPtr()) + throw std::runtime_error("can not disable player object"); - reference.getRefData().disable(); + reference.getRefData().disable(); - if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) - mWorldScene->removeObjectFromScene (reference); - } + if (reference.getCellRef().getRefNum().hasContentFile()) + mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), false); + + if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) + mWorldScene->removeObjectFromScene (reference); } void World::advanceTime (double hours, bool incremental) From ce505a9bb355233ff1dfbe4017aa4e66711a86e4 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Thu, 30 Apr 2020 13:37:00 +0000 Subject: [PATCH 15/81] crashfix + optimiziation Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 233 +++++++++++++++++++------- apps/openmw/mwrender/objectpaging.hpp | 6 +- apps/openmw/mwworld/scene.cpp | 2 +- components/sceneutil/optimizer.cpp | 102 +++++++++-- components/terrain/chunkmanager.cpp | 8 +- components/terrain/chunkmanager.hpp | 4 +- components/terrain/quadtreeworld.cpp | 42 +++-- components/terrain/quadtreeworld.hpp | 3 +- components/terrain/terraingrid.cpp | 2 +- files/settings-default.cfg | 4 +- 10 files changed, 300 insertions(+), 106 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 8a8015372..b529336eb 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -1,5 +1,7 @@ #include "objectpaging.hpp" +#include + #include #include #include @@ -14,7 +16,6 @@ #include #include -#include #include #include @@ -60,7 +61,7 @@ namespace MWRender } } - osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) + osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) { if (!far)return nullptr; ChunkId id = std::make_tuple(center, size); @@ -70,7 +71,7 @@ namespace MWRender return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, viewPoint); + osg::ref_ptr node = createChunk(size, center, viewPoint, compile); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -92,12 +93,11 @@ namespace MWRender class CopyOp : public osg::CopyOp { public: - CopyOp() : mDistance(0.f) { - setCopyFlags(osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES - #if OSG_MIN_VERSION_REQUIRED(3,5,6) - |osg::CopyOp::DEEP_COPY_ARRAYS|osg::CopyOp::DEEP_COPY_PRIMITIVES // damned vbogarbage racing - #endif - ); + CopyOp(bool deep) : mDistance(0.f) { + unsigned int flags = osg::CopyOp::DEEP_COPY_NODES; + if (deep) + flags |= osg::CopyOp::DEEP_COPY_DRAWABLES; + setCopyFlags(flags); } float mDistance; @@ -128,12 +128,13 @@ namespace MWRender return n; } - osg::Node* n = osg::CopyOp::operator()(node); - if (n) { - n->setDataVariance(osg::Object::STATIC); - n->setUserDataContainer(nullptr); - n->setName(""); - } + if (const osg::Drawable* d = node->asDrawable()) + return operator()(d); + + osg::Node* n = osg::clone(node, *this); + n->setDataVariance(osg::Object::STATIC); + n->setUserDataContainer(nullptr); + n->setName(""); return n; } virtual osg::Drawable* operator() (const osg::Drawable* drawable) const @@ -142,11 +143,20 @@ namespace MWRender return nullptr; if (const SceneUtil::RigGeometry* rig = dynamic_cast(drawable)) - return osg::CopyOp::operator()(rig->getSourceGeometry()); + return operator()(rig->getSourceGeometry()); if (const SceneUtil::MorphGeometry* morph = dynamic_cast(drawable)) - return osg::CopyOp::operator()(morph->getSourceGeometry()); + return operator()(morph->getSourceGeometry()); - return osg::CopyOp::operator()(drawable); + if (getCopyFlags() & DEEP_COPY_DRAWABLES) + { + osg::Drawable* d = osg::clone(drawable, *this); + d->setDataVariance(osg::Object::STATIC); + d->setUserDataContainer(nullptr); + d->setName(""); + return d; + } + else + return osg::CopyOp::operator()(drawable); } virtual osg::Callback* operator() (const osg::Callback* callback) const { @@ -154,20 +164,77 @@ namespace MWRender } }; + class TemplateRef : public osg::Object + { + public: + TemplateRef() {} + TemplateRef(const TemplateRef& copy, const osg::CopyOp&) : mObjects(copy.mObjects) {} + META_Object(MWRender, TemplateRef) + std::vector> mObjects; + }; + + class AnalyzeVisitor : public osg::NodeVisitor + { + public: + AnalyzeVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mCurrentStateSet(nullptr) {} + + typedef std::unordered_map StateSetCounter; + struct Result + { + StateSetCounter mStateSetCounter; + unsigned int mNumVerts = 0; + }; + + virtual void apply(osg::Node& node) + { + if (node.getStateSet()) + mCurrentStateSet = node.getStateSet(); + traverse(node); + } + virtual void apply(osg::Geometry& geom) + { + mResult.mNumVerts += geom.getVertexArray()->getNumElements(); + ++mResult.mStateSetCounter[mCurrentStateSet]; + ++mGlobalStateSetCounter[mCurrentStateSet]; + } + Result retrieveResult() + { + Result result = mResult; + mResult = Result(); + mCurrentStateSet = nullptr; + return result; + } + float getMergeBenefit(const Result& result) + { + if (result.mStateSetCounter.empty()) return 1; + float mergeBenefit = 0; + for (auto pair : result.mStateSetCounter) + { + mergeBenefit += mGlobalStateSetCounter[pair.first]; + } + mergeBenefit /= result.mStateSetCounter.size(); + return mergeBenefit; + } + + Result mResult; + osg::StateSet* mCurrentStateSet; + StateSetCounter mGlobalStateSetCounter; + }; + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) { - mMergeGeometry = Settings::Manager::getBool("object paging merge geometry", "Terrain"); + mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); } - osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint) + osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile) { osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f)); - osg::ref_ptr group = new osg::Group; - osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE; osg::Vec3f relativeViewPoint = viewPoint - worldCenter; @@ -226,21 +293,18 @@ namespace MWRender osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); + struct InstanceList + { + std::vector mInstances; + AnalyzeVisitor::Result mAnalyzeResult; + }; + typedef std::map, InstanceList> NodeMap; + NodeMap nodes; + AnalyzeVisitor analyzeVisitor; + for (const auto& pair : refs) { const ESM::CellRef& ref = pair.second; - std::string id = Misc::StringUtils::lowerCase(ref.mRefID); - if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") - continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - - int type = store.findStatic(id); - std::string model = "meshes/" + getModel(type, id, store); -/* - bool useAnim = type != ESM::REC_STAT; - if (useAnim) - model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); -*/ - if (model.empty()) continue; osg::Vec3f pos = ref.mPos.asVec3(); if (size < 1.f) @@ -254,50 +318,101 @@ namespace MWRender continue; } - osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); + std::string id = Misc::StringUtils::lowerCase(ref.mRefID); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player + + int type = store.findStatic(id); + std::string model = getModel(type, id, store); + if (model.empty()) continue; + model = "meshes/" + model; +/* + bool useAnim = type != ESM::REC_STAT; + if (useAnim) + model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); +*/ + osg::ref_ptr cnode = mSceneManager->getTemplate(model, compile); float d = (viewPoint - pos).length(); - if (cnode->getBound().radius() * ref.mScale < d*mMinSize) continue; - CopyOp co = CopyOp(); - co.mDistance = d; - osg::ref_ptr node = osg::clone(cnode.get(), co); - node->setUserDataContainer(nullptr); - - osg::Matrixf matrix; - matrix.preMultTranslate(pos - worldCenter); - matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * - osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * - osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); - matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); - osg::ref_ptr trans = new osg::MatrixTransform(matrix); - trans->addChild(node); - trans->setDataVariance(osg::Object::STATIC); - - group->addChild(trans); + auto emplaced = nodes.emplace(cnode, InstanceList()); + if (emplaced.second) + { + const_cast(cnode.get())->accept(analyzeVisitor); + emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); + } + emplaced.first->second.mInstances.push_back(&ref); } - if (mMergeGeometry) + osg::ref_ptr group = new osg::Group; + osg::ref_ptr mergeGroup = new osg::Group; + osg::ref_ptr templateRefs = new TemplateRef; + for (const auto& pair : nodes) + { + const osg::Node* cnode = pair.first; + + // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache + templateRefs->mObjects.push_back(cnode); + + const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult; + + float mergeCost = analyzeResult.mNumVerts * size; + float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; + bool merge = mergeBenefit > mergeCost; + + for (auto cref : pair.second.mInstances) + { + const ESM::CellRef& ref = *cref; + osg::Vec3f pos = ref.mPos.asVec3(); + float d = (viewPoint - pos).length(); + + CopyOp co = CopyOp(merge); + co.mDistance = d; + osg::ref_ptr node = osg::clone(cnode, co); + node->setUserDataContainer(nullptr); + + osg::Matrixf matrix; + matrix.preMultTranslate(pos - worldCenter); + matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * + osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * + osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); + matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); + osg::ref_ptr trans = new osg::MatrixTransform(matrix); + trans->addChild(node); + trans->setDataVariance(osg::Object::STATIC); + + if (merge) + mergeGroup->addChild(trans); + else + group->addChild(trans); + } + } + + if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - group->getBound().center()).length2() > group->getBound().radius2()) + if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); } optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback); unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY; - optimizer.optimize(group, options); + optimizer.optimize(mergeGroup, options); + + group->addChild(mergeGroup); + + auto ico = mSceneManager->getIncrementalCompileOperation(); + if (compile && ico) ico->add(mergeGroup); } - auto ico = mSceneManager->getIncrementalCompileOperation(); - if (ico) ico->add(group); - else group->getBound(); - + group->getBound(); group->setNodeMask(Mask_Static); + group->getOrCreateUserDataContainer()->addUserObject(templateRefs); + return group; } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 8c3c2b493..16de6e8bc 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -27,9 +27,9 @@ namespace MWRender ObjectPaging(Resource::SceneManager* sceneManager); ~ObjectPaging() = default; - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) override; + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) override; - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile); virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } @@ -40,7 +40,7 @@ namespace MWRender private: Resource::SceneManager* mSceneManager; - bool mMergeGeometry; + float mMergeFactor; float mMinSize; OpenThreads::Mutex mDisabledMutex; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 127b56c94..1dac18eaa 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -477,7 +477,7 @@ namespace MWWorld { float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(currentGridCenter->x(), currentGridCenter->y(), centerX, centerY, true); - float distance = std::max(std::abs(centerY-pos.x()), std::abs(centerY-pos.y())); + float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold if (distance <= maxDistance) return *currentGridCenter; diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 7bcccdbe3..985fd9ee2 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -18,6 +18,7 @@ #include "optimizer.hpp" +#include #include #include #include @@ -587,17 +588,36 @@ void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Node& node) traverse(node); } +bool needvbo(const osg::Geometry* geom) +{ +#if OSG_MIN_VERSION_REQUIRED(3,5,6) + return true; +#else + return geom->getUseVertexBufferObjects(); +#endif +} + +osg::Array* cloneArray(osg::Array* array, osg::VertexBufferObject*& vbo, const osg::Geometry* geom) +{ + array = osg::clone(array, osg::CopyOp::DEEP_COPY_ALL); + if (!vbo && needvbo(geom)) + vbo = new osg::VertexBufferObject; + if (vbo) + array->setVertexBufferObject(vbo); + return array; +} void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Drawable& drawable) { osg::Geometry *geometry = drawable.asGeometry(); if((geometry) && (isOperationPermissibleForObject(&drawable))) { + osg::VertexBufferObject* vbo = nullptr; if(geometry->getVertexArray() && geometry->getVertexArray()->referenceCount() > 1) { - geometry->setVertexArray(dynamic_cast(geometry->getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL))); + geometry->setVertexArray(cloneArray(geometry->getVertexArray(), vbo, geometry)); } if(geometry->getNormalArray() && geometry->getNormalArray()->referenceCount() > 1) { - geometry->setNormalArray(dynamic_cast(geometry->getNormalArray()->clone(osg::CopyOp::DEEP_COPY_ALL))); + geometry->setNormalArray(cloneArray(geometry->getNormalArray(), vbo, geometry)); } } _drawableSet.insert(&drawable); @@ -1110,14 +1130,30 @@ void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) popStateSet(); } -osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps) +osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps, osg::ElementBufferObject*& ebo, const osg::Geometry* geom) { if (ps->referenceCount() <= 1) return ps; - ps = dynamic_cast(ps->clone(osg::CopyOp::DEEP_COPY_ALL)); + ps = osg::clone(ps, osg::CopyOp::DEEP_COPY_ALL); + + osg::DrawElements* drawElements = ps->getDrawElements(); + if (!drawElements) return ps; + + if (!ebo && needvbo(geom)) + ebo = new osg::ElementBufferObject; + if (ebo) + drawElements->setElementBufferObject(ebo); + return ps; } +bool containsSharedPrimitives(const osg::Geometry* geom) +{ + for (unsigned int i=0; igetNumPrimitiveSets(); ++i) + if (geom->getPrimitiveSet(i)->referenceCount() > 1) return true; + return false; +} + bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (!isOperationPermissibleForObject(&group)) return false; @@ -1305,6 +1341,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (!drawable) continue; osg::Geometry* geom = drawable->asGeometry(); + osg::ElementBufferObject* ebo = nullptr; if (geom) { osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList(); @@ -1317,12 +1354,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (prim->getNumIndices()==3) { - prim = clonePrimitive(prim); (*itr) = prim; + prim = clonePrimitive(prim, ebo, geom); (*itr) = prim; prim->setMode(osg::PrimitiveSet::TRIANGLES); } else if (prim->getNumIndices()==4) { - prim = clonePrimitive(prim); (*itr) = prim; + prim = clonePrimitive(prim, ebo, geom); (*itr) = prim; prim->setMode(osg::PrimitiveSet::QUADS); } } @@ -1337,6 +1374,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (!drawable) continue; osg::Geometry* geom = drawable->asGeometry(); + osg::ElementBufferObject* ebo = nullptr; if (geom) { if (geom->getNumPrimitiveSets()>0 && @@ -1379,7 +1417,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (combine) { - lhs = clonePrimitive(lhs); + lhs = clonePrimitive(lhs, ebo, geom); primitives[lhsNo] = lhs; switch(lhs->getType()) @@ -1499,6 +1537,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) } } #endif + if (doneCombine && !geom->containsSharedArrays() && !containsSharedPrimitives(geom)) + { + // prefer to use vbo for merged geometries as vbo uses less memory than display lists. + geom->setUseVertexBufferObjects(true); + geom->setUseDisplayList(false); + } } } @@ -1578,16 +1622,14 @@ class MergeArrayVisitor : public osg::ArrayVisitor bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs) { - if (lhs.containsSharedArrays()) - lhs.duplicateSharedArrays(); - MergeArrayVisitor merger; - + osg::VertexBufferObject* vbo = nullptr; unsigned int base = 0; if (lhs.getVertexArray() && rhs.getVertexArray()) { - base = lhs.getVertexArray()->getNumElements(); + if (lhs.getVertexArray()->referenceCount() > 1) + lhs.setVertexArray(cloneArray(lhs.getVertexArray(), vbo, &lhs)); if (!merger.merge(lhs.getVertexArray(),rhs.getVertexArray())) { OSG_DEBUG << "MergeGeometry: vertex array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getNormalArray()->referenceCount() > 1) + lhs.setNormalArray(cloneArray(lhs.getNormalArray(), vbo, &lhs)); if (!merger.merge(lhs.getNormalArray(),rhs.getNormalArray())) { OSG_DEBUG << "MergeGeometry: normal array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getColorArray()->referenceCount() > 1) + lhs.setColorArray(cloneArray(lhs.getColorArray(), vbo, &lhs)); if (!merger.merge(lhs.getColorArray(),rhs.getColorArray())) { OSG_DEBUG << "MergeGeometry: color array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getSecondaryColorArray()->referenceCount() > 1) + lhs.setSecondaryColorArray(cloneArray(lhs.getSecondaryColorArray(), vbo, &lhs)); if (!merger.merge(lhs.getSecondaryColorArray(),rhs.getSecondaryColorArray())) { OSG_DEBUG << "MergeGeometry: secondary color array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getFogCoordArray()->referenceCount() > 1) + lhs.setFogCoordArray(cloneArray(lhs.getFogCoordArray(), vbo, &lhs)); if (!merger.merge(lhs.getFogCoordArray(),rhs.getFogCoordArray())) { OSG_DEBUG << "MergeGeometry: fog coord array not merged. Some data may be lost." <referenceCount() > 1) + lhs.setTexCoordArray(unit, cloneArray(lhs.getTexCoordArray(unit), vbo, &lhs)); if (!merger.merge(lhs.getTexCoordArray(unit),rhs.getTexCoordArray(unit))) { OSG_DEBUG << "MergeGeometry: tex coord array not merged. Some data may be lost." <referenceCount() > 1) + lhs.setVertexAttribArray(unit, cloneArray(lhs.getVertexAttribArray(unit), vbo, &lhs)); if (!merger.merge(lhs.getVertexAttribArray(unit),rhs.getVertexAttribArray(unit))) { OSG_DEBUG << "MergeGeometry: vertex attrib array not merged. Some data may be lost." <getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; @@ -1696,13 +1758,18 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom { // must promote to a DrawElementsUShort osg::DrawElementsUShort* new_primitive = new osg::DrawElementsUShort(primitive->getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; } else { - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); } } @@ -1722,13 +1789,18 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom { // must promote to a DrawElementsUInt osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUShort->begin(),primitiveUShort->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; } else { - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); } } @@ -1738,7 +1810,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType): case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType): default: - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); break; } diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 1dc62cfb8..5f80b9d36 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -34,7 +34,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T } -osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) +osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) { ChunkId id = std::make_tuple(center, lod, lodFlags); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); @@ -42,7 +42,7 @@ osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f &cen return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, lod, lodFlags); + osg::ref_ptr node = createChunk(size, center, lod, lodFlags, compile); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -160,7 +160,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, unsigned char lod, unsigned int lodFlags) +osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags, bool compile) { osg::ref_ptr positions (new osg::Vec3Array); osg::ref_ptr normals (new osg::Vec3Array); @@ -221,7 +221,7 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts); - if (mSceneManager->getIncrementalCompileOperation()) + if (compile && mSceneManager->getIncrementalCompileOperation()) { mSceneManager->getIncrementalCompileOperation()->add(geometry); } diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index d6f4dd98e..e323da6a4 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -35,7 +35,7 @@ namespace Terrain public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile); void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; } void setCompositeMapLevel(float level) { mCompositeMapLevel = level; } @@ -53,7 +53,7 @@ namespace Terrain virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } private: - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool compile); osg::ref_ptr createCompositeMapRTT(); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 842dced20..d252149b2 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -53,9 +53,10 @@ namespace Terrain class DefaultLodCallback : public LodCallback { public: - DefaultLodCallback(float factor, float minSize) + DefaultLodCallback(float factor, float minSize, const osg::Vec4i& grid) : mFactor(factor) , mMinSize(minSize) + , mActiveGrid(grid) { } @@ -64,20 +65,27 @@ public: int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); - if (node->getSize()>1 && dist < (8192+1024)*1.41421356237) + if (node->getSize()>1) { + float halfSize = node->getSize()/2; + const osg::Vec2f& center = node->getCenter(); + osg::Vec4i nodeBounds (static_cast(center.x() - halfSize), static_cast(center.y() - halfSize), static_cast(center.x() + halfSize), static_cast(center.y() + halfSize)); + bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) <= std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) <= std::min(nodeBounds.w(), mActiveGrid.w())); // to prevent making chunks who will cross the activegrid border - return false; + if (intersects) + return false; } - return nativeLodLevel <= lodLevel; } private: float mFactor; float mMinSize; + osg::Vec4i mActiveGrid; }; +const float MIN_SIZE = 1/8.f; + class RootNode : public QuadTreeNode { public: @@ -297,7 +305,7 @@ unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewD return lodFlags; } -void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector& chunkManagers) +void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector& chunkManagers, bool compile) { if (!vd->hasChanged() && entry.mRenderingNode) return; @@ -325,7 +333,7 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, f for (QuadTreeWorld::ChunkManager* m : chunkManagers) { - osg::Node* n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint()); + osg::ref_ptr n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint(), compile); if (n) pat->addChild(n); } entry.mRenderingNode = pat; @@ -411,6 +419,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) { static ViewData sIntersectionViewData; vd = &sIntersectionViewData; + vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. } if (needsUpdate) @@ -430,7 +439,10 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5,y+0.5)); } else - mRootNode->traverseNodes(vd, cv->getViewPoint(), mLodCallback, mViewDistance); + { + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); + mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); + } } else { @@ -458,16 +470,13 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers, false); entry.mRenderingNode->accept(nv); } if (isCullVisitor) updateWaterCullingView(mHeightCullCallback, vd, static_cast(&nv), mStorage->getCellWorldSize(), !isGridEmpty()); - if (!isCullVisitor) - vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. - vd->markUnchanged(); double referenceTime = nv.getFrameStamp() ? nv.getFrameStamp()->getReferenceTime() : 0.0; @@ -484,9 +493,7 @@ void QuadTreeWorld::ensureQuadTreeBuilt() if (mQuadTreeBuilt) return; - const float minSize = 1/8.f; - mLodCallback = new DefaultLodCallback(mLodFactor, minSize); - QuadTreeBuilder builder(mStorage, minSize); + QuadTreeBuilder builder(mStorage, MIN_SIZE); builder.build(); mRootNode = builder.getRootNode(); @@ -521,7 +528,7 @@ void QuadTreeWorld::cacheCell(View *view, int x, int y) for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); } } @@ -537,14 +544,15 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); vd->setActiveGrid(grid); - mRootNode->traverseNodes(vd, viewPoint, mLodCallback, mViewDistance); + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, grid); + mRootNode->traverseNodes(vd, viewPoint, &lodCallback, mViewDistance); const float cellWorldSize = mStorage->getCellWorldSize(); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); } vd->markUnchanged(); } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 7634ea868..201054852 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -47,7 +47,7 @@ namespace Terrain { public: virtual ~ChunkManager(){} - virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) = 0; + virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) = 0; virtual unsigned int getNodeMask() { return 0; } }; void addChunkManager(ChunkManager*); @@ -58,7 +58,6 @@ namespace Terrain osg::ref_ptr mRootNode; osg::ref_ptr mViewDataMap; - osg::ref_ptr mLodCallback; std::vector mChunkManagers; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 4398d1a14..5f99cd97e 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -58,7 +58,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu } else { - osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f()); + osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f(), true); if (!node) return nullptr; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 2653f2fc4..ec5e87016 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -109,8 +109,8 @@ max composite geometry size = 4.0 # Load far objects on terrain object paging = true -# Turn off to save memory but worse FPS -object paging merge geometry = true +# Affects the likelyhood of objects being merged. A higher value means merging is more likely and improves FPS at the cost of memory. +object paging merge factor = 1500 # Cull objects smaller than this size divided by distance object paging min size = 0.01 From cf439581e1e84bb1a6a3a7ae0ef7ddba1dc3887c Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sun, 3 May 2020 13:37:00 +0000 Subject: [PATCH 16/81] comply by elsid review Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 12 +++++++----- apps/openmw/mwrender/objectpaging.hpp | 2 -- components/resource/scenemanager.cpp | 10 ++++++++-- components/terrain/chunkmanager.hpp | 2 -- components/terrain/quadtreeworld.cpp | 2 +- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index b529336eb..560edfcb4 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -45,7 +45,7 @@ namespace MWRender } } - const std::string& getModel(int type, const std::string& id, const MWWorld::ESMStore& store) + std::string getModel(int type, const std::string& id, const MWWorld::ESMStore& store) { switch (type) { @@ -57,7 +57,8 @@ namespace MWRender return store.get().searchStatic(id)->mModel; case ESM::REC_CONT: return store.get().searchStatic(id)->mModel; - default: throw std::exception(); + default: + return std::string(); } } @@ -340,7 +341,7 @@ namespace MWRender auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { - const_cast(cnode.get())->accept(analyzeVisitor); + const_cast(cnode.get())->accept(analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); } emplaced.first->second.mInstances.push_back(&ref); @@ -410,8 +411,9 @@ namespace MWRender group->getBound(); group->setNodeMask(Mask_Static); - - group->getOrCreateUserDataContainer()->addUserObject(templateRefs); + osg::UserDataContainer* udc = group->getOrCreateUserDataContainer(); + udc->addUserObject(templateRefs); + udc->addUserObject(mergeGroup); // for ICO ref counting return group; } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 16de6e8bc..56f393c7b 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -31,8 +31,6 @@ namespace MWRender osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile); - virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } - virtual unsigned int getNodeMask() override; void enableObject(const ESM::RefNum & refnum, bool enabled); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 1709a3d14..f4a605bca 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -717,8 +717,14 @@ namespace Resource if (mIncrementalCompileOperation) { OpenThreads::ScopedLock lock(*mIncrementalCompileOperation->getToCompiledMutex()); - while (mIncrementalCompileOperation->getToCompile().size() > 1000) - mIncrementalCompileOperation->getToCompile().pop_front(); + osgUtil::IncrementalCompileOperation::CompileSets& sets = mIncrementalCompileOperation->getToCompile(); + for(osgUtil::IncrementalCompileOperation::CompileSets::iterator it = sets.begin(); it != sets.end();) + { + if ((*it)->_subgraphToCompile->referenceCount() <= 2) + it = sets.erase(it); + else + ++it; + } } } diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index e323da6a4..87770fafb 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -50,8 +50,6 @@ namespace Terrain void releaseGLObjects(osg::State* state) override; - virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } - private: osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool compile); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index d252149b2..69291f6d8 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -325,7 +325,7 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, f if (!entry.mRenderingNode) { - auto pat = new SceneUtil::PositionAttitudeTransform; + osg::ref_ptr pat = new SceneUtil::PositionAttitudeTransform; pat->setPosition(osg::Vec3f(entry.mNode->getCenter().x()*cellWorldSize, entry.mNode->getCenter().y()*cellWorldSize, 0.f)); const osg::Vec2f& center = entry.mNode->getCenter(); From 1f891ca46def28a9d6cc4eab51676476cef4f6e2 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 4 May 2020 13:37:00 +0000 Subject: [PATCH 17/81] billboarding support for tree mods Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 87 ++++++++++++++++++++++----- components/terrain/world.cpp | 2 + 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 560edfcb4..6393bfc76 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -94,14 +94,16 @@ namespace MWRender class CopyOp : public osg::CopyOp { public: - CopyOp(bool deep) : mDistance(0.f) { + CopyOp(bool deep) : mSqrDistance(0.f) { unsigned int flags = osg::CopyOp::DEEP_COPY_NODES; if (deep) flags |= osg::CopyOp::DEEP_COPY_DRAWABLES; setCopyFlags(flags); } - float mDistance; + float mSqrDistance; + osg::Vec3f mViewVector; + mutable std::vector mNodePath; virtual osg::Node* operator() (const osg::Node* node) const { @@ -123,7 +125,7 @@ namespace MWRender { osg::Group* n = new osg::Group; for (unsigned int i=0; igetNumChildren(); ++i) - if (lod->getMinRange(i) <= mDistance && mDistance < lod->getMaxRange(i)) + if (lod->getMinRange(i) * lod->getMinRange(i) <= mSqrDistance && mSqrDistance < lod->getMaxRange(i) * lod->getMaxRange(i)) n->addChild(operator()(lod->getChild(i))); n->setDataVariance(osg::Object::STATIC); return n; @@ -132,11 +134,64 @@ namespace MWRender if (const osg::Drawable* d = node->asDrawable()) return operator()(d); - osg::Node* n = osg::clone(node, *this); - n->setDataVariance(osg::Object::STATIC); - n->setUserDataContainer(nullptr); - n->setName(""); - return n; + mNodePath.push_back(node); + + osg::Node* cloned = osg::clone(node, *this); + cloned->setDataVariance(osg::Object::STATIC); + cloned->setUserDataContainer(nullptr); + cloned->setName(""); + + mNodePath.pop_back(); + + handleCallbacks(node, cloned); + + return cloned; + } + void handleCallbacks(const osg::Node* node, osg::Node *cloned) const + { + const osg::Callback* callback = node->getCullCallback(); + while (callback) + { + if (callback->className() == std::string("BillboardCallback")) + handleBillboard(cloned); + callback = callback->getNestedCallback(); + } + } + void handleBillboard(osg::Node* node) const + { + osg::Transform* transform = node->asTransform(); + if (!transform) return; + osg::MatrixTransform* matrixTransform = transform->asMatrixTransform(); + if (!matrixTransform) return; + + osg::Matrix worldToLocal = osg::Matrix::identity(); + for (auto node : mNodePath) + if (const osg::Transform* t = node->asTransform()) + t->computeWorldToLocalMatrix(worldToLocal, nullptr); + worldToLocal = osg::Matrix::orthoNormal(worldToLocal); + + osg::Matrix billboardMatrix; + osg::Vec3f viewVector = -(mViewVector + worldToLocal.getTrans()); + viewVector.normalize(); + osg::Vec3f right = viewVector ^ osg::Vec3f(0,0,1); + right.normalize(); + osg::Vec3f up = right ^ viewVector; + up.normalize(); + billboardMatrix.makeLookAt(osg::Vec3f(0,0,0), viewVector, up); + billboardMatrix.invert(billboardMatrix); + + const osg::Matrix& oldMatrix = matrixTransform->getMatrix(); + float mag[3]; // attempt to preserve scale + for (int i=0;i<3;++i) + mag[i] = std::sqrt(oldMatrix(0,i) * oldMatrix(0,i) + oldMatrix(1,i) * oldMatrix(1,i) + oldMatrix(2,i) * oldMatrix(2,i)); + osg::Matrix newMatrix; + worldToLocal.setTrans(0,0,0); + newMatrix *= worldToLocal; + newMatrix.preMult(billboardMatrix); + newMatrix.preMultScale(osg::Vec3f(mag[0], mag[1], mag[2])); + newMatrix.setTrans(oldMatrix.getTrans()); + + matrixTransform->setMatrix(newMatrix); } virtual osg::Drawable* operator() (const osg::Drawable* drawable) const { @@ -367,12 +422,6 @@ namespace MWRender { const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); - float d = (viewPoint - pos).length(); - - CopyOp co = CopyOp(merge); - co.mDistance = d; - osg::ref_ptr node = osg::clone(cnode, co); - node->setUserDataContainer(nullptr); osg::Matrixf matrix; matrix.preMultTranslate(pos - worldCenter); @@ -381,9 +430,17 @@ namespace MWRender osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); osg::ref_ptr trans = new osg::MatrixTransform(matrix); - trans->addChild(node); trans->setDataVariance(osg::Object::STATIC); + CopyOp co = CopyOp(merge); + co.mNodePath.push_back(trans); + co.mSqrDistance = (viewPoint - pos).length2(); + co.mViewVector = (viewPoint - worldCenter); + osg::ref_ptr node = osg::clone(cnode, co); + node->setUserDataContainer(nullptr); + + trans->addChild(node); + if (merge) mergeGroup->addChild(trans); else diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index b57fa1cef..fcacb442a 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -23,6 +23,8 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); + mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); + osg::ref_ptr material (new osg::Material); material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); From 69514dfd46ede1f2b561e0ffcbe85432c3866016 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 4 May 2020 13:37:00 +0000 Subject: [PATCH 18/81] ico redundency fix + stats counter Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 17 +++++++++++++---- apps/openmw/mwrender/objectpaging.hpp | 2 ++ components/resource/scenemanager.cpp | 7 ++++++- components/resource/stats.cpp | 1 + 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 6393bfc76..28644cecf 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -463,14 +463,17 @@ namespace MWRender group->addChild(mergeGroup); auto ico = mSceneManager->getIncrementalCompileOperation(); - if (compile && ico) ico->add(mergeGroup); + if (compile && ico) + { + auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(mergeGroup); + ico->add(compileSet); + compileSet->_subgraphToCompile = group; // for ref counting in SceneManager::updateCache + } } group->getBound(); group->setNodeMask(Mask_Static); - osg::UserDataContainer* udc = group->getOrCreateUserDataContainer(); - udc->addUserObject(templateRefs); - udc->addUserObject(mergeGroup); // for ICO ref counting + group->getOrCreateUserDataContainer()->addUserObject(templateRefs); return group; } @@ -492,4 +495,10 @@ namespace MWRender OpenThreads::ScopedLock lock(mDisabledMutex); mDisabled.clear(); } + + void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const + { + stats->setAttribute(frameNumber, "Object Chunk", mCache->getCacheSize()); + } + } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 56f393c7b..9fb3aa754 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -36,6 +36,8 @@ namespace MWRender void enableObject(const ESM::RefNum & refnum, bool enabled); void clear(); + void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; + private: Resource::SceneManager* mSceneManager; float mMergeFactor; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index f4a605bca..29b312670 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -720,8 +720,13 @@ namespace Resource osgUtil::IncrementalCompileOperation::CompileSets& sets = mIncrementalCompileOperation->getToCompile(); for(osgUtil::IncrementalCompileOperation::CompileSets::iterator it = sets.begin(); it != sets.end();) { - if ((*it)->_subgraphToCompile->referenceCount() <= 2) + int refcount = (*it)->_subgraphToCompile->referenceCount(); + if ((*it)->_subgraphToCompile->asDrawable()) refcount -= 1; // ref by CompileList. + if (refcount <= 2) // ref by ObjectCache + ref by _subgraphToCompile. + { + // no other ref = not needed anymore. it = sets.erase(it); + } else ++it; } diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 0dd52ffb6..2dda2c2df 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -292,6 +292,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) "Nif", "Keyframe", "", + "Object Chunk", "Terrain Chunk", "Terrain Texture", "Land", From 38c21163eab45050096ed7067ddabddd27a3a444 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 4 May 2020 13:37:00 +0000 Subject: [PATCH 19/81] + meshsizecache for reduce i&o stalling Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 21 +++++++++++++++++++-- apps/openmw/mwrender/objectpaging.hpp | 4 ++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 28644cecf..412123309 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -374,6 +374,17 @@ namespace MWRender continue; } + float d = (viewPoint - pos).length(); + { + OpenThreads::ScopedLock lock(mSizeCacheMutex); + SizeCache::iterator found = mSizeCache.find(pair.first); + if (found != mSizeCache.end()) + { + if (found->second < d*mMinSize) + continue; + } + } + std::string id = Misc::StringUtils::lowerCase(ref.mRefID); if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player @@ -389,9 +400,15 @@ namespace MWRender */ osg::ref_ptr cnode = mSceneManager->getTemplate(model, compile); - float d = (viewPoint - pos).length(); - if (cnode->getBound().radius() * ref.mScale < d*mMinSize) + float radius = cnode->getBound().radius() * ref.mScale; + if (radius < d*mMinSize) + { + OpenThreads::ScopedLock lock(mSizeCacheMutex); + { + mSizeCache[pair.first] = radius; + } continue; + } auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 9fb3aa754..71a0db996 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -45,6 +45,10 @@ namespace MWRender OpenThreads::Mutex mDisabledMutex; std::set mDisabled; + + OpenThreads::Mutex mSizeCacheMutex; + typedef std::map SizeCache; + SizeCache mSizeCache; }; } From 0b4226f3e27488d4f6dd3293871ffb4b27eba94a Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 5 May 2020 13:37:00 +0000 Subject: [PATCH 20/81] ico effieciency Signed-off-by: Bret Curtis --- apps/openmw/mwgui/loadingscreen.cpp | 32 ++++++++++++++------ apps/openmw/mwgui/loadingscreen.hpp | 10 ++++--- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwrender/objectpaging.cpp | 36 +++++++++++++++++------ apps/openmw/mwrender/renderingmanager.cpp | 1 - 5 files changed, 57 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index dcfe723f7..436e9c2bc 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/statemanager.hpp" @@ -29,9 +30,9 @@ namespace MWGui { - LoadingScreen::LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer) + LoadingScreen::LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer) : WindowBase("openmw_loading_screen.layout") - , mVFS(vfs) + , mResourceSystem(resourceSystem) , mViewer(viewer) , mTargetFrameRate(120.0) , mLastWallpaperChangeTime(0.0) @@ -64,9 +65,9 @@ namespace MWGui void LoadingScreen::findSplashScreens() { - const std::map& index = mVFS->getIndex(); + const std::map& index = mResourceSystem->getVFS()->getIndex(); std::string pattern = "Splash/"; - mVFS->normalizeFilename(pattern); + mResourceSystem->getVFS()->normalizeFilename(pattern); /* priority given to the left */ const std::array supported_extensions {{".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}}; @@ -171,6 +172,11 @@ namespace MWGui // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); + if (const osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) { + mOldIcoMin = ico->getMinimumTimeAvailableForGLCompileAndDeletePerFrame(); + mOldIcoMax = ico->getMaximumNumOfObjectsToCompilePerFrame(); + } + mVisible = visible; mLoadingBox->setVisible(mVisible); setVisible(true); @@ -215,6 +221,12 @@ namespace MWGui //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl; setVisible(false); + if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) + { + ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(mOldIcoMin); + ico->setMaximumNumOfObjectsToCompilePerFrame(mOldIcoMax); + } + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper); } @@ -336,7 +348,13 @@ namespace MWGui MWBase::Environment::get().getInputManager()->update(0, true, true); - //osg::Timer timer; + mResourceSystem->reportStats(mViewer->getFrameStamp()->getFrameNumber(), mViewer->getViewerStats()); + if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) + { + ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(1.f/getTargetFrameRate()); + ico->setMaximumNumOfObjectsToCompilePerFrame(1000); + } + // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() @@ -344,10 +362,6 @@ namespace MWGui mViewer->updateTraversal(); mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); - //std::cout << "frame took " << timer.time_m() << std::endl; - - //if (mViewer->getIncrementalCompileOperation()) - //std::cout << "num to compile " << mViewer->getIncrementalCompileOperation()->getToCompile().size() << std::endl; mLastRenderTime = mTimer.time_m(); } diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index c054f3bbd..1be1b3a0c 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -20,9 +20,9 @@ namespace osg class Texture2D; } -namespace VFS +namespace Resource { - class Manager; + class ResourceSystem; } namespace MWGui @@ -32,7 +32,7 @@ namespace MWGui class LoadingScreen : public WindowBase, public Loading::Listener { public: - LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer); + LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer); virtual ~LoadingScreen(); /// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details @@ -53,7 +53,7 @@ namespace MWGui void setupCopyFramebufferToTextureCallback(); - const VFS::Manager* mVFS; + Resource::ResourceSystem* mResourceSystem; osg::ref_ptr mViewer; double mTargetFrameRate; @@ -70,6 +70,8 @@ namespace MWGui size_t mProgress; bool mShowWallpaper; + float mOldIcoMin = 0.f; + unsigned int mOldIcoMax = 0; MyGUI::Widget* mLoadingBox; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ab7c3334c..e1f7a4fbf 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -230,7 +230,7 @@ namespace MWGui mKeyboardNavigation->setEnabled(keyboardNav); Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav); - mLoadingScreen = new LoadingScreen(mResourceSystem->getVFS(), mViewer); + mLoadingScreen = new LoadingScreen(mResourceSystem, mViewer); mWindows.push_back(mLoadingScreen); //set up the hardware cursor manager diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 412123309..0d547184c 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -353,6 +353,7 @@ namespace MWRender { std::vector mInstances; AnalyzeVisitor::Result mAnalyzeResult; + bool mNeedCompile = false; }; typedef std::map, InstanceList> NodeMap; NodeMap nodes; @@ -398,7 +399,7 @@ namespace MWRender if (useAnim) model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); */ - osg::ref_ptr cnode = mSceneManager->getTemplate(model, compile); + osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); float radius = cnode->getBound().radius() * ref.mScale; if (radius < d*mMinSize) @@ -415,6 +416,7 @@ namespace MWRender { const_cast(cnode.get())->accept(analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); + emplaced.first->second.mNeedCompile = compile && cnode->referenceCount() <= 3; } emplaced.first->second.mInstances.push_back(&ref); } @@ -422,19 +424,29 @@ namespace MWRender osg::ref_ptr group = new osg::Group; osg::ref_ptr mergeGroup = new osg::Group; osg::ref_ptr templateRefs = new TemplateRef; + osgUtil::StateToCompile stateToCompile(0, nullptr); for (const auto& pair : nodes) { const osg::Node* cnode = pair.first; - // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache - templateRefs->mObjects.push_back(cnode); - const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult; float mergeCost = analyzeResult.mNumVerts * size; float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; bool merge = mergeBenefit > mergeCost; + // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache + templateRefs->mObjects.push_back(cnode); + + if (pair.second.mNeedCompile) + { + int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES; + if (!merge) + mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + stateToCompile._mode = mode; + const_cast(cnode)->accept(stateToCompile); + } + for (auto cref : pair.second.mInstances) { const ESM::CellRef& ref = *cref; @@ -479,15 +491,21 @@ namespace MWRender group->addChild(mergeGroup); - auto ico = mSceneManager->getIncrementalCompileOperation(); - if (compile && ico) + if (compile) { - auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(mergeGroup); - ico->add(compileSet); - compileSet->_subgraphToCompile = group; // for ref counting in SceneManager::updateCache + stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + mergeGroup->accept(stateToCompile); } } + auto ico = mSceneManager->getIncrementalCompileOperation(); + if (!stateToCompile.empty() && ico) + { + auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(group); + compileSet->buildCompileMap(ico->getContextSet(), stateToCompile); + ico->add(compileSet, false); + } + group->getBound(); group->setNodeMask(Mask_Static); group->getOrCreateUserDataContainer()->addUserObject(templateRefs); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 41cfc56a4..a545e0674 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -260,7 +260,6 @@ namespace MWRender { mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); - mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100); } mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation()); From 8a624e5a71aa39b81e3f69586eacd336363ff198 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 5 May 2020 13:37:00 +0000 Subject: [PATCH 21/81] minsize based on mergedecision solves partial culling Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 50 +++++++++++++++++++-------- apps/openmw/mwrender/objectpaging.hpp | 2 ++ files/settings-default.cfg | 6 ++++ 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 0d547184c..5438347a1 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -285,6 +285,8 @@ namespace MWRender { mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); + mMinSizeMergeFactor = Settings::Manager::getFloat("object paging min size merge factor", "Terrain"); + mMinSizeCostMultiplier = Settings::Manager::getFloat("object paging min size cost multiplier", "Terrain"); } osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile) @@ -358,7 +360,9 @@ namespace MWRender typedef std::map, InstanceList> NodeMap; NodeMap nodes; AnalyzeVisitor analyzeVisitor; - + float minSize = mMinSize; + if (mMinSizeMergeFactor) + minSize *= mMinSizeMergeFactor; for (const auto& pair : refs) { const ESM::CellRef& ref = pair.second; @@ -381,7 +385,7 @@ namespace MWRender SizeCache::iterator found = mSizeCache.find(pair.first); if (found != mSizeCache.end()) { - if (found->second < d*mMinSize) + if (found->second < d*minSize) continue; } } @@ -402,7 +406,7 @@ namespace MWRender osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); float radius = cnode->getBound().radius() * ref.mScale; - if (radius < d*mMinSize) + if (radius < d*minSize) { OpenThreads::ScopedLock lock(mSizeCacheMutex); { @@ -435,23 +439,26 @@ namespace MWRender float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; bool merge = mergeBenefit > mergeCost; - // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache - templateRefs->mObjects.push_back(cnode); - - if (pair.second.mNeedCompile) - { - int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES; - if (!merge) - mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; - stateToCompile._mode = mode; - const_cast(cnode)->accept(stateToCompile); - } + float minSizeMerged = mMinSize; + float factor2 = mergeBenefit > 0 ? std::min(1.f, mergeCost * mMinSizeCostMultiplier / mergeBenefit) : 1; + float minSizeMergeFactor2 = (1-factor2) * mMinSizeMergeFactor + factor2; + if (minSizeMergeFactor2 > 0) + minSizeMerged *= minSizeMergeFactor2; + unsigned int numinstances = 0; for (auto cref : pair.second.mInstances) { const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); + if (minSizeMerged != minSize) + { + float d = (viewPoint - pos).length(); + float radius = cnode->getBound().radius() * cref->mScale; + if (radius < d*minSizeMerged) + continue; + } + osg::Matrixf matrix; matrix.preMultTranslate(pos - worldCenter); matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * @@ -474,6 +481,21 @@ namespace MWRender mergeGroup->addChild(trans); else group->addChild(trans); + ++numinstances; + } + if (numinstances > 0) + { + // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache + templateRefs->mObjects.push_back(cnode); + + if (pair.second.mNeedCompile) + { + int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES; + if (!merge) + mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + stateToCompile._mode = mode; + const_cast(cnode)->accept(stateToCompile); + } } } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 71a0db996..c85fe3705 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -42,6 +42,8 @@ namespace MWRender Resource::SceneManager* mSceneManager; float mMergeFactor; float mMinSize; + float mMinSizeMergeFactor; + float mMinSizeCostMultiplier; OpenThreads::Mutex mDisabledMutex; std::set mDisabled; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index ec5e87016..bb21df73e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -115,6 +115,12 @@ object paging merge factor = 1500 # Cull objects smaller than this size divided by distance object paging min size = 0.01 +# Adjusts 'min size' based on merging decision. Allows inexpensive objects to be rendered from a greater distance. +object paging min size merge factor = 0.6 + +# Controls how inexpensive an object needs to be to utilize 'min size merge factor'. +object paging min size cost multiplier = 4 + [Fog] # If true, use extended fog parameters for distant terrain not controlled by From 00e56ae8624e8b75102945efb6a2f13695f78d4a Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 5 May 2020 13:37:00 +0000 Subject: [PATCH 22/81] batch debug colours Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 29 +++++++++++++++++++++++++++ apps/openmw/mwrender/objectpaging.hpp | 1 + files/settings-default.cfg | 5 ++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 5438347a1..dc84ccdd2 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include "apps/openmw/mwworld/esmstore.hpp" #include "apps/openmw/mwbase/environment.hpp" @@ -279,10 +281,31 @@ namespace MWRender StateSetCounter mGlobalStateSetCounter; }; + class DebugVisitor : public osg::NodeVisitor + { + public: + DebugVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {} + virtual void apply(osg::Drawable& node) + { + osg::ref_ptr m (new osg::Material); + osg::Vec4f color(Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), 0.f); + color.normalize(); + m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f)); + m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f)); + m->setColorMode(osg::Material::OFF); + m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(color)); + osg::ref_ptr stateset = node.getStateSet() ? osg::clone(node.getStateSet(), osg::CopyOp::SHALLOW_COPY) : new osg::StateSet; + stateset->setAttribute(m); + stateset->addUniform(new osg::Uniform("colorMode", 0)); + node.setStateSet(stateset); + } + }; + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) { + mDebugBatches = Settings::Manager::getBool("object paging debug batches", "Terrain"); mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); mMinSizeMergeFactor = Settings::Manager::getFloat("object paging min size merge factor", "Terrain"); @@ -518,6 +541,12 @@ namespace MWRender stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; mergeGroup->accept(stateToCompile); } + + if (mDebugBatches) + { + DebugVisitor dv; + mergeGroup->accept(dv); + } } auto ico = mSceneManager->getIncrementalCompileOperation(); diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index c85fe3705..57121ae76 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -40,6 +40,7 @@ namespace MWRender private: Resource::SceneManager* mSceneManager; + bool mDebugBatches; float mMergeFactor; float mMinSize; float mMinSizeMergeFactor; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index bb21df73e..4a8eb889e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -109,7 +109,7 @@ max composite geometry size = 4.0 # Load far objects on terrain object paging = true -# Affects the likelyhood of objects being merged. A higher value means merging is more likely and improves FPS at the cost of memory. +# Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory. object paging merge factor = 1500 # Cull objects smaller than this size divided by distance @@ -121,6 +121,9 @@ object paging min size merge factor = 0.6 # Controls how inexpensive an object needs to be to utilize 'min size merge factor'. object paging min size cost multiplier = 4 +# Assign a random color to merged batches. +object paging debug batches = false + [Fog] # If true, use extended fog parameters for distant terrain not controlled by From 77b92aee9cd4b6282f5b3600b075994becc9f968 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Wed, 6 May 2020 13:37:00 +0000 Subject: [PATCH 23/81] fix shadowsglitch by bounds overflow Signed-off-by: Bret Curtis --- components/terrain/quadtreenode.cpp | 7 ------- components/terrain/quadtreenode.hpp | 2 -- components/terrain/quadtreeworld.cpp | 2 ++ files/settings-default.cfg | 4 ++-- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index a28554be9..ddb2c611b 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -171,8 +171,6 @@ void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) { mBoundingBox = boundingBox; mValidBounds = boundingBox.valid(); - dirtyBound(); - getBound(); } const osg::BoundingBox &QuadTreeNode::getBoundingBox() const @@ -180,11 +178,6 @@ const osg::BoundingBox &QuadTreeNode::getBoundingBox() const return mBoundingBox; } -osg::BoundingSphere QuadTreeNode::computeBound() const -{ - return osg::BoundingSphere(mBoundingBox); -} - float QuadTreeNode::getSize() const { return mSize; diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 4adbc6025..0d4cf7807 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -91,8 +91,6 @@ namespace Terrain const osg::BoundingBox& getBoundingBox() const; bool hasValidBounds() const { return mValidBounds; } - virtual osg::BoundingSphere computeBound() const; - /// size in cell coordinates float getSize() const; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 69291f6d8..d13a3989d 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -140,6 +140,8 @@ public: addChildren(mRootNode); mRootNode->initNeighbours(); + float cellWorldSize = mStorage->getCellWorldSize(); + mRootNode->setInitialBound(osg::BoundingSphere(osg::BoundingBox(osg::Vec3(mMinX*cellWorldSize, mMinY*cellWorldSize, 0), osg::Vec3(mMaxX*cellWorldSize, mMaxY*cellWorldSize, 0)))); } void addChildren(QuadTreeNode* parent) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 4a8eb889e..f250b68aa 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -826,8 +826,8 @@ enable debug hud = false # Enable the debug overlay to see where each shadow map affects. enable debug overlay = false -# Attempt to better use the shadow map by making them cover a smaller area. May have a minor to major performance impact. -compute tight scene bounds = true +# Attempt to better use the shadow map by making them cover a smaller area. May have a major performance impact. +compute tight scene bounds = false # How large to make the shadow map(s). Higher values increase GPU load, but can produce better-looking results. Power-of-two values may turn out to be faster on some GPU/driver combinations. shadow map resolution = 1024 From b7b31926a844d0608915826c5d9c4c9421704be2 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Wed, 6 May 2020 13:37:00 +0000 Subject: [PATCH 24/81] fix map glitch + cleanup Signed-off-by: Bret Curtis --- apps/openmw/mwrender/localmap.cpp | 8 +------ components/terrain/quadtreenode.cpp | 22 ----------------- components/terrain/quadtreenode.hpp | 3 --- components/terrain/quadtreeworld.cpp | 36 +++------------------------- components/terrain/quadtreeworld.hpp | 2 +- 5 files changed, 5 insertions(+), 66 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 416a753eb..12401f45e 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -168,11 +168,10 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax) { osg::ref_ptr camera (new osg::Camera); - camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10); camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); camera->setViewMatrixAsLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector); - camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); + camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f)); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -360,11 +359,6 @@ void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell) osg::ref_ptr camera = createOrthographicCamera(x*mMapWorldSize + mMapWorldSize/2.f, y*mMapWorldSize + mMapWorldSize/2.f, mMapWorldSize, mMapWorldSize, osg::Vec3d(0,1,0), zmin, zmax); - camera->getOrCreateUserDataContainer()->addDescription("NoTerrainLod"); - std::ostringstream stream; - stream << x << " " << y; - camera->getOrCreateUserDataContainer()->addDescription(stream.str()); - setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY()); MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())]; diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index ddb2c611b..1f5a3e4b3 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -128,28 +128,6 @@ void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodC } } -void QuadTreeNode::traverseTo(ViewData* vd, float size, const osg::Vec2f& center) -{ - if (!hasValidBounds()) - return; - - if (getCenter().x() + getSize()/2.f <= center.x() - size/2.f - || getCenter().x() - getSize()/2.f >= center.x() + size/2.f - || getCenter().y() + getSize()/2.f <= center.y() - size/2.f - || getCenter().y() - getSize()/2.f >= center.y() + size/2.f) - return; - - bool stopTraversal = (getSize() == size); - - if (stopTraversal) - vd->add(this); - else - { - for (unsigned int i=0; itraverseTo(vd, size, center); - } -} - void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector& intersector) { if (!hasValidBounds()) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 0d4cf7807..74abea362 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -100,9 +100,6 @@ namespace Terrain /// Traverse nodes according to LOD selection. void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist); - /// Traverse to a specific node and add only that node. - void traverseTo(ViewData* vd, float size, const osg::Vec2f& center); - /// Adds all leaf nodes which intersect the line from start to end void intersect(ViewData* vd, TerrainLineIntersector& intersector); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index d13a3989d..40d2c823b 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -70,7 +70,7 @@ public: float halfSize = node->getSize()/2; const osg::Vec2f& center = node->getCenter(); osg::Vec4i nodeBounds (static_cast(center.x() - halfSize), static_cast(center.y() - halfSize), static_cast(center.x() + halfSize), static_cast(center.y() + halfSize)); - bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) <= std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) <= std::min(nodeBounds.w(), mActiveGrid.w())); + bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) < std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) < std::min(nodeBounds.w(), mActiveGrid.w())); // to prevent making chunks who will cross the activegrid border if (intersects) return false; @@ -430,21 +430,8 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) if (isCullVisitor) { osgUtil::CullVisitor* cv = static_cast(&nv); - - osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer(); - if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod") - { - std::istringstream stream(udc->getDescriptions()[1]); - int x,y; - stream >> x; - stream >> y; - mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5,y+0.5)); - } - else - { - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); - mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); - } + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); + mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); } else { @@ -517,23 +504,6 @@ void QuadTreeWorld::enable(bool enabled) mRootNode->setNodeMask(enabled ? ~0 : 0); } -void QuadTreeWorld::cacheCell(View *view, int x, int y) -{ - ensureQuadTreeBuilt(); - osg::Vec4i grid (x,y,x+1,y+1); - ViewData* vd = static_cast(view); - vd->setActiveGrid(grid); - mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5f,y+0.5f)); - - const float cellWorldSize = mStorage->getCellWorldSize(); - - for (unsigned int i=0; igetNumEntries(); ++i) - { - ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); - } -} - View* QuadTreeWorld::createView() { return new ViewData; diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 201054852..15ef0634a 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -31,7 +31,7 @@ namespace Terrain virtual void setViewDistance(float distance) { mViewDistance = distance; } - void cacheCell(View *view, int x, int y); + void cacheCell(View *view, int x, int y) {} /// @note Not thread safe. virtual void loadCell(int x, int y); /// @note Not thread safe. From 4dccabeb83231557a353b22588043e32366ec062 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Wed, 6 May 2020 13:37:00 +0000 Subject: [PATCH 25/81] fix analyzation not taking instancecount in account + settings calibration Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 7 +++++++ files/settings-default.cfg | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index dc84ccdd2..d30bd7a63 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -264,6 +264,11 @@ namespace MWRender mCurrentStateSet = nullptr; return result; } + void addInstance(const Result& result) + { + for (auto pair : result.mStateSetCounter) + mGlobalStateSetCounter[pair.first] += pair.second; + } float getMergeBenefit(const Result& result) { if (result.mStateSetCounter.empty()) return 1; @@ -445,6 +450,8 @@ namespace MWRender emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); emplaced.first->second.mNeedCompile = compile && cnode->referenceCount() <= 3; } + else + analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult); emplaced.first->second.mInstances.push_back(&ref); } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index f250b68aa..b8c151e56 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -110,16 +110,16 @@ max composite geometry size = 4.0 object paging = true # Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory. -object paging merge factor = 1500 +object paging merge factor = 250 # Cull objects smaller than this size divided by distance object paging min size = 0.01 # Adjusts 'min size' based on merging decision. Allows inexpensive objects to be rendered from a greater distance. -object paging min size merge factor = 0.6 +object paging min size merge factor = 0.3 # Controls how inexpensive an object needs to be to utilize 'min size merge factor'. -object paging min size cost multiplier = 4 +object paging min size cost multiplier = 25 # Assign a random color to merged batches. object paging debug batches = false From da92ad329b23c91f4baa7d36b00e7b59a374d25b Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Thu, 7 May 2020 13:37:00 +0000 Subject: [PATCH 26/81] move renderbin Signed-off-by: Bret Curtis --- components/terrain/chunkmanager.cpp | 9 ++++++++- components/terrain/chunkmanager.hpp | 2 ++ components/terrain/material.cpp | 25 +++++++++++++------------ components/terrain/terraindrawable.cpp | 6 ++++++ components/terrain/world.cpp | 7 ------- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 5f80b9d36..3c3bd0f9d 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -31,7 +32,11 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T , mCompositeMapLevel(1.f) , mMaxCompGeometrySize(1.f) { - + mMultiPassRoot = new osg::StateSet; + mMultiPassRoot->setRenderingHint(osg::StateSet::OPAQUE_BIN); + osg::ref_ptr material (new osg::Material); + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + mMultiPassRoot->setAttributeAndModes(material, osg::StateAttribute::ON); } osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) @@ -196,6 +201,8 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->createClusterCullingCallback(); + geometry->setStateSet(mMultiPassRoot); + if (useCompositeMap) { osg::ref_ptr compositeMap = new CompositeMap; diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 87770fafb..11e5769de 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -65,6 +65,8 @@ namespace Terrain CompositeMapRenderer* mCompositeMapRenderer; BufferCache mBufferCache; + osg::ref_ptr mMultiPassRoot; + unsigned int mNodeMask; unsigned int mCompositeMapSize; diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index fd385e793..e662f4439 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -183,17 +183,20 @@ namespace Terrain osg::ref_ptr stateset (new osg::StateSet); - stateset->setMode(GL_BLEND, osg::StateAttribute::ON); - - if (!firstLayer) + if (!blendmaps.empty()) { - stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON); - stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON); - } - else - { - stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON); - stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); + stateset->setRenderBinDetails(passIndex++, "RenderBin"); + if (!firstLayer) + { + stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON); + } + else + { + stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); + } } int texunit = 0; @@ -268,8 +271,6 @@ namespace Terrain } - stateset->setRenderBinDetails(passIndex++, "RenderBin"); - passes.push_back(stateset); } return passes; diff --git a/components/terrain/terraindrawable.cpp b/components/terrain/terraindrawable.cpp index 9593687cf..0d82be4ff 100644 --- a/components/terrain/terraindrawable.cpp +++ b/components/terrain/terraindrawable.cpp @@ -102,6 +102,10 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) bool pushedLight = mLightListCallback && mLightListCallback->pushLightState(this, cv); + osg::StateSet* stateset = getStateSet(); + if (stateset) + cv->pushStateSet(stateset); + for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it) { cv->pushStateSet(*it); @@ -109,6 +113,8 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) cv->popStateSet(); } + if (stateset) + cv->popStateSet(); if (pushedLight) cv->popStateSet(); } diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index fcacb442a..5b4807b38 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -1,7 +1,6 @@ #include "world.hpp" #include -#include #include #include @@ -23,12 +22,6 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); - mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); - - osg::ref_ptr material (new osg::Material); - material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); - mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); - mTerrainRoot->setName("Terrain Root"); osg::ref_ptr compositeCam = new osg::Camera; From ffbed7ee38dc7fe7afb1a85a98bef241f83d69a5 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Thu, 7 May 2020 13:37:00 +0000 Subject: [PATCH 27/81] loadingscreen Signed-off-by: Bret Curtis --- apps/openmw/mwworld/cellpreloader.cpp | 52 +++++++++++++++++++++++---- apps/openmw/mwworld/cellpreloader.hpp | 3 ++ apps/openmw/mwworld/scene.cpp | 28 +++++++++++++++ apps/openmw/mwworld/scene.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 3 +- components/terrain/quadtreeworld.cpp | 8 +++-- components/terrain/quadtreeworld.hpp | 2 +- components/terrain/world.hpp | 2 +- 8 files changed, 88 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 60b9a3220..8f61673b3 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -174,6 +174,8 @@ namespace MWWorld public: TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) : mAbort(false) + , mProgress(views.size()) + , mProgressRange(0) , mTerrainViews(views) , mWorld(world) , mPreloadPositions(preloadPositions) @@ -191,7 +193,7 @@ namespace MWWorld for (unsigned int i=0; ireset(); - mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort); + mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort, mProgress[i], mProgressRange); } } @@ -200,8 +202,13 @@ namespace MWWorld mAbort = true; } + int getProgress() const { return !mProgress.empty() ? mProgress[0].load() : 0; } + int getProgressRange() const { return !mProgress.empty() && mProgress[0].load() ? mProgressRange : 0; } + private: std::atomic mAbort; + std::vector> mProgress; + int mProgressRange; std::vector > mTerrainViews; Terrain::World* mWorld; std::vector mPreloadPositions; @@ -328,9 +335,6 @@ namespace MWWorld } mPreloadCells.erase(found); - - if (cell->isExterior() && mTerrainPreloadItem && mTerrainPreloadItem->isDone()) - mTerrainPreloadItem->storeViews(0.0); } } @@ -415,6 +419,38 @@ namespace MWWorld mUnrefQueue = unrefQueue; } + bool CellPreloader::getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp) + { + if (!mTerrainPreloadItem) + return false; + else if (mTerrainPreloadItem->isDone()) + { + mTerrainPreloadItem->storeViews(timestamp); + mTerrainPreloadItem = nullptr; + return false; + } + else + { + progress = mTerrainPreloadItem->getProgress(); + progressRange = mTerrainPreloadItem->getProgressRange(); + return !progress || progress < progressRange; + } + } + + void CellPreloader::abortTerrainPreloadExcept(const osg::Vec3f& exceptPos) + { + if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) + { + const float resetThreshold = ESM::Land::REAL_SIZE; + for (auto pos : mTerrainPreloadPositions) + if ((pos.first-exceptPos).length2() < resetThreshold*resetThreshold) + return; + mTerrainPreloadItem->abort(); + mTerrainPreloadItem->waitTillDone(); + mTerrainPreloadItem = nullptr; + } + } + void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) @@ -436,8 +472,12 @@ namespace MWWorld } mTerrainPreloadPositions = positions; - mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); - mWorkQueue->addWorkItem(mTerrainPreloadItem); + + if (!positions.empty()) + { + mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); + mWorkQueue->addWorkItem(mTerrainPreloadItem); + } } } diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index ef2817019..386fee293 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -72,6 +72,9 @@ namespace MWWorld typedef std::pair PositionCellGrid; void setTerrainPreloadPositions(const std::vector& positions); + bool getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp); + void abortTerrainPreloadExcept(const osg::Vec3f& exceptPos); + private: Resource::ResourceSystem* mResourceSystem; Resource::BulletShapeManager* mBulletShapeManager; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 1dac18eaa..f8c16ca37 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -849,15 +849,42 @@ namespace MWWorld if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); + preloadTerrain(position.asVec3()); + changeCellGrid(x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); + checkTerrainLoaded(); + if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } + void Scene::checkTerrainLoaded() + { + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); + int progress = 0, initialProgress = -1, progressRange = 0; + while (mPreloader->getTerrainPreloadInProgress(progress, progressRange, mRendering.getReferenceTime())) + { + if (initialProgress == -1) + { + loadingListener->setLabel("#{sLoadingMessage4}"); + initialProgress = progress; + } + if (progress) + { + loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); + loadingListener->setProgress(progress-initialProgress); + } + else + loadingListener->setProgress(0); + OpenThreads::Thread::microSleep(5000); + } + } + CellStore* Scene::getCurrentCell () { return mCurrentCell; @@ -1102,6 +1129,7 @@ namespace MWWorld void Scene::preloadTerrain(const osg::Vec3f &pos) { + mPreloader->abortTerrainPreloadExcept(pos); std::vector vec; vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); mPreloader->setTerrainPreloadPositions(vec); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 9b8403c38..349dce423 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -101,6 +101,8 @@ namespace MWWorld void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + void checkTerrainLoaded(); + osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0817a4226..5f4f9d1cd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -243,7 +243,6 @@ namespace MWWorld if (bypass && !mStartCell.empty()) { ESM::Position pos; - if (findExteriorPosition (mStartCell, pos)) { changeToExteriorCell (pos, true); @@ -384,9 +383,9 @@ namespace MWWorld mPlayer->readRecord(reader, type); if (getPlayerPtr().isInCell()) { - mWorldScene->preloadCell(getPlayerPtr().getCell(), true); if (getPlayerPtr().getCell()->isExterior()) mWorldScene->preloadTerrain(getPlayerPtr().getRefData().getPosition().asVec3()); + mWorldScene->preloadCell(getPlayerPtr().getCell(), true); } break; default: diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 40d2c823b..cdb9e6191 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -509,7 +509,7 @@ View* QuadTreeWorld::createView() return new ViewData; } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort, std::atomic &progress, int& progressTotal) { ensureQuadTreeBuilt(); @@ -519,12 +519,16 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, grid); mRootNode->traverseNodes(vd, viewPoint, &lodCallback, mViewDistance); - const float cellWorldSize = mStorage->getCellWorldSize(); + if (!progressTotal) + for (unsigned int i=0; igetNumEntries(); ++i) + progressTotal += vd->getEntry(i).mNode->getSize(); + const float cellWorldSize = mStorage->getCellWorldSize(); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); + progress += entry.mNode->getSize(); } vd->markUnchanged(); } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 15ef0634a..97fcd004d 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -38,7 +38,7 @@ namespace Terrain virtual void unloadCell(int x, int y); View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort); + void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); void storeView(const View* view, double referenceTime); void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index e55932169..dba79994a 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -147,7 +147,7 @@ namespace Terrain /// @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& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort) {} + virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange) {} /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// @note Not thread safe. From c1ebd9474bdafb7c252428c83927802eac09b269 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 8 May 2020 13:37:00 +0000 Subject: [PATCH 28/81] stop navmesh updates when ai off Signed-off-by: Bret Curtis --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 8 ++++++++ components/detournavigator/navigator.hpp | 5 +++++ components/detournavigator/navigatorimpl.cpp | 8 ++++++++ components/detournavigator/navigatorimpl.hpp | 3 +++ components/detournavigator/navigatorstub.hpp | 2 ++ 5 files changed, 26 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5f5e7d13b..6b175239e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include #include "../mwworld/esmstore.hpp" @@ -900,6 +902,12 @@ namespace MWMechanics bool MechanicsManager::toggleAI() { mAI = !mAI; + + MWBase::World* world = MWBase::Environment::get().getWorld(); + world->getNavigator()->setUpdatesEnabled(mAI); + if (mAI) + world->getNavigator()->update(world->getPlayerPtr().getRefData().getPosition().asVec3()); + return mAI; } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index cfdf92232..fa2faf383 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -155,6 +155,11 @@ namespace DetourNavigator */ virtual void update(const osg::Vec3f& playerPosition) = 0; + /** + * @brief disable navigator updates + */ + virtual void setUpdatesEnabled(bool enabled) = 0; + /** * @brief wait locks thread until all tiles are updated from last update call. */ diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index d7889629e..c47cf9766 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -12,6 +12,7 @@ namespace DetourNavigator NavigatorImpl::NavigatorImpl(const Settings& settings) : mSettings(settings) , mNavMeshManager(mSettings) + , mUpdatesEnabled(true) { } @@ -138,11 +139,18 @@ namespace DetourNavigator void NavigatorImpl::update(const osg::Vec3f& playerPosition) { + if (!mUpdatesEnabled) + return; removeUnusedNavMeshes(); for (const auto& v : mAgents) mNavMeshManager.update(playerPosition, v.first); } + void NavigatorImpl::setUpdatesEnabled(bool enabled) + { + mUpdatesEnabled = enabled; + } + void NavigatorImpl::wait() { mNavMeshManager.wait(); diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 66a4d8bb3..f99aab05a 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -46,6 +46,8 @@ namespace DetourNavigator void update(const osg::Vec3f& playerPosition) override; + void setUpdatesEnabled(bool enabled) override; + void wait() override; SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const override; @@ -61,6 +63,7 @@ namespace DetourNavigator private: Settings mSettings; NavMeshManager mNavMeshManager; + bool mUpdatesEnabled; std::map mAgents; std::unordered_map mAvoidIds; std::unordered_map mWaterIds; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index 9c379338f..9279e77e3 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -66,6 +66,8 @@ namespace DetourNavigator void update(const osg::Vec3f& /*playerPosition*/) override {} + void setUpdatesEnabled(bool enabled) override {} + void wait() override {} SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override From 17637c65759fd93008672dc1704dd0dd05783fea Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 8 May 2020 13:37:00 +0000 Subject: [PATCH 29/81] pagerebuild on disable Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 47 ++++++++++++++++++++--- apps/openmw/mwrender/objectpaging.hpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 6 +-- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 10 ++++- components/resource/objectcache.hpp | 4 +- components/terrain/quadtreeworld.cpp | 7 ++++ components/terrain/quadtreeworld.hpp | 1 + components/terrain/texturemanager.cpp | 2 +- components/terrain/viewdata.cpp | 20 ++++++++++ components/terrain/viewdata.hpp | 2 + components/terrain/world.hpp | 2 + 12 files changed, 91 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index d30bd7a63..eb71f0993 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -576,17 +576,52 @@ namespace MWRender return Mask_Static; } - void ObjectPaging::enableObject(const ESM::RefNum & refnum, bool enabled) + struct ClearCacheFunctor { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (enabled) mDisabled.erase(refnum); - else mDisabled.insert(refnum); + void operator()(MWRender::ChunkId id, osg::Object* obj) + { + if (intersects(id, mPosition)) + mToClear.insert(id); + } + bool intersects(ChunkId id, osg::Vec3f pos) + { + pos /= ESM::Land::REAL_SIZE; + osg::Vec2f center = std::get<0>(id); + float halfSize = std::get<1>(id)/2; + return pos.x() >= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize; + } + osg::Vec3f mPosition; + std::set mToClear; + }; + + bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) + { + if (!typeFilter(type, false)) + return false; + + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (enabled && !mDisabled.erase(refnum)) return false; + if (!enabled && !mDisabled.insert(refnum).second) return false; + } + + ClearCacheFunctor ccf; + ccf.mPosition = pos; + mCache->call(ccf); + for (auto chunk : ccf.mToClear) + mCache->removeFromObjectCache(chunk); + return true; } void ObjectPaging::clear() { - OpenThreads::ScopedLock lock(mDisabledMutex); - mDisabled.clear(); + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (mDisabled.empty()) + return; + mDisabled.clear(); + } + mCache->clear(); } void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 57121ae76..93423f21e 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -33,7 +33,9 @@ namespace MWRender virtual unsigned int getNodeMask() override; - void enableObject(const ESM::RefNum & refnum, bool enabled); + /// @return true if something changed + bool enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled); + void clear(); void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a545e0674..9d435a612 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1481,9 +1481,9 @@ namespace MWRender { mTerrain->setActiveGrid(grid); } - void RenderingManager::pagingEnableObject(const ESM::RefNum & refnum, bool enabled) + void RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) { - if (mObjectPaging) - mObjectPaging->enableObject(refnum, enabled); + if (mObjectPaging && mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getRefData().getPosition().asVec3(), enabled)) + mTerrain->clearCachedViews(ptr.getRefData().getPosition().asVec3()); } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 6bf122232..36df09202 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -241,7 +241,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); - void pagingEnableObject(const ESM::RefNum & refnum, bool enabled); + void pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); private: void updateProjectionMatrix(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5f4f9d1cd..fdb54fb75 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -815,7 +815,10 @@ namespace MWWorld mWorldScene->addObjectToScene (reference); if (reference.getCellRef().getRefNum().hasContentFile()) - mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), true); + { + int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); + mRendering->pagingEnableObject(type, reference, true); + } } } @@ -853,7 +856,10 @@ namespace MWWorld reference.getRefData().disable(); if (reference.getCellRef().getRefNum().hasContentFile()) - mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), false); + { + int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); + mRendering->pagingEnableObject(type, reference, false); + } if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->removeObjectFromScene (reference); diff --git a/components/resource/objectcache.hpp b/components/resource/objectcache.hpp index cfd41f19c..24d7d04a9 100644 --- a/components/resource/objectcache.hpp +++ b/components/resource/objectcache.hpp @@ -161,13 +161,13 @@ class GenericObjectCache : public osg::Referenced } } - /** call operator()(osg::Object*) for each object in the cache. */ + /** call operator()(KeyType, osg::Object*) for each object in the cache. */ template void call(Functor& f) { OpenThreads::ScopedLock lock(_objectCacheMutex); for (typename ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it) - f(it->second.first.get()); + f(it->first, it->second.first.get()); } /** Get the number of objects in the cache. */ diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index cdb9e6191..92bd09bd0 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -574,4 +574,11 @@ void QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m) mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask()); } +void QuadTreeWorld::clearCachedViews(const osg::Vec3f &pos) +{ +//mViewDataMap->clear(); + osg::Vec3 pos_ = pos / mStorage->getCellWorldSize(); + mViewDataMap->clearCachedViews(pos_); +} + } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 97fcd004d..7c01f5c27 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -40,6 +40,7 @@ namespace Terrain View* createView(); void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); void storeView(const View* view, double referenceTime); + void clearCachedViews(const osg::Vec3f& pos) override; void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/texturemanager.cpp b/components/terrain/texturemanager.cpp index b901fa159..c28b13f1d 100644 --- a/components/terrain/texturemanager.cpp +++ b/components/terrain/texturemanager.cpp @@ -25,7 +25,7 @@ struct UpdateTextureFilteringFunctor } Resource::SceneManager* mSceneManager; - void operator()(osg::Object* obj) + void operator()(std::string, osg::Object* obj) { mSceneManager->applyFilterSettings(static_cast(obj)); } diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 6399425a4..e5e856cad 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -1,5 +1,7 @@ #include "viewdata.hpp" +#include "quadtreenode.hpp" + namespace Terrain { @@ -99,6 +101,18 @@ bool ViewData::contains(QuadTreeNode *node) return false; } +bool intersects(const osg::Vec2f& center, float halfSize, osg::Vec3f pos) +{ + return (pos.x() >= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize); +} + +void ViewData::clearCache(const osg::Vec3f &cellPos) +{ + for (Entry& entry : mEntries) + if (entry.mNode && intersects(entry.mNode->getCenter(), entry.mNode->getSize()/2.f, cellPos)) + entry.mRenderingNode = nullptr; +} + ViewData::Entry::Entry() : mNode(nullptr) , mLodFlags(0) @@ -188,6 +202,12 @@ void ViewDataMap::clearUnusedViews(double referenceTime) } } +void ViewDataMap::clearCachedViews(const osg::Vec3f &cellPos) +{ + for (auto pair : mViews) + pair.second->clearCache(cellPos); +} + void ViewDataMap::clear() { mViews.clear(); diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 87b45f57b..f21f56ae1 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -24,6 +24,7 @@ namespace Terrain void reset(); void clear(); + void clearCache(const osg::Vec3f &cellPos); bool contains(QuadTreeNode* node); @@ -84,6 +85,7 @@ namespace Terrain ViewData* createOrReuseView(); void clearUnusedViews(double referenceTime); + void clearCachedViews(const osg::Vec3f &cellPos); void clear(); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index dba79994a..4ad4e5f0a 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -153,6 +153,8 @@ namespace Terrain /// @note Not thread safe. virtual void storeView(const View* view, double referenceTime) {} + virtual void clearCachedViews(const osg::Vec3f& pos) {} + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} virtual void setViewDistance(float distance) {} From b4af2ac6725430b9547006c87ee0daed2be73b93 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 8 May 2020 13:37:00 +0000 Subject: [PATCH 30/81] avoid blocking on pagerebuild Signed-off-by: Bret Curtis --- apps/openmw/mwrender/renderingmanager.cpp | 10 +- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/cellpreloader.cpp | 44 ++++++-- apps/openmw/mwworld/cellpreloader.hpp | 1 + apps/openmw/mwworld/scene.cpp | 5 + apps/openmw/mwworld/scene.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 6 +- components/terrain/quadtreenode.hpp | 1 - components/terrain/quadtreeworld.cpp | 20 ++-- components/terrain/quadtreeworld.hpp | 4 +- components/terrain/viewdata.cpp | 117 ++++++++++++---------- components/terrain/viewdata.hpp | 26 +++-- components/terrain/world.hpp | 4 +- 13 files changed, 151 insertions(+), 90 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9d435a612..569f40483 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1481,9 +1481,15 @@ namespace MWRender { mTerrain->setActiveGrid(grid); } - void RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) + bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) { + if (!ptr.isInCell() || !ptr.getCell()->isExterior()) + return false; if (mObjectPaging && mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getRefData().getPosition().asVec3(), enabled)) - mTerrain->clearCachedViews(ptr.getRefData().getPosition().asVec3()); + { + mTerrain->rebuildViews(); + return true; + } + return false; } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 36df09202..b45e3c8dc 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -241,7 +241,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); - void pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); + bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); private: void updateProjectionMatrix(); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 8f61673b3..4280e1e68 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -182,10 +182,12 @@ namespace MWWorld { } - void storeViews(double referenceTime) + bool storeViews(double referenceTime) { for (unsigned int i=0; istoreView(mTerrainViews[i], referenceTime); + if (!mWorld->storeView(mTerrainViews[i], referenceTime)) + return false; + return true; } virtual void doWork() @@ -244,6 +246,7 @@ namespace MWWorld , mMaxCacheSize(0) , mPreloadInstances(true) , mLastResourceCacheUpdate(0.0) + , mStoreViewsFailCount(0) { } @@ -379,7 +382,17 @@ namespace MWWorld if (mTerrainPreloadItem && mTerrainPreloadItem->isDone()) { - mTerrainPreloadItem->storeViews(timestamp); + if (!mTerrainPreloadItem->storeViews(timestamp)) + { + if (++mStoreViewsFailCount > 100) + { + OSG_ALWAYS << "paging views are rebuilt every frame, please check for faulty enable/disable scripts." << std::endl; + mStoreViewsFailCount = 0; + } + setTerrainPreloadPositions(std::vector()); + } + else + mStoreViewsFailCount = 0; mTerrainPreloadItem = nullptr; } } @@ -451,11 +464,31 @@ namespace MWWorld } } + bool contains(const std::vector& container, const std::vector& contained) + { + for (auto pos : contained) + { + bool found = false; + for (auto pos2 : container) + { + if ((pos.first-pos2.first).length2() < 1 && pos.second == pos2.second) + { + found = true; + break; + } + } + if (!found) return false; + } + return true; + } + void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { - if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) + if (positions.empty()) + mTerrainPreloadPositions.clear(); + else if (contains(mTerrainPreloadPositions, positions)) return; - else if (positions == mTerrainPreloadPositions) + if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) return; else { @@ -472,7 +505,6 @@ namespace MWWorld } mTerrainPreloadPositions = positions; - if (!positions.empty()) { mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 386fee293..c17078735 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -88,6 +88,7 @@ namespace MWWorld bool mPreloadInstances; double mLastResourceCacheUpdate; + int mStoreViewsFailCount; struct PreloadEntry { diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index f8c16ca37..36f877bee 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1135,6 +1135,11 @@ namespace MWWorld mPreloader->setTerrainPreloadPositions(vec); } + void Scene::reloadTerrain() + { + mPreloader->setTerrainPreloadPositions(std::vector()); + } + struct ListFastTravelDestinationsVisitor { ListFastTravelDestinationsVisitor(float preloadDist, const osg::Vec3f& playerPos) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 349dce423..5727f1391 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -115,6 +115,7 @@ namespace MWWorld void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); void preloadTerrain(const osg::Vec3f& pos); + void reloadTerrain(); void unloadCell (CellStoreCollection::iterator iter, bool test = false); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fdb54fb75..8392a827c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -817,7 +817,8 @@ namespace MWWorld if (reference.getCellRef().getRefNum().hasContentFile()) { int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); - mRendering->pagingEnableObject(type, reference, true); + if (mRendering->pagingEnableObject(type, reference, true)) + mWorldScene->reloadTerrain(); } } } @@ -858,7 +859,8 @@ namespace MWWorld if (reference.getCellRef().getRefNum().hasContentFile()) { int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); - mRendering->pagingEnableObject(type, reference, false); + if (mRendering->pagingEnableObject(type, reference, false)) + mWorldScene->reloadTerrain(); } if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 74abea362..147ca8c8a 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -53,7 +53,6 @@ namespace Terrain virtual bool isSufficientDetail(QuadTreeNode *node, float dist) = 0; }; - class ViewDataMap; class ViewData; class QuadTreeNode : public osg::Group diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 92bd09bd0..c7544dbe2 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -253,7 +253,6 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour QuadTreeWorld::~QuadTreeWorld() { - mViewDataMap->clear(); } /// get the level of vertex detail to render this node at, expressed relative to the native resolution of the data set. @@ -279,7 +278,7 @@ unsigned int getVertexLod(QuadTreeNode* node, int vertexLodMod) } /// get the flags to use for stitching in the index buffer so that chunks of different LOD connect seamlessly -unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewData* vd) +unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, const ViewData* vd) { unsigned int lodFlags = 0; for (unsigned int i=0; i<4; ++i) @@ -506,7 +505,7 @@ void QuadTreeWorld::enable(bool enabled) View* QuadTreeWorld::createView() { - return new ViewData; + return mViewDataMap->createIndependentView(); } void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort, std::atomic &progress, int& progressTotal) @@ -533,14 +532,9 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: vd->markUnchanged(); } -void QuadTreeWorld::storeView(const View* view, double referenceTime) +bool QuadTreeWorld::storeView(const View* view, double referenceTime) { - osg::ref_ptr dummy = new osg::DummyObject; - const ViewData* vd = static_cast(view); - bool needsUpdate = false; - ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), vd->getActiveGrid(), needsUpdate); - stored->copyFrom(*vd); - stored->setLastUsageTimeStamp(referenceTime); + return mViewDataMap->storeView(static_cast(view), referenceTime); } void QuadTreeWorld::reportStats(unsigned int frameNumber, osg::Stats *stats) @@ -574,11 +568,9 @@ void QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m) mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask()); } -void QuadTreeWorld::clearCachedViews(const osg::Vec3f &pos) +void QuadTreeWorld::rebuildViews() { -//mViewDataMap->clear(); - osg::Vec3 pos_ = pos / mStorage->getCellWorldSize(); - mViewDataMap->clearCachedViews(pos_); + mViewDataMap->rebuildViews(); } } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 7c01f5c27..0cd2526de 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -39,8 +39,8 @@ namespace Terrain View* createView(); void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); - void storeView(const View* view, double referenceTime); - void clearCachedViews(const osg::Vec3f& pos) override; + bool storeView(const View* view, double referenceTime); + void rebuildViews() override; void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index e5e856cad..8a8769000 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -10,6 +10,7 @@ ViewData::ViewData() , mLastUsageTimeStamp(0.0) , mChanged(false) , mHasViewPoint(false) + , mWorldUpdateRevision(0) { } @@ -27,6 +28,7 @@ void ViewData::copyFrom(const ViewData& other) mHasViewPoint = other.mHasViewPoint; mViewPoint = other.mViewPoint; mActiveGrid = other.mActiveGrid; + mWorldUpdateRevision = other.mWorldUpdateRevision; } void ViewData::add(QuadTreeNode *node) @@ -93,7 +95,12 @@ void ViewData::clear() mHasViewPoint = false; } -bool ViewData::contains(QuadTreeNode *node) +bool ViewData::suitableToUse(const osg::Vec4i &activeGrid) const +{ + return hasViewPoint() && activeGrid == mActiveGrid && getNumEntries(); +} + +bool ViewData::contains(QuadTreeNode *node) const { for (unsigned int i=0; i= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize); -} - -void ViewData::clearCache(const osg::Vec3f &cellPos) -{ - for (Entry& entry : mEntries) - if (entry.mNode && intersects(entry.mNode->getCenter(), entry.mNode->getSize()/2.f, cellPos)) - entry.mRenderingNode = nullptr; -} - ViewData::Entry::Entry() : mNode(nullptr) , mLodFlags(0) @@ -133,86 +128,106 @@ bool ViewData::Entry::set(QuadTreeNode *node) } } -bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist, const osg::Vec4i& activeGrid) -{ - return vd->hasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist && vd->getActiveGrid() == activeGrid; -} - ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate) { - Map::const_iterator found = mViews.find(viewer); + ViewerMap::const_iterator found = mViewers.find(viewer); ViewData* vd = nullptr; - if (found == mViews.end()) + if (found == mViewers.end()) { vd = createOrReuseView(); - mViews[viewer] = vd; + mViewers[viewer] = vd; } else vd = found->second; + needsUpdate = false; - if (!suitable(vd, viewPoint, mReuseDistance, activeGrid)) + if (!(vd->suitableToUse(activeGrid) && (vd->getViewPoint()-viewPoint).length2() < mReuseDistance*mReuseDistance && vd->getWorldUpdateRevision() >= mWorldUpdateRevision)) { - for (Map::const_iterator other = mViews.begin(); other != mViews.end(); ++other) + float shortestDist = std::numeric_limits::max(); + const ViewData* mostSuitableView = nullptr; + for (const ViewData& other : mViewVector) { - if (suitable(other->second, viewPoint, mReuseDistance, activeGrid) && other->second->getNumEntries()) + if (other.suitableToUse(activeGrid) && other.getWorldUpdateRevision() >= mWorldUpdateRevision) { - vd->copyFrom(*other->second); - needsUpdate = false; - return vd; + float dist = (viewPoint-other.getViewPoint()).length2(); + if (dist < shortestDist) + { + shortestDist = dist; + mostSuitableView = &other; + } } } + if (mostSuitableView && mostSuitableView != vd) + { + vd->copyFrom(*mostSuitableView); + return vd; + } + } + if (!vd->suitableToUse(activeGrid)) + { vd->setViewPoint(viewPoint); vd->setActiveGrid(activeGrid); needsUpdate = true; } - else - needsUpdate = false; - return vd; } +bool ViewDataMap::storeView(const ViewData* view, double referenceTime) +{ + if (view->getWorldUpdateRevision() < mWorldUpdateRevision) + return false; + ViewData* store = createOrReuseView(); + store->copyFrom(*view); + store->setLastUsageTimeStamp(referenceTime); + return true; +} + ViewData *ViewDataMap::createOrReuseView() { + ViewData* vd = nullptr; if (mUnusedViews.size()) { - ViewData* vd = mUnusedViews.front(); + vd = mUnusedViews.front(); mUnusedViews.pop_front(); - return vd; } else { mViewVector.push_back(ViewData()); - return &mViewVector.back(); + vd = &mViewVector.back(); } + vd->setWorldUpdateRevision(mWorldUpdateRevision); + return vd; +} + +ViewData *ViewDataMap::createIndependentView() const +{ + ViewData* vd = new ViewData; + vd->setWorldUpdateRevision(mWorldUpdateRevision); + return vd; } void ViewDataMap::clearUnusedViews(double referenceTime) { - for (Map::iterator it = mViews.begin(); it != mViews.end(); ) + for (ViewerMap::iterator it = mViewers.begin(); it != mViewers.end(); ) { - ViewData* vd = it->second; - if (vd->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) - { - vd->clear(); - mUnusedViews.push_back(vd); - mViews.erase(it++); - } + if (it->second->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) + mViewers.erase(it++); else ++it; } + for (ViewData& vd : mViewVector) + { + if (vd.getLastUsageTimeStamp() + mExpiryDelay < referenceTime) + { + vd.clear(); + mUnusedViews.push_back(&vd); + } + } } -void ViewDataMap::clearCachedViews(const osg::Vec3f &cellPos) +void ViewDataMap::rebuildViews() { - for (auto pair : mViews) - pair.second->clearCache(cellPos); -} - -void ViewDataMap::clear() -{ - mViews.clear(); - mUnusedViews.clear(); - mViewVector.clear(); + ++mWorldUpdateRevision; } } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index f21f56ae1..ba4fba3b2 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -23,10 +23,11 @@ namespace Terrain void reset(); - void clear(); - void clearCache(const osg::Vec3f &cellPos); + bool suitableToUse(const osg::Vec4i& activeGrid) const; - bool contains(QuadTreeNode* node); + void clear(); + + bool contains(QuadTreeNode* node) const; void copyFrom(const ViewData& other); @@ -61,6 +62,9 @@ namespace Terrain void setActiveGrid(const osg::Vec4i &grid) { if (grid != mActiveGrid) {mActiveGrid = grid;mEntries.clear();mNumEntries=0;} } const osg::Vec4i &getActiveGrid() const { return mActiveGrid;} + unsigned int getWorldUpdateRevision() const { return mWorldUpdateRevision; } + void setWorldUpdateRevision(int updateRevision) { mWorldUpdateRevision = updateRevision; } + private: std::vector mEntries; unsigned int mNumEntries; @@ -69,35 +73,39 @@ namespace Terrain osg::Vec3f mViewPoint; bool mHasViewPoint; osg::Vec4i mActiveGrid; + unsigned int mWorldUpdateRevision; }; class ViewDataMap : public osg::Referenced { public: ViewDataMap() - : mReuseDistance(300) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused. + : mReuseDistance(150) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused. // this value also serves as a threshold for when a newly loaded LOD gets unloaded again so that if you hover around an LOD transition point the LODs won't keep loading and unloading all the time. , mExpiryDelay(1.f) + , mWorldUpdateRevision(0) {} ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate); ViewData* createOrReuseView(); + ViewData* createIndependentView() const; void clearUnusedViews(double referenceTime); - void clearCachedViews(const osg::Vec3f &cellPos); - - void clear(); + void rebuildViews(); + bool storeView(const ViewData* view, double referenceTime); private: std::list mViewVector; - typedef std::map, ViewData*> Map; - Map mViews; + typedef std::map, ViewData*> ViewerMap; + ViewerMap mViewers; float mReuseDistance; float mExpiryDelay; // time in seconds for unused view to be removed + unsigned int mWorldUpdateRevision; + std::deque mUnusedViews; }; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 4ad4e5f0a..9f08454c8 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -151,9 +151,9 @@ namespace Terrain /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// @note Not thread safe. - virtual void storeView(const View* view, double referenceTime) {} + virtual bool storeView(const View* view, double referenceTime) {return true;} - virtual void clearCachedViews(const osg::Vec3f& pos) {} + virtual void rebuildViews() {} virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} From c7fda6d28004fa9be927c0f48d4cede21773d991 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sun, 10 May 2020 13:37:00 +0000 Subject: [PATCH 31/81] activegrid paging = 2xfps Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 86 +++++++++++++++++++---- apps/openmw/mwrender/objectpaging.hpp | 9 ++- apps/openmw/mwrender/renderingmanager.cpp | 5 ++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellpreloader.cpp | 4 +- apps/openmw/mwworld/cellpreloader.hpp | 2 +- apps/openmw/mwworld/scene.cpp | 48 ++++++++----- apps/openmw/mwworld/scene.hpp | 2 + components/terrain/quadtreenode.cpp | 18 ++--- components/terrain/quadtreenode.hpp | 10 ++- components/terrain/quadtreeworld.cpp | 35 +++++---- components/terrain/quadtreeworld.hpp | 1 - files/settings-default.cfg | 5 +- 13 files changed, 158 insertions(+), 68 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index eb71f0993..9b3165d87 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -32,16 +33,19 @@ namespace MWRender { - bool typeFilter(int type, bool far) + bool typeFilter(int type, bool far, bool activeGrid) { switch (type) { case ESM::REC_STAT: - case ESM::REC_ACTI: - case ESM::REC_DOOR: - return true; + return true; + + case ESM::REC_ACTI: // TODO enable when intersectionvisitor supported + case ESM::REC_DOOR: + return !activeGrid; case ESM::REC_CONT: - return far ? false : true; + return far ? false : !activeGrid; + default: return false; } @@ -64,17 +68,19 @@ namespace MWRender } } - osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) + osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) { - if (!far)return nullptr; - ChunkId id = std::make_tuple(center, size); + if (activeGrid && !mActiveGrid) + return nullptr; + + ChunkId id = std::make_tuple(center, size, activeGrid); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); if (obj) return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, viewPoint, compile); + osg::ref_ptr node = createChunk(size, center, activeGrid, viewPoint, compile); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -231,6 +237,15 @@ namespace MWRender std::vector> mObjects; }; + class RefnumSet : public osg::Object + { + public: + RefnumSet(){} + RefnumSet(const RefnumSet& copy, const osg::CopyOp&) : mRefnums(copy.mRefnums) {} + META_Object(MWRender, RefnumSet) + std::set mRefnums; + }; + class AnalyzeVisitor : public osg::NodeVisitor { public: @@ -310,6 +325,7 @@ namespace MWRender : GenericResourceManager(nullptr) , mSceneManager(sceneManager) { + mActiveGrid = Settings::Manager::getBool("object paging active grid", "Terrain"); mDebugBatches = Settings::Manager::getBool("object paging debug batches", "Terrain"); mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); @@ -317,7 +333,7 @@ namespace MWRender mMinSizeCostMultiplier = Settings::Manager::getFloat("object paging min size cost multiplier", "Terrain"); } - osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile) + osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) { osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f)); @@ -349,7 +365,7 @@ namespace MWRender { if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2)) continue; + if (!typeFilter(type,size>=2,activeGrid)) continue; if (deleted) { refs.erase(ref.mRefNum); continue; } refs[ref.mRefNum] = ref; } @@ -365,7 +381,7 @@ namespace MWRender bool deleted = it->second; if (deleted) { refs.erase(ref.mRefNum); continue; } int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2)) continue; + if (!typeFilter(type,size>=2,activeGrid)) continue; refs[ref.mRefNum] = ref; } } @@ -387,6 +403,7 @@ namespace MWRender }; typedef std::map, InstanceList> NodeMap; NodeMap nodes; + osg::ref_ptr refnumSet = activeGrid ? new RefnumSet : nullptr; AnalyzeVisitor analyzeVisitor; float minSize = mMinSize; if (mMinSizeMergeFactor) @@ -443,6 +460,9 @@ namespace MWRender continue; } + if (activeGrid && cnode->getNumChildrenRequiringUpdateTraversal() > 0) + continue; + auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { @@ -453,6 +473,9 @@ namespace MWRender else analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult); emplaced.first->second.mInstances.push_back(&ref); + + if (activeGrid) + refnumSet->mRefnums.insert(pair.first); } osg::ref_ptr group = new osg::Group; @@ -566,7 +589,13 @@ namespace MWRender group->getBound(); group->setNodeMask(Mask_Static); - group->getOrCreateUserDataContainer()->addUserObject(templateRefs); + osg::UserDataContainer* udc = group->getOrCreateUserDataContainer(); + if (activeGrid) + { + udc->addUserObject(refnumSet); + group->addCullCallback(new SceneUtil::LightListCallback); + } + udc->addUserObject(templateRefs); return group; } @@ -596,7 +625,7 @@ namespace MWRender bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) { - if (!typeFilter(type, false)) + if (!typeFilter(type, false, false)) return false; { @@ -624,6 +653,35 @@ namespace MWRender mCache->clear(); } + struct GetRefnumsFunctor + { + GetRefnumsFunctor(std::set& output) : mOutput(output) {} + void operator()(MWRender::ChunkId chunkId, osg::Object* obj) + { + if (!std::get<2>(chunkId)) return; + const osg::Vec2f& center = std::get<0>(chunkId); + bool activeGrid = (center.x() > mActiveGrid.x() || center.y() > mActiveGrid.y() || center.x() < mActiveGrid.z() || center.y() < mActiveGrid.w()); + if (!activeGrid) return; + + osg::UserDataContainer* udc = obj->getUserDataContainer(); + if (udc && udc->getNumUserObjects()) + { + RefnumSet* refnums = dynamic_cast(udc->getUserObject(0)); + if (!refnums) return; + mOutput.insert(refnums->mRefnums.begin(), refnums->mRefnums.end()); + } + } + osg::Vec4i mActiveGrid; + std::set& mOutput; + }; + + void ObjectPaging::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) + { + GetRefnumsFunctor grf(out); + grf.mActiveGrid = activeGrid; + mCache->call(grf); + } + void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const { stats->setAttribute(frameNumber, "Object Chunk", mCache->getCacheSize()); diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 93423f21e..79591dcc4 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -19,7 +19,7 @@ namespace MWWorld namespace MWRender { - typedef std::tuple ChunkId; // Center, Size + typedef std::tuple ChunkId; // Center, Size, ActiveGrid class ObjectPaging : public Resource::GenericResourceManager, public Terrain::QuadTreeWorld::ChunkManager { @@ -27,9 +27,9 @@ namespace MWRender ObjectPaging(Resource::SceneManager* sceneManager); ~ObjectPaging() = default; - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) override; + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override; - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile); virtual unsigned int getNodeMask() override; @@ -40,8 +40,11 @@ namespace MWRender void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; + void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); + private: Resource::SceneManager* mSceneManager; + bool mActiveGrid; bool mDebugBatches; float mMergeFactor; float mMinSize; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 569f40483..5523a6936 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1492,4 +1492,9 @@ namespace MWRender } return false; } + void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) + { + if (mObjectPaging) + mObjectPaging->getPagedRefnums(activeGrid, out); + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b45e3c8dc..3082866c4 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -242,6 +242,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); + void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); private: void updateProjectionMatrix(); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 4280e1e68..eb48424c5 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -450,13 +450,13 @@ namespace MWWorld } } - void CellPreloader::abortTerrainPreloadExcept(const osg::Vec3f& exceptPos) + void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid& exceptPos) { if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) { const float resetThreshold = ESM::Land::REAL_SIZE; for (auto pos : mTerrainPreloadPositions) - if ((pos.first-exceptPos).length2() < resetThreshold*resetThreshold) + if ((pos.first-exceptPos.first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos.second) return; mTerrainPreloadItem->abort(); mTerrainPreloadItem->waitTillDone(); diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index c17078735..98e173f17 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -73,7 +73,7 @@ namespace MWWorld void setTerrainPreloadPositions(const std::vector& positions); bool getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp); - void abortTerrainPreloadExcept(const osg::Vec3f& exceptPos); + void abortTerrainPreloadExcept(const PositionCellGrid &exceptPos); private: Resource::ResourceSystem* mResourceSystem; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 36f877bee..bd9a92dae 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -87,7 +88,7 @@ namespace } void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, - MWRender::RenderingManager& rendering) + MWRender::RenderingManager& rendering, std::set& pagedRefs) { if (ptr.getRefData().getBaseNode() || physics.getActor(ptr)) { @@ -104,7 +105,11 @@ namespace if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - ptr.getClass().insertObjectRendering(ptr, model, rendering); + const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); + if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end()) + ptr.getClass().insertObjectRendering(ptr, model, rendering); + else + ptr.getRefData().setBaseNode(new SceneUtil::PositionAttitudeTransform); // FIXME remove this when physics code is fixed not to depend on basenode setNodeRotation(ptr, rendering, RotationOrder::direct); ptr.getClass().insertObject (ptr, model, physics); @@ -498,18 +503,14 @@ namespace MWWorld osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter); if (newCell != mCurrentGridCenter) + { + preloadTerrain(pos); changeCellGrid(newCell.x(), newCell.y()); + } } void Scene::changeCellGrid (int playerCellX, int playerCellY, bool changeEvent) { - Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - Loading::ScopedLoad load(loadingListener); - - int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); - std::string loadingExteriorText = "#{sLoadingMessage3}"; - loadingListener->setLabel(loadingExteriorText, false, messagesCount > 0); - CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) { @@ -526,6 +527,14 @@ namespace MWWorld unloadCell (active++); } + mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); + osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); + mRendering.setActiveGrid(newGrid); + + mPagedRefs.clear(); + checkTerrainLoaded(); + mRendering.getPagedRefnums(newGrid, mPagedRefs); + std::size_t refsToLoad = 0; std::vector> cellsPositionsToLoad; // get the number of refs to load @@ -554,6 +563,11 @@ namespace MWWorld } } + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); + int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); + std::string loadingExteriorText = "#{sLoadingMessage3}"; + loadingListener->setLabel(loadingExteriorText, false, messagesCount > 0); loadingListener->setProgressRange(refsToLoad); const auto getDistanceToPlayerCell = [&] (const std::pair& cellPosition) @@ -601,9 +615,6 @@ namespace MWWorld CellStore* current = MWBase::Environment::get().getWorld()->getExterior(playerCellX, playerCellY); MWBase::Environment::get().getWindowManager()->changeCell(current); - mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); - mRendering.setActiveGrid(gridCenterToBounds(mCurrentGridCenter)); - if (changeEvent) mCellChanged = true; } @@ -820,6 +831,7 @@ namespace MWWorld loadingListener->setProgressRange(cell->count()); // Load cell. + mPagedRefs.clear(); loadCell (cell, loadingListener, changeEvent); changePlayerCell(cell, position, adjustPlayerPos); @@ -850,14 +862,12 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); preloadTerrain(position.asVec3()); - + checkTerrainLoaded(); changeCellGrid(x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); - checkTerrainLoaded(); - if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } @@ -899,7 +909,7 @@ namespace MWWorld { InsertVisitor insertVisitor (cell, *loadingListener, test); cell.forEach (insertVisitor); - insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering); }); + insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs); }); insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); }); // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order @@ -911,7 +921,7 @@ namespace MWWorld { try { - addObject(ptr, *mPhysics, mRendering); + addObject(ptr, *mPhysics, mRendering, mPagedRefs); addObject(ptr, *mPhysics, mNavigator); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); @@ -943,6 +953,8 @@ namespace MWWorld mRendering.removeObject (ptr); if (ptr.getClass().isActor()) mRendering.removeWaterRippleEmitter(ptr); + ptr.getRefData().setBaseNode(nullptr); + mPagedRefs.erase(ptr.getCellRef().getRefNum()); } bool Scene::isCellActive(const CellStore &cell) @@ -1129,9 +1141,9 @@ namespace MWWorld void Scene::preloadTerrain(const osg::Vec3f &pos) { - mPreloader->abortTerrainPreloadExcept(pos); std::vector vec; vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); + mPreloader->abortTerrainPreloadExcept(vec[0]); mPreloader->setTerrainPreloadPositions(vec); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 5727f1391..b7aeafbfe 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -88,6 +88,8 @@ namespace MWWorld osg::Vec3f mLastPlayerPos; + std::set mPagedRefs; + void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false); osg::Vec2i mCurrentGridCenter; diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 1f5a3e4b3..8f47986df 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -108,24 +108,20 @@ void QuadTreeNode::initNeighbours() getChild(i)->initNeighbours(); } -void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist) +void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback) { if (!hasValidBounds()) return; - - float dist = distance(viewPoint); - if (dist > maxDist) + LodCallback::ReturnValue lodResult = lodCallback->isSufficientDetail(this, distance(viewPoint)); + if (lodResult == LodCallback::StopTraversal) return; - - bool stopTraversal = (lodCallback->isSufficientDetail(this, dist)) || !getNumChildren(); - - if (stopTraversal) - vd->add(this); - else + else if (lodResult == LodCallback::Deeper && getNumChildren()) { for (unsigned int i=0; itraverseNodes(vd, viewPoint, lodCallback, maxDist); + getChild(i)->traverseNodes(vd, viewPoint, lodCallback); } + else + vd->add(this); } void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector& intersector) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 147ca8c8a..183b5e07a 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -50,7 +50,13 @@ namespace Terrain public: virtual ~LodCallback() {} - virtual bool isSufficientDetail(QuadTreeNode *node, float dist) = 0; + enum ReturnValue + { + Deeper, + StopTraversal, + StopTraversalAndUse + }; + virtual ReturnValue isSufficientDetail(QuadTreeNode *node, float dist) = 0; }; class ViewData; @@ -97,7 +103,7 @@ namespace Terrain const osg::Vec2f& getCenter() const; /// Traverse nodes according to LOD selection. - void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist); + void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback); /// Adds all leaf nodes which intersect the line from start to end void intersect(ViewData* vd, TerrainLineIntersector& intersector); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index c7544dbe2..78d8835b5 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -53,34 +53,40 @@ namespace Terrain class DefaultLodCallback : public LodCallback { public: - DefaultLodCallback(float factor, float minSize, const osg::Vec4i& grid) + DefaultLodCallback(float factor, float minSize, float viewDistance, const osg::Vec4i& grid) : mFactor(factor) , mMinSize(minSize) + , mViewDistance(viewDistance) , mActiveGrid(grid) { } - virtual bool isSufficientDetail(QuadTreeNode* node, float dist) + virtual ReturnValue isSufficientDetail(QuadTreeNode* node, float dist) { - int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); - int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); - + const osg::Vec2f& center = node->getCenter(); + bool activeGrid = (center.x() > mActiveGrid.x() && center.y() > mActiveGrid.y() && center.x() < mActiveGrid.z() && center.y() < mActiveGrid.w()); + if (dist > mViewDistance && !activeGrid) // for Scene<->ObjectPaging sync the activegrid must remain loaded + return StopTraversal; if (node->getSize()>1) { float halfSize = node->getSize()/2; - const osg::Vec2f& center = node->getCenter(); osg::Vec4i nodeBounds (static_cast(center.x() - halfSize), static_cast(center.y() - halfSize), static_cast(center.x() + halfSize), static_cast(center.y() + halfSize)); bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) < std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) < std::min(nodeBounds.w(), mActiveGrid.w())); // to prevent making chunks who will cross the activegrid border if (intersects) - return false; + return Deeper; } - return nativeLodLevel <= lodLevel; + + int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); + int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); + + return nativeLodLevel <= lodLevel ? StopTraversalAndUse : Deeper; } private: float mFactor; float mMinSize; + float mViewDistance; osg::Vec4i mActiveGrid; }; @@ -330,11 +336,11 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, f pat->setPosition(osg::Vec3f(entry.mNode->getCenter().x()*cellWorldSize, entry.mNode->getCenter().y()*cellWorldSize, 0.f)); const osg::Vec2f& center = entry.mNode->getCenter(); - bool far = (center.x() <= gridbounds.x() || center.y() <= gridbounds.y() || center.x() >= gridbounds.z() || center.y() >= gridbounds.w()); + bool activeGrid = (center.x() > gridbounds.x() && center.y() > gridbounds.y() && center.x() < gridbounds.z() && center.y() < gridbounds.w()); for (QuadTreeWorld::ChunkManager* m : chunkManagers) { - osg::ref_ptr n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint(), compile); + osg::ref_ptr n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, activeGrid, vd->getViewPoint(), compile); if (n) pat->addChild(n); } entry.mRenderingNode = pat; @@ -428,9 +434,8 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) vd->reset(); if (isCullVisitor) { - osgUtil::CullVisitor* cv = static_cast(&nv); - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); - mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, mActiveGrid); + mRootNode->traverseNodes(vd, nv.getViewPoint(), &lodCallback); } else { @@ -515,8 +520,8 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); vd->setActiveGrid(grid); - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, grid); - mRootNode->traverseNodes(vd, viewPoint, &lodCallback, mViewDistance); + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, grid); + mRootNode->traverseNodes(vd, viewPoint, &lodCallback); if (!progressTotal) for (unsigned int i=0; igetNumEntries(); ++i) diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 0cd2526de..7b395bff6 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -15,7 +15,6 @@ namespace Terrain { class RootNode; class ViewDataMap; - class LodCallback; /// @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) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index b8c151e56..686d65adc 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -106,9 +106,12 @@ composite map resolution = 512 # Controls the maximum size of composite geometry, should be >= 1.0. With low values there will be many small chunks, with high values - lesser count of bigger chunks. max composite geometry size = 4.0 -# Load far objects on terrain +# Use object paging for non active cells object paging = true +# Use object paging for active cells grid +object paging active grid = true + # Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory. object paging merge factor = 250 From 340d626589c661724d3c03b12117d3bd0a1825bf Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 32/81] static moving support Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 45 +++++++++++-- apps/openmw/mwrender/objectpaging.hpp | 6 +- apps/openmw/mwrender/renderingmanager.cpp | 13 +++- apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellstore.cpp | 6 ++ apps/openmw/mwworld/scene.cpp | 82 +++++++++++++---------- apps/openmw/mwworld/scene.hpp | 3 + apps/openmw/mwworld/worldimp.cpp | 29 +++++--- 8 files changed, 132 insertions(+), 53 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 9b3165d87..4c8706caf 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -389,8 +389,9 @@ namespace MWRender { OpenThreads::ScopedLock lock(mDisabledMutex); - for (auto disabled : mDisabled) - refs.erase(disabled); + if (activeGrid) + for (auto ref : mBlacklist) + refs.erase(ref); } osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); @@ -424,7 +425,9 @@ namespace MWRender continue; } + float d = (viewPoint - pos).length(); + if (!activeGrid) { OpenThreads::ScopedLock lock(mSizeCacheMutex); SizeCache::iterator found = mSizeCache.find(pair.first); @@ -450,6 +453,15 @@ namespace MWRender */ osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); + if (activeGrid) + refnumSet->mRefnums.insert(pair.first); + + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (mDisabled.count(pair.first)) + continue; + } + float radius = cnode->getBound().radius() * ref.mScale; if (radius < d*minSize) { @@ -473,9 +485,6 @@ namespace MWRender else analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult); emplaced.first->second.mInstances.push_back(&ref); - - if (activeGrid) - refnumSet->mRefnums.insert(pair.first); } osg::ref_ptr group = new osg::Group; @@ -614,6 +623,7 @@ namespace MWRender } bool intersects(ChunkId id, osg::Vec3f pos) { + if (mActiveGridOnly && !std::get<2>(id)) return false; pos /= ESM::Land::REAL_SIZE; osg::Vec2f center = std::get<0>(id); float halfSize = std::get<1>(id)/2; @@ -621,6 +631,7 @@ namespace MWRender } osg::Vec3f mPosition; std::set mToClear; + bool mActiveGridOnly = false; }; bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) @@ -642,13 +653,33 @@ namespace MWRender return true; } + bool ObjectPaging::blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos) + { + if (!typeFilter(type, false, true)) + return false; + + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (!mBlacklist.insert(refnum).second) return false; + } + + ClearCacheFunctor ccf; + ccf.mPosition = pos; + ccf.mActiveGridOnly = true; + mCache->call(ccf); + if (ccf.mToClear.empty()) return false; + for (auto chunk : ccf.mToClear) + mCache->removeFromObjectCache(chunk); + return true; + } + + void ObjectPaging::clear() { { OpenThreads::ScopedLock lock(mDisabledMutex); - if (mDisabled.empty()) - return; mDisabled.clear(); + mBlacklist.clear(); } mCache->clear(); } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 79591dcc4..4419cd86a 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -33,9 +33,12 @@ namespace MWRender virtual unsigned int getNodeMask() override; - /// @return true if something changed + /// @return true if view needs rebuild bool enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled); + /// @return true if view needs rebuild + bool blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos); + void clear(); void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; @@ -53,6 +56,7 @@ namespace MWRender OpenThreads::Mutex mDisabledMutex; std::set mDisabled; + std::set mBlacklist; OpenThreads::Mutex mSizeCacheMutex; typedef std::map SizeCache; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5523a6936..590c59302 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1483,15 +1483,24 @@ namespace MWRender } bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) { - if (!ptr.isInCell() || !ptr.getCell()->isExterior()) + if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging) return false; - if (mObjectPaging && mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getRefData().getPosition().asVec3(), enabled)) + if (mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getCellRef().getPosition().asVec3(), enabled)) { mTerrain->rebuildViews(); return true; } return false; } + void RenderingManager::pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr) + { + if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging) + return; + const ESM::RefNum & refnum = ptr.getCellRef().getRefNum(); + if (!refnum.hasContentFile()) return; + if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3())) + mTerrain->rebuildViews(); + } void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) { if (mObjectPaging) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 3082866c4..c46c2f343 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -242,6 +242,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); + void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr); void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); private: diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 6b737f202..36f568a5f 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -141,7 +141,13 @@ namespace if (iter->mRef.getRefNum()==state.mRef.mRefNum && *iter->mRef.getRefIdPtr() == state.mRef.mRefID) { // overwrite existing reference + float oldscale = iter->mRef.getScale(); iter->load (state); + const ESM::Position & oldpos = iter->mRef.getPosition(); + const ESM::Position & newpos = iter->mData.getPosition(); + const MWWorld::Ptr ptr(&*iter, cellstore); + if ((oldscale != iter->mRef.getScale() || oldpos.asVec3() != newpos.asVec3() || oldpos.rot[0] != newpos.rot[0] || oldpos.rot[1] != newpos.rot[1] || oldpos.rot[2] != newpos.rot[2]) && !ptr.getClass().isActor()) + MWBase::Environment::get().getWorld()->moveObject(ptr, newpos.pos[0], newpos.pos[1], newpos.pos[2]); if (!iter->mData.isEnabled()) { iter->mData.enable(); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index bd9a92dae..a46986616 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -87,6 +87,19 @@ namespace ); } + std::string getModel(const MWWorld::Ptr &ptr, const VFS::Manager *vfs) + { + bool useAnim = ptr.getClass().useAnim(); + std::string model = ptr.getClass().getModel(ptr); + if (useAnim) + model = Misc::ResourceHelpers::correctActorModelPath(model, vfs); + + const std::string &id = ptr.getCellRef().getRefId(); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player + return model; + } + void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering, std::set& pagedRefs) { @@ -97,13 +110,7 @@ namespace } bool useAnim = ptr.getClass().useAnim(); - std::string model = ptr.getClass().getModel(ptr); - if (useAnim) - model = Misc::ResourceHelpers::correctActorModelPath(model, rendering.getResourceSystem()->getVFS()); - - std::string id = ptr.getCellRef().getRefId(); - if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") - model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player + std::string model = getModel(ptr, rendering.getResourceSystem()->getVFS()); const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end()) @@ -188,27 +195,6 @@ namespace } } - void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, - MWRender::RenderingManager& rendering, RotationOrder order) - { - setNodeRotation(ptr, rendering, order); - physics.updateRotation(ptr); - } - - void updateObjectScale(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, - MWRender::RenderingManager& rendering) - { - if (ptr.getRefData().getBaseNode() != nullptr) - { - float scale = ptr.getCellRef().getScale(); - osg::Vec3f scaleVec (scale, scale, scale); - ptr.getClass().adjustScale(ptr, scaleVec, true); - rendering.scaleObject(ptr, scaleVec); - - physics.updateScale(ptr); - } - } - struct InsertVisitor { MWWorld::CellStore& mCell; @@ -281,23 +267,49 @@ namespace namespace MWWorld { - void Scene::updateObjectRotation(const Ptr& ptr, RotationOrder order) + void Scene::removeFromPagedRefs(const Ptr &ptr) { - ::updateObjectRotation(ptr, *mPhysics, mRendering, order); + const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); + if (refnum.hasContentFile() && mPagedRefs.erase(refnum)) + { + if (!ptr.getRefData().getBaseNode()) return; + ptr.getClass().insertObjectRendering(ptr, getModel(ptr, mRendering.getResourceSystem()->getVFS()), mRendering); + setNodeRotation(ptr, mRendering, RotationOrder::direct); + reloadTerrain(); + } + } + + void Scene::updateObjectPosition(const Ptr &ptr, const osg::Vec3f &pos, bool movePhysics) + { + mRendering.moveObject(ptr, pos); + if (movePhysics) + { + mPhysics->updatePosition(ptr); + } + } + + void Scene::updateObjectRotation(const Ptr &ptr, RotationOrder order) + { + setNodeRotation(ptr, mRendering, order); + mPhysics->updateRotation(ptr); } void Scene::updateObjectScale(const Ptr &ptr) { - ::updateObjectScale(ptr, *mPhysics, mRendering); + float scale = ptr.getCellRef().getScale(); + osg::Vec3f scaleVec (scale, scale, scale); + ptr.getClass().adjustScale(ptr, scaleVec, true); + mRendering.scaleObject(ptr, scaleVec); + mPhysics->updateScale(ptr); } void Scene::update (float duration, bool paused) { - mPreloadTimer += duration; - if (mPreloadTimer > 0.1f) + mPreloadTimer -= duration; + if (mPreloadTimer <= 0.f) { preloadCells(0.1f); - mPreloadTimer = 0.f; + mPreloadTimer = 0.1f; } mRendering.update (duration, paused); @@ -954,7 +966,6 @@ namespace MWWorld if (ptr.getClass().isActor()) mRendering.removeWaterRippleEmitter(ptr); ptr.getRefData().setBaseNode(nullptr); - mPagedRefs.erase(ptr.getCellRef().getRefNum()); } bool Scene::isCellActive(const CellStore &cell) @@ -1149,6 +1160,7 @@ namespace MWWorld void Scene::reloadTerrain() { + mPreloadTimer = 0; mPreloader->setTerrainPreloadPositions(std::vector()); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index b7aeafbfe..2fbbccf68 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -155,8 +155,11 @@ namespace MWWorld void removeObjectFromScene (const Ptr& ptr); ///< Remove an object from the scene, but not from the world model. + void removeFromPagedRefs(const Ptr &ptr); + void updateObjectRotation(const Ptr& ptr, RotationOrder order); void updateObjectScale(const Ptr& ptr); + void updateObjectPosition(const Ptr &ptr, const osg::Vec3f &pos, bool movePhysics); bool isCellActive(const CellStore &cell); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8392a827c..2975240e7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1204,20 +1204,22 @@ namespace MWWorld } if (haveToMove && newPtr.getRefData().getBaseNode()) { - mRendering->moveObject(newPtr, vec); + mWorldScene->updateObjectPosition(newPtr, vec, movePhysics); if (movePhysics) { - mPhysics->updatePosition(newPtr); - mPhysics->updatePtr(ptr, newPtr); - - if (const auto object = mPhysics->getObject(newPtr)) + if (const auto object = mPhysics->getObject(ptr)) updateNavigatorObject(object); } } + if (isPlayer) - { mWorldScene->playerMoved(vec); + else + { + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(newPtr); } + return newPtr; } @@ -1246,9 +1248,15 @@ namespace MWWorld if (mPhysics->getActor(ptr)) mNavigator->removeAgent(getPathfindingHalfExtents(ptr)); - ptr.getCellRef().setScale(scale); + if (scale != ptr.getCellRef().getScale()) + { + ptr.getCellRef().setScale(scale); + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(ptr); + } - mWorldScene->updateObjectScale(ptr); + if(ptr.getRefData().getBaseNode() != 0) + mWorldScene->updateObjectScale(ptr); if (mPhysics->getActor(ptr)) mNavigator->addAgent(getPathfindingHalfExtents(ptr)); @@ -1292,6 +1300,9 @@ namespace MWWorld ptr.getRefData().setPosition(pos); + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(ptr); + if(ptr.getRefData().getBaseNode() != 0) { const auto order = flags & MWBase::RotationFlag_inverseOrder @@ -3565,6 +3576,8 @@ namespace MWWorld std::string World::exportSceneGraph(const Ptr &ptr) { std::string file = mUserDataPath + "/openmw.osgt"; + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(ptr); mRendering->exportSceneGraph(ptr, file, "Ascii"); return file; } From 65cd2c77aa40f1e184b6231d8273651bbe43e7dd Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 33/81] static intersections Signed-off-by: Bret Curtis --- components/terrain/quadtreenode.cpp | 17 ------------- components/terrain/quadtreenode.hpp | 30 ---------------------- components/terrain/quadtreeworld.cpp | 37 +++------------------------- 3 files changed, 4 insertions(+), 80 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 8f47986df..7baea45c8 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -124,23 +124,6 @@ void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodC vd->add(this); } -void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector& intersector) -{ - if (!hasValidBounds()) - return; - - if (!intersector.intersectAndClip(getBoundingBox())) - return; - - if (getNumChildren() == 0) - vd->add(this); - else - { - for (unsigned int i=0; iintersect(vd, intersector); - } -} - void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) { mBoundingBox = boundingBox; diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 183b5e07a..a309d91fc 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -2,39 +2,12 @@ #define OPENMW_COMPONENTS_TERRAIN_QUADTREENODE_H #include -#include #include "defs.hpp" namespace Terrain { - class TerrainLineIntersector : public osgUtil::LineSegmentIntersector - { - public: - TerrainLineIntersector(osgUtil::LineSegmentIntersector* intersector, osg::Matrix& matrix) : - osgUtil::LineSegmentIntersector(intersector->getStart() * matrix, intersector->getEnd() * matrix) - { - setPrecisionHint(intersector->getPrecisionHint()); - _intersectionLimit = intersector->getIntersectionLimit(); - _parent = intersector; - } - - TerrainLineIntersector(osgUtil::LineSegmentIntersector* intersector) : - osgUtil::LineSegmentIntersector(intersector->getStart(), intersector->getEnd()) - { - setPrecisionHint(intersector->getPrecisionHint()); - _intersectionLimit = intersector->getIntersectionLimit(); - _parent = intersector; - } - - bool intersectAndClip(const osg::BoundingBox& bbInput) - { - osg::Vec3d s(_start), e(_end); - return osgUtil::LineSegmentIntersector::intersectAndClip(s, e, bbInput); - } - }; - enum ChildDirection { NW = 0, @@ -105,9 +78,6 @@ namespace Terrain /// Traverse nodes according to LOD selection. void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback); - /// Adds all leaf nodes which intersect the line from start to end - void intersect(ViewData* vd, TerrainLineIntersector& intersector); - private: QuadTreeNode* mParent; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 78d8835b5..e09433061 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -418,44 +418,15 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) return; } + osg::Object * viewer = isCullVisitor ? static_cast(&nv)->getCurrentCamera() : nullptr; bool needsUpdate = true; - ViewData* vd = nullptr; - if (isCullVisitor) - vd = mViewDataMap->getViewData(static_cast(&nv)->getCurrentCamera(), nv.getViewPoint(), mActiveGrid, needsUpdate); - else - { - static ViewData sIntersectionViewData; - vd = &sIntersectionViewData; - vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. - } + ViewData *vd = mViewDataMap->getViewData(viewer, nv.getViewPoint(), mActiveGrid, needsUpdate); if (needsUpdate) { vd->reset(); - if (isCullVisitor) - { - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, mActiveGrid); - mRootNode->traverseNodes(vd, nv.getViewPoint(), &lodCallback); - } - else - { - osgUtil::IntersectionVisitor* iv = static_cast(&nv); - osgUtil::LineSegmentIntersector* lineIntersector = dynamic_cast(iv->getIntersector()); - if (!lineIntersector) - throw std::runtime_error("Cannot update QuadTreeWorld: node visitor is not LineSegmentIntersector"); - - if (lineIntersector->getCoordinateFrame() == osgUtil::Intersector::CoordinateFrame::MODEL && iv->getModelMatrix() == 0) - { - TerrainLineIntersector terrainIntersector(lineIntersector); - mRootNode->intersect(vd, terrainIntersector); - } - else - { - osg::Matrix matrix(lineIntersector->getTransformation(*iv, lineIntersector->getCoordinateFrame())); - TerrainLineIntersector terrainIntersector(lineIntersector, matrix); - mRootNode->intersect(vd, terrainIntersector); - } - } + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, mActiveGrid); + mRootNode->traverseNodes(vd, nv.getViewPoint(), &lodCallback); } const float cellWorldSize = mStorage->getCellWorldSize(); From 9f0398c0214b5a52aa13ee21117572d99d4a831d Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 34/81] intersection by refnum tag + enable paging for acti,door,cont Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 75 ++++++++++++++++++----- apps/openmw/mwrender/objectpaging.hpp | 10 +++ apps/openmw/mwrender/renderingmanager.cpp | 16 +++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellstore.cpp | 40 ++---------- apps/openmw/mwworld/worldimp.cpp | 8 +++ components/sceneutil/optimizer.cpp | 4 ++ 7 files changed, 104 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 4c8706caf..365e8ec91 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -33,18 +35,16 @@ namespace MWRender { - bool typeFilter(int type, bool far, bool activeGrid) + bool typeFilter(int type, bool far) { switch (type) { case ESM::REC_STAT: - return true; - - case ESM::REC_ACTI: // TODO enable when intersectionvisitor supported + case ESM::REC_ACTI: case ESM::REC_DOOR: - return !activeGrid; + return true; case ESM::REC_CONT: - return far ? false : !activeGrid; + return !far; default: return false; @@ -321,6 +321,21 @@ namespace MWRender } }; + class AddRefnumMarkerVisitor : public osg::NodeVisitor + { + public: + AddRefnumMarkerVisitor(const ESM::RefNum &refnum) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mRefnum(refnum) {} + ESM::RefNum mRefnum; + virtual void apply(osg::Geometry &node) + { + osg::ref_ptr marker (new RefnumMarker); + marker->mRefnum = mRefnum; + if (osg::Array* array = node.getVertexArray()) + marker->mNumVertices = array->getNumElements(); + node.getOrCreateUserDataContainer()->addUserObject(marker); + } + }; + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) @@ -365,7 +380,7 @@ namespace MWRender { if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2,activeGrid)) continue; + if (!typeFilter(type,size>=2)) continue; if (deleted) { refs.erase(ref.mRefNum); continue; } refs[ref.mRefNum] = ref; } @@ -381,7 +396,7 @@ namespace MWRender bool deleted = it->second; if (deleted) { refs.erase(ref.mRefNum); continue; } int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2,activeGrid)) continue; + if (!typeFilter(type,size>=2)) continue; refs[ref.mRefNum] = ref; } } @@ -446,15 +461,32 @@ namespace MWRender std::string model = getModel(type, id, store); if (model.empty()) continue; model = "meshes/" + model; -/* + bool useAnim = type != ESM::REC_STAT; if (useAnim) + { model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); -*/ + if (activeGrid) + { + std::string kfname = Misc::StringUtils::lowerCase(model); + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) + { + kfname.replace(kfname.size()-4, 4, ".kf"); + if (mSceneManager->getVFS()->exists(kfname)) + continue; + } + } + } + osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); if (activeGrid) - refnumSet->mRefnums.insert(pair.first); + { + if (cnode->getNumChildrenRequiringUpdateTraversal() > 0 || SceneUtil::hasUserDescription(cnode, Constants::NightDayLabel) || SceneUtil::hasUserDescription(cnode, Constants::HerbalismLabel)) + continue; + else + refnumSet->mRefnums.insert(pair.first); + } { OpenThreads::ScopedLock lock(mDisabledMutex); @@ -472,9 +504,6 @@ namespace MWRender continue; } - if (activeGrid && cnode->getNumChildrenRequiringUpdateTraversal() > 0) - continue; - auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { @@ -537,6 +566,20 @@ namespace MWRender osg::ref_ptr node = osg::clone(cnode, co); node->setUserDataContainer(nullptr); + if (activeGrid) + { + if (merge) + { + AddRefnumMarkerVisitor visitor(ref.mRefNum); + node->accept(visitor); + } + else + { + osg::ref_ptr marker = new RefnumMarker; marker->mRefnum = ref.mRefNum; + node->getOrCreateUserDataContainer()->addUserObject(marker); + } + } + trans->addChild(node); if (merge) @@ -636,7 +679,7 @@ namespace MWRender bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) { - if (!typeFilter(type, false, false)) + if (!typeFilter(type, false)) return false; { @@ -655,7 +698,7 @@ namespace MWRender bool ObjectPaging::blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos) { - if (!typeFilter(type, false, true)) + if (!typeFilter(type, false)) return false; { diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 4419cd86a..03d9ccfad 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -63,6 +63,16 @@ namespace MWRender SizeCache mSizeCache; }; + class RefnumMarker : public osg::Object + { + public: + RefnumMarker() : mNumVertices(0) {} + RefnumMarker(const RefnumMarker ©, osg::CopyOp co) : mRefnum(copy.mRefnum), mNumVertices(copy.mNumVertices) {} + META_Object(MWRender, RefnumMarker) + + ESM::RefNum mRefnum; + unsigned int mNumVertices; + }; } #endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 590c59302..bb7528a20 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1016,6 +1016,7 @@ namespace MWRender { RenderingManager::RayResult result; result.mHit = false; + result.mHitRefnum.mContentFile = -1; result.mRatio = 0; if (intersector->containsIntersections()) { @@ -1027,6 +1028,7 @@ namespace MWRender result.mRatio = intersection.ratio; PtrHolder* ptrHolder = nullptr; + std::vector refnumMarkers; for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) { osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer(); @@ -1036,11 +1038,25 @@ namespace MWRender { if (PtrHolder* p = dynamic_cast(userDataContainer->getUserObject(i))) ptrHolder = p; + if (RefnumMarker* r = dynamic_cast(userDataContainer->getUserObject(i))) + refnumMarkers.push_back(r); } } if (ptrHolder) result.mHitObject = ptrHolder->mPtr; + + unsigned int vertexCounter = 0; + for (unsigned int i=0; imNumVertices || (intersectionIndex >= vertexCounter && intersectionIndex < vertexCounter + refnumMarkers[i]->mNumVertices)) + { + result.mHitRefnum = refnumMarkers[i]->mRefnum; + break; + } + vertexCounter += refnumMarkers[i]->mNumVertices; + } } return result; diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index c46c2f343..28376d1d6 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -157,6 +157,7 @@ namespace MWRender osg::Vec3f mHitNormalWorld; osg::Vec3f mHitPointWorld; MWWorld::Ptr mHitObject; + ESM::RefNum mHitRefnum; float mRatio; }; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 36f568a5f..2cda83e17 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -165,28 +165,6 @@ namespace ref.load (state); collection.mList.push_back (ref); } - - struct SearchByRefNumVisitor - { - MWWorld::LiveCellRefBase* mFound; - ESM::RefNum mRefNumToFind; - - SearchByRefNumVisitor(const ESM::RefNum& toFind) - : mFound(nullptr) - , mRefNumToFind(toFind) - { - } - - bool operator()(const MWWorld::Ptr& ptr) - { - if (ptr.getCellRef().getRefNum() == mRefNumToFind) - { - mFound = ptr.getBase(); - return false; - } - return true; - } - }; } namespace MWWorld @@ -263,9 +241,7 @@ namespace MWWorld throw std::runtime_error("moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)"); // Ensure that the object actually exists in the cell - SearchByRefNumVisitor searchVisitor(object.getCellRef().getRefNum()); - forEach(searchVisitor); - if (!searchVisitor.mFound) + if (searchViaRefNum(object.getCellRef().getRefNum()).isEmpty()) throw std::runtime_error("moveTo: object is not in this cell"); @@ -942,26 +918,22 @@ namespace MWWorld movedTo.load(reader); // Search for the reference. It might no longer exist if its content file was removed. - SearchByRefNumVisitor visitor(refnum); - forEachInternal(visitor); - - if (!visitor.mFound) + Ptr movedRef = searchViaRefNum(refnum); + if (movedRef.isEmpty()) { Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)"; continue; } - MWWorld::LiveCellRefBase* movedRef = visitor.mFound; - CellStore* otherCell = callback->getCellStore(movedTo); if (otherCell == nullptr) { - Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() + Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef.getCellRef().getRefId() << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location."; // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. // Restore original coordinates: - movedRef->mData.setPosition(movedRef->mRef.getPosition()); + movedRef.getRefData().setPosition(movedRef.getCellRef().getPosition()); continue; } @@ -972,7 +944,7 @@ namespace MWWorld continue; } - moveTo(MWWorld::Ptr(movedRef, this), otherCell); + moveTo(movedRef, otherCell); } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2975240e7..5f1c8aaa2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1965,6 +1965,14 @@ namespace MWWorld rayToObject = mRendering->castCameraToViewportRay(0.5f, 0.5f, maxDistance, ignorePlayer); facedObject = rayToObject.mHitObject; + if (facedObject.isEmpty() && rayToObject.mHitRefnum.hasContentFile()) + { + for (CellStore* cellstore : mWorldScene->getActiveCells()) + { + facedObject = cellstore->searchViaRefNum(rayToObject.mHitRefnum); + if (!facedObject.isEmpty()) break; + } + } if (rayToObject.mHit) mDistanceToFacedObject = (rayToObject.mRatio * maxDistance) - camDist; else diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 985fd9ee2..f0da9e7e2 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -1824,6 +1824,10 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom lhs.dirtyBound(); lhs.dirtyDisplayList(); + if (osg::UserDataContainer* rhsUserData = rhs.getUserDataContainer()) + for (unsigned int i=0; igetNumUserObjects(); ++i) + lhs.getOrCreateUserDataContainer()->addUserObject(rhsUserData->getUserObject(i)); + return true; } From 89ec6cfea2d9aadd5996cff755ee457aaf6a2437 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 35/81] tooltips labels Signed-off-by: Bret Curtis --- apps/openmw/mwphysics/physicssystem.cpp | 9 +++++++++ apps/openmw/mwphysics/physicssystem.hpp | 4 ++++ apps/openmw/mwrender/renderingmanager.cpp | 12 +++--------- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 11 +++++++++-- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 7cc0f7770..bbb2ae8d3 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -397,6 +397,15 @@ namespace MWPhysics return osg::Vec3f(); } + osg::BoundingBox PhysicsSystem::getBoundingBox(const MWWorld::ConstPtr &object) const + { + const Object * physobject = getObject(object); + if (!physobject) return osg::BoundingBox(); + btVector3 min, max; + physobject->getCollisionObject()->getCollisionShape()->getAabb(physobject->getCollisionObject()->getWorldTransform(), min, max); + return osg::BoundingBox(Misc::Convert::toOsg(min), Misc::Convert::toOsg(max)); + } + osg::Vec3f PhysicsSystem::getCollisionObjectPosition(const MWWorld::ConstPtr &actor) const { const Actor* physactor = getActor(actor); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 8b09722af..32d460b1d 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include "../mwworld/ptr.hpp" @@ -144,6 +145,9 @@ namespace MWPhysics /// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. osg::Vec3f getCollisionObjectPosition(const MWWorld::ConstPtr& actor) const; + /// Get bounding box in world space of the given object. + osg::BoundingBox getBoundingBox(const MWWorld::ConstPtr &object) const; + /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will /// be overwritten. Valid until the next call to applyQueuedMovement. void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index bb7528a20..30e63e56e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -977,20 +977,14 @@ namespace MWRender renderCameraToImage(rttCamera.get(),image,w,h); } - osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr) + osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox &worldbb) { - if (!ptr.getRefData().getBaseNode()) - return osg::Vec4f(); - - osg::ComputeBoundsVisitor computeBoundsVisitor; - computeBoundsVisitor.setTraversalMask(~(Mask_ParticleSystem|Mask_Effect)); - ptr.getRefData().getBaseNode()->accept(computeBoundsVisitor); - + if (!worldbb.valid()) return osg::Vec4f(); osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix(); float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f; for (int i=0; i<8; ++i) { - osg::Vec3f corner = computeBoundsVisitor.getBoundingBox().corner(i); + osg::Vec3f corner = worldbb.corner(i); corner = corner * viewProj; float x = (corner.x() + 1.f) * 0.5f; diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 28376d1d6..b18d2caf1 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -168,7 +168,7 @@ namespace MWRender RayResult castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors=false); /// Get the bounding box of the given object in screen coordinates as (minX, minY, maxX, maxY), with (0,0) being the top left corner. - osg::Vec4f getScreenBounds(const MWWorld::Ptr& ptr); + osg::Vec4f getScreenBounds(const osg::BoundingBox &worldbb); void setSkyEnabled(bool enabled); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5f1c8aaa2..12521f059 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1934,8 +1934,15 @@ namespace MWWorld // retrieve object dimensions so we know where to place the floating label if (!object.isEmpty ()) { - osg::Vec4f screenBounds = mRendering->getScreenBounds(object); - + osg::BoundingBox bb = mPhysics->getBoundingBox(object); + if (!bb.valid() && object.getRefData().getBaseNode()) + { + osg::ComputeBoundsVisitor computeBoundsVisitor; + computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect)); + object.getRefData().getBaseNode()->accept(computeBoundsVisitor); + bb = computeBoundsVisitor.getBoundingBox(); + } + osg::Vec4f screenBounds = mRendering->getScreenBounds(bb); MWBase::Environment::get().getWindowManager()->setFocusObjectScreenCoords( screenBounds.x(), screenBounds.y(), screenBounds.z(), screenBounds.w()); } From 6fa12a6eb854476c98a9546658d4ce6f837e930c Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 36/81] preload tweak Signed-off-by: Bret Curtis --- apps/openmw/mwworld/cellpreloader.cpp | 51 ++++++++++----------------- apps/openmw/mwworld/scene.cpp | 12 ++----- apps/openmw/mwworld/scene.hpp | 1 - 3 files changed, 22 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index eb48424c5..872a29e89 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -65,23 +66,7 @@ namespace MWWorld mTerrainView = mTerrain->createView(); ListModelsVisitor visitor (mMeshes); - if (cell->getState() == MWWorld::CellStore::State_Loaded) - { - cell->forEach(visitor); - } - else - { - const std::vector& objectIds = cell->getPreloadedIds(); - - // could possibly build the model list in the worker thread if we manage to make the Store thread safe - for (const std::string& id : objectIds) - { - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id); - std::string model = ref.getPtr().getClass().getModel(ref.getPtr()); - if (!model.empty()) - mMeshes.push_back(model); - } - } + cell->forEach(visitor); } virtual void abort() @@ -97,7 +82,7 @@ namespace MWWorld try { mTerrain->cacheCell(mTerrainView.get(), mX, mY); - mPreloadedObjects.push_back(mLandManager->getLand(mX, mY)); + mPreloadedObjects.insert(mLandManager->getLand(mX, mY)); } catch(std::exception& e) { @@ -113,17 +98,7 @@ namespace MWWorld { mesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS()); - if (mPreloadInstances) - { - mPreloadedObjects.push_back(mSceneManager->cacheInstance(mesh)); - mPreloadedObjects.push_back(mBulletShapeManager->cacheInstance(mesh)); - } - else - { - mPreloadedObjects.push_back(mSceneManager->getTemplate(mesh)); - mPreloadedObjects.push_back(mBulletShapeManager->getShape(mesh)); - } - + bool animated = false; size_t slashpos = mesh.find_last_of("/\\"); if (slashpos != std::string::npos && slashpos != mesh.size()-1) { @@ -134,11 +109,23 @@ namespace MWWorld if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) { kfname.replace(kfname.size()-4, 4, ".kf"); - mPreloadedObjects.push_back(mKeyframeManager->get(kfname)); + if (mSceneManager->getVFS()->exists(kfname)) + { + mPreloadedObjects.insert(mKeyframeManager->get(kfname)); + animated = true; + } } - } } + if (mPreloadInstances && animated) + mPreloadedObjects.insert(mSceneManager->cacheInstance(mesh)); + else + mPreloadedObjects.insert(mSceneManager->getTemplate(mesh)); + if (mPreloadInstances) + mPreloadedObjects.insert(mBulletShapeManager->cacheInstance(mesh)); + else + mPreloadedObjects.insert(mBulletShapeManager->getShape(mesh)); + } catch (std::exception& e) { @@ -166,7 +153,7 @@ namespace MWWorld osg::ref_ptr mTerrainView; // keep a ref to the loaded objects to make sure it stays loaded as long as this cell is in the preloaded state - std::vector > mPreloadedObjects; + std::set > mPreloadedObjects; }; class TerrainPreloadItem : public SceneUtil::WorkItem diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index a46986616..d2b31a855 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -305,12 +305,7 @@ namespace MWWorld void Scene::update (float duration, bool paused) { - mPreloadTimer -= duration; - if (mPreloadTimer <= 0.f) - { - preloadCells(0.1f); - mPreloadTimer = 0.1f; - } + preloadCells(duration); mRendering.update (duration, paused); @@ -760,13 +755,12 @@ namespace MWWorld MWBase::Environment::get().getWorld()->adjustSky(); - mLastPlayerPos = pos.asVec3(); + mLastPlayerPos = player.getRefData().getPosition().asVec3(); } Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics, DetourNavigator::Navigator& navigator) : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNavigator(navigator) - , mPreloadTimer(0.f) , mHalfGridSize(Settings::Manager::getInt("exterior cell load distance", "Cells")) , mCellLoadingThreshold(1024.f) , mPreloadDistance(Settings::Manager::getInt("preload distance", "Cells")) @@ -1025,6 +1019,7 @@ namespace MWWorld void Scene::preloadCells(float dt) { + if (dt<=1e-06) return; std::vector exteriorPositions; const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); @@ -1160,7 +1155,6 @@ namespace MWWorld void Scene::reloadTerrain() { - mPreloadTimer = 0; mPreloader->setTerrainPreloadPositions(std::vector()); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 2fbbccf68..227bb7b31 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -75,7 +75,6 @@ namespace MWWorld MWRender::RenderingManager& mRendering; DetourNavigator::Navigator& mNavigator; std::unique_ptr mPreloader; - float mPreloadTimer; int mHalfGridSize; float mCellLoadingThreshold; float mPreloadDistance; From b27b76e32582601412da380eef273619a62cfe4b Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 37/81] avoid pagerebuild when reloading a same save Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 41 ++++++++++----- apps/openmw/mwrender/objectpaging.hpp | 20 +++++-- apps/openmw/mwrender/renderingmanager.cpp | 9 ++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellpreloader.cpp | 32 +++++++----- apps/openmw/mwworld/cellpreloader.hpp | 4 +- apps/openmw/mwworld/scene.cpp | 64 +++++++++++------------ apps/openmw/mwworld/scene.hpp | 6 +-- 8 files changed, 110 insertions(+), 67 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 365e8ec91..c3a90da80 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -339,6 +339,7 @@ namespace MWRender ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) + , mRefTrackerLocked(false) { mActiveGrid = Settings::Manager::getBool("object paging active grid", "Terrain"); mDebugBatches = Settings::Manager::getBool("object paging debug batches", "Terrain"); @@ -403,9 +404,9 @@ namespace MWRender } { - OpenThreads::ScopedLock lock(mDisabledMutex); + OpenThreads::ScopedLock lock(mRefTrackerMutex); if (activeGrid) - for (auto ref : mBlacklist) + for (auto ref : getRefTracker().mBlacklist) refs.erase(ref); } @@ -489,8 +490,8 @@ namespace MWRender } { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (mDisabled.count(pair.first)) + OpenThreads::ScopedLock lock(mRefTrackerMutex); + if (getRefTracker().mDisabled.count(pair.first)) continue; } @@ -683,14 +684,16 @@ namespace MWRender return false; { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (enabled && !mDisabled.erase(refnum)) return false; - if (!enabled && !mDisabled.insert(refnum).second) return false; + OpenThreads::ScopedLock lock(mRefTrackerMutex); + if (enabled && !getWritableRefTracker().mDisabled.erase(refnum)) return false; + if (!enabled && !getWritableRefTracker().mDisabled.insert(refnum).second) return false; + if (mRefTrackerLocked) return false; } ClearCacheFunctor ccf; ccf.mPosition = pos; mCache->call(ccf); + if (ccf.mToClear.empty()) return false; for (auto chunk : ccf.mToClear) mCache->removeFromObjectCache(chunk); return true; @@ -702,8 +705,9 @@ namespace MWRender return false; { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (!mBlacklist.insert(refnum).second) return false; + OpenThreads::ScopedLock lock(mRefTrackerMutex); + if (!getWritableRefTracker().mBlacklist.insert(refnum).second) return false; + if (mRefTrackerLocked) return false; } ClearCacheFunctor ccf; @@ -719,12 +723,25 @@ namespace MWRender void ObjectPaging::clear() { + OpenThreads::ScopedLock lock(mRefTrackerMutex); + mRefTrackerNew.mDisabled.clear(); + mRefTrackerNew.mBlacklist.clear(); + mRefTrackerLocked = true; + } + + bool ObjectPaging::unlockCache() + { + if (!mRefTrackerLocked) return false; { - OpenThreads::ScopedLock lock(mDisabledMutex); - mDisabled.clear(); - mBlacklist.clear(); + OpenThreads::ScopedLock lock(mRefTrackerMutex); + mRefTrackerLocked = false; + if (mRefTracker == mRefTrackerNew) + return false; + else + mRefTracker = mRefTrackerNew; } mCache->clear(); + return true; } struct GetRefnumsFunctor diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 03d9ccfad..c79dd6e06 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -41,6 +41,10 @@ namespace MWRender void clear(); + /// Must be called after clear() before rendering starts. + /// @return true if view needs rebuild + bool unlockCache(); + void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); @@ -54,9 +58,19 @@ namespace MWRender float mMinSizeMergeFactor; float mMinSizeCostMultiplier; - OpenThreads::Mutex mDisabledMutex; - std::set mDisabled; - std::set mBlacklist; + OpenThreads::Mutex mRefTrackerMutex; + struct RefTracker + { + std::set mDisabled; + std::set mBlacklist; + bool operator==(const RefTracker&other) { return mDisabled == other.mDisabled && mBlacklist == other.mBlacklist; } + }; + RefTracker mRefTracker; + RefTracker mRefTrackerNew; + bool mRefTrackerLocked; + + const RefTracker& getRefTracker() const { return mRefTracker; } + RefTracker& getWritableRefTracker() { return mRefTrackerLocked ? mRefTrackerNew : mRefTracker; } OpenThreads::Mutex mSizeCacheMutex; typedef std::map SizeCache; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 30e63e56e..99f3e25ac 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1511,6 +1511,15 @@ namespace MWRender if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3())) mTerrain->rebuildViews(); } + bool RenderingManager::pagingUnlockCache() + { + if (mObjectPaging && mObjectPaging->unlockCache()) + { + mTerrain->rebuildViews(); + return true; + } + return false; + } void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) { if (mObjectPaging) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b18d2caf1..db4788970 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -244,6 +244,7 @@ namespace MWRender bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr); + bool pagingUnlockCache(); void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); private: diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 872a29e89..bee6a957d 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -419,36 +419,44 @@ namespace MWWorld mUnrefQueue = unrefQueue; } - bool CellPreloader::getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp) + bool CellPreloader::syncTerrainLoad(const std::vector &positions, int& progress, int& progressRange, double timestamp) { if (!mTerrainPreloadItem) - return false; + return true; else if (mTerrainPreloadItem->isDone()) { - mTerrainPreloadItem->storeViews(timestamp); - mTerrainPreloadItem = nullptr; - return false; + if (mTerrainPreloadItem->storeViews(timestamp)) + { + mTerrainPreloadItem = nullptr; + return true; + } + else + { + setTerrainPreloadPositions(std::vector()); + setTerrainPreloadPositions(positions); + return false; + } } else { progress = mTerrainPreloadItem->getProgress(); progressRange = mTerrainPreloadItem->getProgressRange(); - return !progress || progress < progressRange; + return false; } } - void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid& exceptPos) + void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid *exceptPos) { + const float resetThreshold = ESM::Land::REAL_SIZE; + for (auto pos : mTerrainPreloadPositions) + if (exceptPos && (pos.first-exceptPos->first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos->second) + return; if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) { - const float resetThreshold = ESM::Land::REAL_SIZE; - for (auto pos : mTerrainPreloadPositions) - if ((pos.first-exceptPos.first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos.second) - return; mTerrainPreloadItem->abort(); mTerrainPreloadItem->waitTillDone(); - mTerrainPreloadItem = nullptr; } + setTerrainPreloadPositions(std::vector()); } bool contains(const std::vector& container, const std::vector& contained) diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 98e173f17..e719f2e60 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -72,8 +72,8 @@ namespace MWWorld typedef std::pair PositionCellGrid; void setTerrainPreloadPositions(const std::vector& positions); - bool getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp); - void abortTerrainPreloadExcept(const PositionCellGrid &exceptPos); + bool syncTerrainLoad(const std::vector &positions, int& progress, int& progressRange, double timestamp); + void abortTerrainPreloadExcept(const PositionCellGrid *exceptPos); private: Resource::ResourceSystem* mResourceSystem; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index d2b31a855..53ddaf08c 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -510,13 +510,10 @@ namespace MWWorld osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter); if (newCell != mCurrentGridCenter) - { - preloadTerrain(pos); - changeCellGrid(newCell.x(), newCell.y()); - } + changeCellGrid(pos, newCell.x(), newCell.y()); } - void Scene::changeCellGrid (int playerCellX, int playerCellY, bool changeEvent) + void Scene::changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent) { CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) @@ -538,8 +535,8 @@ namespace MWWorld osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); mRendering.setActiveGrid(newGrid); + preloadTerrain(pos, true); mPagedRefs.clear(); - checkTerrainLoaded(); mRendering.getPagedRefnums(newGrid, mPagedRefs); std::size_t refsToLoad = 0; @@ -867,9 +864,7 @@ namespace MWWorld if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); - preloadTerrain(position.asVec3()); - checkTerrainLoaded(); - changeCellGrid(x, y, changeEvent); + changeCellGrid(position.asVec3(), x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); @@ -878,29 +873,6 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } - void Scene::checkTerrainLoaded() - { - Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - Loading::ScopedLoad load(loadingListener); - int progress = 0, initialProgress = -1, progressRange = 0; - while (mPreloader->getTerrainPreloadInProgress(progress, progressRange, mRendering.getReferenceTime())) - { - if (initialProgress == -1) - { - loadingListener->setLabel("#{sLoadingMessage4}"); - initialProgress = progress; - } - if (progress) - { - loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); - loadingListener->setProgress(progress-initialProgress); - } - else - loadingListener->setProgress(0); - OpenThreads::Thread::microSleep(5000); - } - } - CellStore* Scene::getCurrentCell () { return mCurrentCell; @@ -1145,12 +1117,36 @@ namespace MWWorld mPreloader->preload(cell, mRendering.getReferenceTime()); } - void Scene::preloadTerrain(const osg::Vec3f &pos) + void Scene::preloadTerrain(const osg::Vec3f &pos, bool sync) { std::vector vec; vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); - mPreloader->abortTerrainPreloadExcept(vec[0]); + if (sync && mRendering.pagingUnlockCache()) + mPreloader->abortTerrainPreloadExcept(nullptr); + else + mPreloader->abortTerrainPreloadExcept(&vec[0]); mPreloader->setTerrainPreloadPositions(vec); + if (!sync) return; + + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); + int progress = 0, initialProgress = -1, progressRange = 0; + while (!mPreloader->syncTerrainLoad(vec, progress, progressRange, mRendering.getReferenceTime())) + { + if (initialProgress == -1) + { + loadingListener->setLabel("#{sLoadingMessage4}"); + initialProgress = progress; + } + if (progress) + { + loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); + loadingListener->setProgress(progress-initialProgress); + } + else + loadingListener->setProgress(0); + OpenThreads::Thread::microSleep(5000); + } } void Scene::reloadTerrain() diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 227bb7b31..b9a39779d 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -93,7 +93,7 @@ namespace MWWorld osg::Vec2i mCurrentGridCenter; // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center - void changeCellGrid (int playerCellX, int playerCellY, bool changeEvent = true); + void changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent = true); typedef std::pair PositionCellGrid; @@ -102,8 +102,6 @@ namespace MWWorld void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); - void checkTerrainLoaded(); - osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; @@ -115,7 +113,7 @@ namespace MWWorld ~Scene(); void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); - void preloadTerrain(const osg::Vec3f& pos); + void preloadTerrain(const osg::Vec3f& pos, bool sync=false); void reloadTerrain(); void unloadCell (CellStoreCollection::iterator iter, bool test = false); From 4238fbccdf450c89454bfdb76fc7b1a7c1b19945 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 38/81] view fix Signed-off-by: Bret Curtis --- apps/openmw/mwrender/renderingmanager.cpp | 1 + apps/openmw/mwworld/scene.cpp | 3 +-- components/terrain/viewdata.cpp | 20 ++++++++++++-------- components/terrain/viewdata.hpp | 1 + 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 99f3e25ac..d21482894 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1063,6 +1063,7 @@ namespace MWRender mIntersectionVisitor = new osgUtil::IntersectionVisitor; mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber()); + mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp()); mIntersectionVisitor->setIntersector(intersector); int mask = ~0; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 53ddaf08c..b311107f4 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -305,11 +305,10 @@ namespace MWWorld void Scene::update (float duration, bool paused) { + mPreloader->updateCache(mRendering.getReferenceTime()); preloadCells(duration); mRendering.update (duration, paused); - - mPreloader->updateCache(mRendering.getReferenceTime()); } void Scene::unloadCell (CellStoreCollection::iterator iter, bool test) diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 8a8769000..c24252b7d 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -145,15 +145,15 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo { float shortestDist = std::numeric_limits::max(); const ViewData* mostSuitableView = nullptr; - for (const ViewData& other : mViewVector) + for (const ViewData* other : mUsedViews) { - if (other.suitableToUse(activeGrid) && other.getWorldUpdateRevision() >= mWorldUpdateRevision) + if (other->suitableToUse(activeGrid) && other->getWorldUpdateRevision() >= mWorldUpdateRevision) { - float dist = (viewPoint-other.getViewPoint()).length2(); + float dist = (viewPoint-other->getViewPoint()).length2(); if (dist < shortestDist) { shortestDist = dist; - mostSuitableView = &other; + mostSuitableView = other; } } } @@ -195,6 +195,7 @@ ViewData *ViewDataMap::createOrReuseView() mViewVector.push_back(ViewData()); vd = &mViewVector.back(); } + mUsedViews.push_back(vd); vd->setWorldUpdateRevision(mWorldUpdateRevision); return vd; } @@ -215,13 +216,16 @@ void ViewDataMap::clearUnusedViews(double referenceTime) else ++it; } - for (ViewData& vd : mViewVector) + for (std::deque::iterator it = mUsedViews.begin(); it != mUsedViews.end(); ) { - if (vd.getLastUsageTimeStamp() + mExpiryDelay < referenceTime) + if ((*it)->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) { - vd.clear(); - mUnusedViews.push_back(&vd); + (*it)->clear(); + mUnusedViews.push_back(*it); + it = mUsedViews.erase(it); } + else + ++it; } } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index ba4fba3b2..400d9fbbe 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -106,6 +106,7 @@ namespace Terrain unsigned int mWorldUpdateRevision; + std::deque mUsedViews; std::deque mUnusedViews; }; From f12879a04c4db10e8ee8f80f217c9b9e8d11c121 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 39/81] allow statesetupdater as cullcallback = faster + works in paging Signed-off-by: Bret Curtis --- apps/openmw/mwrender/animation.cpp | 22 +++++----------------- apps/openmw/mwrender/animation.hpp | 3 --- apps/openmw/mwrender/npcanimation.cpp | 8 +++----- apps/openmw/mwrender/npcanimation.hpp | 2 +- apps/openmw/mwrender/objectpaging.cpp | 11 +++++++++++ components/nifosg/controller.cpp | 16 ++++++++-------- components/nifosg/controller.hpp | 8 +++++--- components/nifosg/nifloader.cpp | 18 +++++++++++++----- components/sceneutil/statesetupdater.cpp | 22 +++++++++++++++------- components/sceneutil/statesetupdater.hpp | 2 +- 10 files changed, 62 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8f8e8c233..48c58f247 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -513,6 +513,9 @@ namespace MWRender if (mShadowUniform) stateset->addUniform(mShadowUniform); + stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); + // FIXME: overriding diffuse/ambient/emissive colors osg::Material* material = new osg::Material; material->setColorMode(osg::Material::OFF); @@ -1741,31 +1744,16 @@ namespace MWRender if (mTransparencyUpdater == nullptr) { mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform()); - mObjectRoot->addUpdateCallback(mTransparencyUpdater); + mObjectRoot->addCullCallback(mTransparencyUpdater); } else mTransparencyUpdater->setAlpha(alpha); } else { - mObjectRoot->removeUpdateCallback(mTransparencyUpdater); + mObjectRoot->removeCullCallback(mTransparencyUpdater); mTransparencyUpdater = nullptr; - mObjectRoot->setStateSet(nullptr); } - - setRenderBin(); - } - - void Animation::setRenderBin() - { - if (mAlpha != 1.f) - { - osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet(); - stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); - } - else if (osg::StateSet* stateset = mObjectRoot->getStateSet()) - stateset->setRenderBinToInherit(); } void Animation::setLightEffect(float effect) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 2890e7be4..c53cf98a9 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -336,9 +336,6 @@ protected: */ virtual void addControllers(); - /// Set the render bin for this animation's object root. May be customized by subclasses. - virtual void setRenderBin(); - public: Animation(const MWWorld::Ptr &ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ec825ca2f..6e7669976 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -435,12 +435,10 @@ void NpcAnimation::setRenderBin() osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin); prototypeAdded = true; } - - osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet(); - stateset->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); + mObjectRoot->getOrCreateStateSet()->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); } - else - Animation::setRenderBin(); + else if (osg::StateSet* stateset = mObjectRoot->getStateSet()) + stateset->setRenderBinToInherit(); } void NpcAnimation::rebuild() diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index e102f5097..7f8d5434b 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -88,7 +88,7 @@ private: void addPartGroup(int group, int priority, const std::vector &parts, bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr); - virtual void setRenderBin(); + void setRenderBin(); osg::ref_ptr mFirstPersonNeckController; diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index c3a90da80..b138a6412 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -162,6 +162,17 @@ namespace MWRender { if (callback->className() == std::string("BillboardCallback")) handleBillboard(cloned); + else + { + if (node->getCullCallback()->getNestedCallback()) + { + osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); + clonedCallback->setNestedCallback(nullptr); + cloned->addCullCallback(clonedCallback); + } + else + cloned->addCullCallback(const_cast(callback)); + } callback = callback->getNestedCallback(); } } diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index a088ead4c..b5fd374e3 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -352,8 +352,9 @@ void RollController::operator() (osg::Node* node, osg::NodeVisitor* nv) } } -AlphaController::AlphaController(const Nif::NiFloatData *data) +AlphaController::AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial) : mData(data->mKeyList, 1.f) + , mBaseMaterial(baseMaterial) { } @@ -365,14 +366,13 @@ AlphaController::AlphaController() AlphaController::AlphaController(const AlphaController ©, const osg::CopyOp ©op) : StateSetUpdater(copy, copyop), Controller(copy) , mData(copy.mData) + , mBaseMaterial(copy.mBaseMaterial) { } void AlphaController::setDefaults(osg::StateSet *stateset) { - // need to create a deep copy of StateAttributes we will modify - osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); - stateset->setAttribute(osg::clone(mat, osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); } void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) @@ -387,9 +387,10 @@ void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) } } -MaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color) +MaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial) : mData(data->mKeyList, osg::Vec3f(1,1,1)) , mTargetColor(color) + , mBaseMaterial(baseMaterial) { } @@ -401,14 +402,13 @@ MaterialColorController::MaterialColorController(const MaterialColorController & : StateSetUpdater(copy, copyop), Controller(copy) , mData(copy.mData) , mTargetColor(copy.mTargetColor) + , mBaseMaterial(copy.mBaseMaterial) { } void MaterialColorController::setDefaults(osg::StateSet *stateset) { - // need to create a deep copy of StateAttributes we will modify - osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); - stateset->setAttribute(osg::clone(mat, osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); } void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 3f66013a2..c81f97a71 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -24,6 +24,7 @@ namespace osg { class Node; class StateSet; + class Material; } namespace osgParticle @@ -268,9 +269,9 @@ namespace NifOsg { private: FloatInterpolator mData; - + osg::ref_ptr mBaseMaterial; public: - AlphaController(const Nif::NiFloatData *data); + AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial); AlphaController(); AlphaController(const AlphaController& copy, const osg::CopyOp& copyop); @@ -291,7 +292,7 @@ namespace NifOsg Specular = 2, Emissive = 3 }; - MaterialColorController(const Nif::NiPosData *data, TargetColor color); + MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial); MaterialColorController(); MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop); @@ -304,6 +305,7 @@ namespace NifOsg private: Vec3Interpolator mData; TargetColor mTargetColor = Ambient; + osg::ref_ptr mBaseMaterial; }; class FlipController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index bff414707..88e89b400 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -639,7 +639,15 @@ namespace NifOsg handleParticleSystem(nifNode, node, composite, animflags, rootNode); if (composite->getNumControllers() > 0) - node->addUpdateCallback(composite); + { + osg::Callback *cb = composite; + if (composite->getNumControllers() == 1) + cb = composite->getController(0); + if (animflags & Nif::NiNode::AnimFlag_AutoPlay) + node->addCullCallback(cb); + else + node->addUpdateCallback(cb); // have to remain as UpdateCallback so AssignControllerSourcesVisitor can find it. + } bool isAnimated = false; handleNodeControllers(nifNode, node, animflags, isAnimated); @@ -778,7 +786,7 @@ namespace NifOsg } } - void handleMaterialControllers(const Nif::Property *materialProperty, SceneUtil::CompositeStateSetUpdater* composite, int animflags) + void handleMaterialControllers(const Nif::Property *materialProperty, SceneUtil::CompositeStateSetUpdater* composite, int animflags, const osg::Material* baseMaterial) { for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { @@ -789,7 +797,7 @@ namespace NifOsg const Nif::NiAlphaController* alphactrl = static_cast(ctrl.getPtr()); if (alphactrl->data.empty()) continue; - osg::ref_ptr osgctrl(new AlphaController(alphactrl->data.getPtr())); + osg::ref_ptr osgctrl(new AlphaController(alphactrl->data.getPtr(), baseMaterial)); setupController(alphactrl, osgctrl, animflags); composite->addController(osgctrl); } @@ -799,7 +807,7 @@ namespace NifOsg if (matctrl->data.empty()) continue; auto targetColor = static_cast(matctrl->targetColor); - osg::ref_ptr osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor)); + osg::ref_ptr osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor, baseMaterial)); setupController(matctrl, osgctrl, animflags); composite->addController(osgctrl); } @@ -1767,7 +1775,7 @@ namespace NifOsg if (!matprop->controller.empty()) { hasMatCtrl = true; - handleMaterialControllers(matprop, composite, animflags); + handleMaterialControllers(matprop, composite, animflags, mat); } break; diff --git a/components/sceneutil/statesetupdater.cpp b/components/sceneutil/statesetupdater.cpp index 121cdaca6..08418f331 100644 --- a/components/sceneutil/statesetupdater.cpp +++ b/components/sceneutil/statesetupdater.cpp @@ -2,30 +2,38 @@ #include #include +#include namespace SceneUtil { void StateSetUpdater::operator()(osg::Node* node, osg::NodeVisitor* nv) { + bool isCullVisitor = nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR; if (!mStateSets[0]) { - // first time setup - osg::StateSet* src = node->getOrCreateStateSet(); - for (int i=0; i<2; ++i) // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first - // This can be done conveniently in user implementations of the setDefaults() method + for (int i=0; i<2; ++i) { - mStateSets[i] = new osg::StateSet(*src, osg::CopyOp::SHALLOW_COPY); + if (!isCullVisitor) + mStateSets[i] = new osg::StateSet(*node->getOrCreateStateSet(), osg::CopyOp::SHALLOW_COPY); // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first in setDefaults + else + mStateSets[i] = new osg::StateSet; setDefaults(mStateSets[i]); } } osg::StateSet* stateset = mStateSets[nv->getTraversalNumber()%2]; - node->setStateSet(stateset); - apply(stateset, nv); + if (!isCullVisitor) + node->setStateSet(stateset); + else + static_cast(nv)->pushStateSet(stateset); + traverse(node, nv); + + if (isCullVisitor) + static_cast(nv)->popStateSet(); } void StateSetUpdater::reset() diff --git a/components/sceneutil/statesetupdater.hpp b/components/sceneutil/statesetupdater.hpp index 51398844c..d12316fb2 100644 --- a/components/sceneutil/statesetupdater.hpp +++ b/components/sceneutil/statesetupdater.hpp @@ -13,7 +13,7 @@ namespace SceneUtil /// traversals run in parallel can yield up to 200% framerates. /// @par Race conditions are prevented using a "double buffering" scheme - we have two StateSets that take turns, /// one StateSet we can write to, the second one is currently in use by the draw traversal of the last frame. - /// @par Must be set as UpdateCallback on a Node. + /// @par Must be set as UpdateCallback or CullCallback on a Node. If set as a CullCallback, the StateSetUpdater operates on an empty StateSet, otherwise it operates on a clone of the node's existing StateSet. /// @note Do not add the same StateSetUpdater to multiple nodes. /// @note Do not add multiple StateSetControllers on the same Node as they will conflict - instead use the CompositeStateSetUpdater. class StateSetUpdater : public osg::NodeCallback From 66c9469a804d772d56f60e0329b46d48d69432e4 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 40/81] fix Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index b138a6412..8cff46fa8 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -444,15 +444,12 @@ namespace MWRender if (size < 1.f) { osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE; - cellPos.x() = std::max(cellPos.x(), std::floor(minBound.x())); - cellPos.x() = std::min(cellPos.x(), std::ceil(maxBound.x())); - cellPos.y() = std::max(cellPos.y(), std::floor(minBound.y())); - cellPos.y() = std::min(cellPos.y(), std::ceil(maxBound.y())); - if (cellPos.x() < minBound.x() || cellPos.x() > maxBound.x() || cellPos.y() < minBound.y() || cellPos.y() > maxBound.y()) + if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y())) + continue; + if ((maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y())) continue; } - float d = (viewPoint - pos).length(); if (!activeGrid) { @@ -619,7 +616,7 @@ namespace MWRender if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()) + if ((relativeViewPoint - mergeGroup->getBound().center()).length() > mergeGroup->getBound().radius()*2) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); @@ -630,17 +627,16 @@ namespace MWRender group->addChild(mergeGroup); - if (compile) - { - stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; - mergeGroup->accept(stateToCompile); - } - if (mDebugBatches) { DebugVisitor dv; mergeGroup->accept(dv); } + if (compile) + { + stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + mergeGroup->accept(stateToCompile); + } } auto ico = mSceneManager->getIncrementalCompileOperation(); From 4e2efb3cdb6d4ab3cbf4b120bfc6d3a70062de1a Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 15 May 2020 13:37:00 +0000 Subject: [PATCH 41/81] avoid sqrt Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 8cff46fa8..dbe99b0e1 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -450,14 +450,14 @@ namespace MWRender continue; } - float d = (viewPoint - pos).length(); + float dSqr = (viewPoint - pos).length2(); if (!activeGrid) { OpenThreads::ScopedLock lock(mSizeCacheMutex); SizeCache::iterator found = mSizeCache.find(pair.first); if (found != mSizeCache.end()) { - if (found->second < d*minSize) + if (found->second < dSqr*minSize*minSize) continue; } } @@ -503,12 +503,12 @@ namespace MWRender continue; } - float radius = cnode->getBound().radius() * ref.mScale; - if (radius < d*minSize) + float radius2 = cnode->getBound().radius2() * ref.mScale*ref.mScale; + if (radius2 < dSqr*minSize*minSize) { OpenThreads::ScopedLock lock(mSizeCacheMutex); { - mSizeCache[pair.first] = radius; + mSizeCache[pair.first] = radius2; } continue; } @@ -551,13 +551,8 @@ namespace MWRender const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); - if (minSizeMerged != minSize) - { - float d = (viewPoint - pos).length(); - float radius = cnode->getBound().radius() * cref->mScale; - if (radius < d*minSizeMerged) - continue; - } + if (minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged) + continue; osg::Matrixf matrix; matrix.preMultTranslate(pos - worldCenter); @@ -616,7 +611,7 @@ namespace MWRender if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - mergeGroup->getBound().center()).length() > mergeGroup->getBound().radius()*2) + if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()*2*2) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); From daa2761c2d01120d2fad597b25668a2563cb712f Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sat, 16 May 2020 13:37:00 +0000 Subject: [PATCH 42/81] alphablending & billboardfix Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 68 ++++++++++------------ components/sceneutil/mwshadowtechnique.cpp | 5 +- components/sceneutil/optimizer.cpp | 7 +++ 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index dbe99b0e1..d65fffde8 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -95,21 +95,15 @@ namespace MWRender } virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Node* node,unsigned int option) const { - return true; + return (node->getDataVariance() != osg::Object::DYNAMIC); } }; class CopyOp : public osg::CopyOp { public: - CopyOp(bool deep) : mSqrDistance(0.f) { - unsigned int flags = osg::CopyOp::DEEP_COPY_NODES; - if (deep) - flags |= osg::CopyOp::DEEP_COPY_DRAWABLES; - setCopyFlags(flags); - } - - float mSqrDistance; + bool mOptimizeBillboards = true; + float mSqrDistance = 0.f; osg::Vec3f mViewVector; mutable std::vector mNodePath; @@ -157,23 +151,27 @@ namespace MWRender } void handleCallbacks(const osg::Node* node, osg::Node *cloned) const { - const osg::Callback* callback = node->getCullCallback(); - while (callback) + for (const osg::Callback* callback = node->getCullCallback(); callback != nullptr; callback = callback->getNestedCallback()) { if (callback->className() == std::string("BillboardCallback")) - handleBillboard(cloned); - else { - if (node->getCullCallback()->getNestedCallback()) + if (mOptimizeBillboards) { - osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); - clonedCallback->setNestedCallback(nullptr); - cloned->addCullCallback(clonedCallback); + handleBillboard(cloned); + continue; } else - cloned->addCullCallback(const_cast(callback)); + cloned->setDataVariance(osg::Object::DYNAMIC); } - callback = callback->getNestedCallback(); + + if (node->getCullCallback()->getNestedCallback()) + { + osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); + clonedCallback->setNestedCallback(nullptr); + cloned->addCullCallback(clonedCallback); + } + else + cloned->addCullCallback(const_cast(callback)); } } void handleBillboard(osg::Node* node) const @@ -414,11 +412,11 @@ namespace MWRender } } + if (activeGrid) { OpenThreads::ScopedLock lock(mRefTrackerMutex); - if (activeGrid) - for (auto ref : getRefTracker().mBlacklist) - refs.erase(ref); + for (auto ref : getRefTracker().mBlacklist) + refs.erase(ref); } osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); @@ -444,9 +442,8 @@ namespace MWRender if (size < 1.f) { osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE; - if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y())) - continue; - if ((maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y())) + if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y()) + || (maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y())) continue; } @@ -455,11 +452,8 @@ namespace MWRender { OpenThreads::ScopedLock lock(mSizeCacheMutex); SizeCache::iterator found = mSizeCache.find(pair.first); - if (found != mSizeCache.end()) - { - if (found->second < dSqr*minSize*minSize) - continue; - } + if (found != mSizeCache.end() && found->second < dSqr*minSize*minSize) + continue; } std::string id = Misc::StringUtils::lowerCase(ref.mRefID); @@ -504,12 +498,10 @@ namespace MWRender } float radius2 = cnode->getBound().radius2() * ref.mScale*ref.mScale; - if (radius2 < dSqr*minSize*minSize) + if (radius2 < dSqr*minSize*minSize && !activeGrid) { OpenThreads::ScopedLock lock(mSizeCacheMutex); - { - mSizeCache[pair.first] = radius2; - } + mSizeCache[pair.first] = radius2; continue; } @@ -551,7 +543,7 @@ namespace MWRender const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); - if (minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged) + if (!activeGrid && minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged) continue; osg::Matrixf matrix; @@ -563,7 +555,9 @@ namespace MWRender osg::ref_ptr trans = new osg::MatrixTransform(matrix); trans->setDataVariance(osg::Object::STATIC); - CopyOp co = CopyOp(merge); + CopyOp co; + co.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES); + co.mOptimizeBillboards = (size > 1/4.f); co.mNodePath.push_back(trans); co.mSqrDistance = (viewPoint - pos).length2(); co.mViewVector = (viewPoint - worldCenter); @@ -611,7 +605,7 @@ namespace MWRender if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()*2*2) + if (size > 1/8.f) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index cb3a1b278..98771bbb4 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -1580,7 +1581,9 @@ void MWShadowTechnique::createShaders() _shadowCastingStateSet->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON); _shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false)); _shadowCastingStateSet->addUniform(_shadowMapAlphaTestDisableUniform); - + osg::ref_ptr depth = new osg::Depth; + depth->setWriteMask(true); + _shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON); _shadowCastingStateSet->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "RenderBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS); diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index f0da9e7e2..71cb8a2e5 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1543,6 +1544,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) geom->setUseVertexBufferObjects(true); geom->setUseDisplayList(false); } + if (_alphaBlendingActive && _mergeAlphaBlending && !geom->getStateSet()) + { + osg::Depth* d = new osg::Depth; + d->setWriteMask(0); + geom->getOrCreateStateSet()->setAttribute(d); + } } } From 26ab1763893e19b5f4d0b9eded3b86956829fa2f Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sat, 16 May 2020 13:37:00 +0000 Subject: [PATCH 43/81] profiling Signed-off-by: Bret Curtis --- apps/openmw/mwrender/animation.cpp | 2 +- apps/openmw/mwrender/objectpaging.cpp | 80 ++++++++++++++------------ apps/openmw/mwworld/store.cpp | 18 ++---- components/esm/cellref.cpp | 16 ------ components/esm/cellref.hpp | 16 +++++- components/nifosg/controller.cpp | 4 +- components/nifosg/particle.cpp | 2 +- components/resource/scenemanager.cpp | 2 +- components/sceneutil/clone.cpp | 19 ++---- components/sceneutil/clone.hpp | 2 - components/sceneutil/morphgeometry.cpp | 2 +- components/sceneutil/optimizer.cpp | 4 +- components/sceneutil/riggeometry.cpp | 6 +- 13 files changed, 78 insertions(+), 95 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 48c58f247..594713a1c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1372,7 +1372,7 @@ namespace MWRender osg::Group* sheathParent = findVisitor.mFoundNode; if (sheathParent) { - osg::Node* copy = osg::clone(nodePair.first, osg::CopyOp::DEEP_COPY_NODES); + osg::Node* copy = static_cast(nodePair.first->clone(osg::CopyOp::DEEP_COPY_NODES)); sheathParent->addChild(copy); } } diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index d65fffde8..71268a52c 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -107,8 +107,23 @@ namespace MWRender osg::Vec3f mViewVector; mutable std::vector mNodePath; + void copy(const osg::Node* toCopy, osg::Group* attachTo) + { + const osg::Group* groupToCopy = toCopy->asGroup(); + if (toCopy->getStateSet() || toCopy->asTransform() || !groupToCopy) + attachTo->addChild(operator()(toCopy)); + else + { + for (unsigned int i=0; igetNumChildren(); ++i) + attachTo->addChild(operator()(groupToCopy->getChild(i))); + } + } + virtual osg::Node* operator() (const osg::Node* node) const { + if (const osg::Drawable* d = node->asDrawable()) + return operator()(d); + if (dynamic_cast(node)) return nullptr; if (dynamic_cast(node)) @@ -133,12 +148,9 @@ namespace MWRender return n; } - if (const osg::Drawable* d = node->asDrawable()) - return operator()(d); - mNodePath.push_back(node); - osg::Node* cloned = osg::clone(node, *this); + osg::Node* cloned = static_cast(node->clone(*this)); cloned->setDataVariance(osg::Object::STATIC); cloned->setUserDataContainer(nullptr); cloned->setName(""); @@ -222,14 +234,14 @@ namespace MWRender if (getCopyFlags() & DEEP_COPY_DRAWABLES) { - osg::Drawable* d = osg::clone(drawable, *this); + osg::Drawable* d = static_cast(drawable->clone(*this)); d->setDataVariance(osg::Object::STATIC); d->setUserDataContainer(nullptr); d->setName(""); return d; } else - return osg::CopyOp::operator()(drawable); + return const_cast(drawable); } virtual osg::Callback* operator() (const osg::Callback* callback) const { @@ -388,8 +400,9 @@ namespace MWRender bool deleted = false; while(cell->getNextRef(esm[index], ref, deleted)) { + Misc::StringUtils::lowerCaseInPlace(ref.mRefID); if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; - int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + int type = store.findStatic(ref.mRefID); if (!typeFilter(type,size>=2)) continue; if (deleted) { refs.erase(ref.mRefNum); continue; } refs[ref.mRefNum] = ref; @@ -403,9 +416,10 @@ namespace MWRender for (ESM::CellRefTracker::const_iterator it = cell->mLeasedRefs.begin(); it != cell->mLeasedRefs.end(); ++it) { ESM::CellRef ref = it->first; + Misc::StringUtils::lowerCaseInPlace(ref.mRefID); bool deleted = it->second; if (deleted) { refs.erase(ref.mRefNum); continue; } - int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + int type = store.findStatic(ref.mRefID); if (!typeFilter(type,size>=2)) continue; refs[ref.mRefNum] = ref; } @@ -456,28 +470,23 @@ namespace MWRender continue; } - std::string id = Misc::StringUtils::lowerCase(ref.mRefID); - if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + if (ref.mRefID == "prisonmarker" || ref.mRefID == "divinemarker" || ref.mRefID == "templemarker" || ref.mRefID == "northmarker") continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - int type = store.findStatic(id); - std::string model = getModel(type, id, store); + int type = store.findStatic(ref.mRefID); + std::string model = getModel(type, ref.mRefID, store); if (model.empty()) continue; model = "meshes/" + model; - bool useAnim = type != ESM::REC_STAT; - if (useAnim) + if (activeGrid && type != ESM::REC_STAT) { model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); - if (activeGrid) + std::string kfname = Misc::StringUtils::lowerCase(model); + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) { - std::string kfname = Misc::StringUtils::lowerCase(model); - if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) - { - kfname.replace(kfname.size()-4, 4, ".kf"); - if (mSceneManager->getVFS()->exists(kfname)) - continue; - } + kfname.replace(kfname.size()-4, 4, ".kf"); + if (mSceneManager->getVFS()->exists(kfname)) + continue; } } @@ -521,6 +530,7 @@ namespace MWRender osg::ref_ptr mergeGroup = new osg::Group; osg::ref_ptr templateRefs = new TemplateRef; osgUtil::StateToCompile stateToCompile(0, nullptr); + CopyOp copyop; for (const auto& pair : nodes) { const osg::Node* cnode = pair.first; @@ -555,35 +565,29 @@ namespace MWRender osg::ref_ptr trans = new osg::MatrixTransform(matrix); trans->setDataVariance(osg::Object::STATIC); - CopyOp co; - co.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES); - co.mOptimizeBillboards = (size > 1/4.f); - co.mNodePath.push_back(trans); - co.mSqrDistance = (viewPoint - pos).length2(); - co.mViewVector = (viewPoint - worldCenter); - osg::ref_ptr node = osg::clone(cnode, co); - node->setUserDataContainer(nullptr); + copyop.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES); + copyop.mOptimizeBillboards = (size > 1/4.f); + copyop.mNodePath.push_back(trans); + copyop.mSqrDistance = (viewPoint - pos).length2(); + copyop.mViewVector = (viewPoint - worldCenter); + copyop.copy(cnode, trans); if (activeGrid) { if (merge) { AddRefnumMarkerVisitor visitor(ref.mRefNum); - node->accept(visitor); + trans->accept(visitor); } else { osg::ref_ptr marker = new RefnumMarker; marker->mRefnum = ref.mRefNum; - node->getOrCreateUserDataContainer()->addUserObject(marker); + trans->getOrCreateUserDataContainer()->addUserObject(marker); } } - trans->addChild(node); - - if (merge) - mergeGroup->addChild(trans); - else - group->addChild(trans); + osg::Group* attachTo = merge ? mergeGroup : group; + attachTo->addChild(trans); ++numinstances; } if (numinstances > 0) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 93f11d4fd..4c4417587 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -139,15 +139,12 @@ namespace MWWorld std::string idLower = Misc::StringUtils::lowerCase(id); typename Dynamic::const_iterator dit = mDynamic.find(idLower); - if (dit != mDynamic.end()) { + if (dit != mDynamic.end()) return &dit->second; - } typename std::map::const_iterator it = mStatic.find(idLower); - - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) return &(it->second); - } return 0; } @@ -156,10 +153,8 @@ namespace MWWorld { std::string idLower = Misc::StringUtils::lowerCase(id); typename std::map::const_iterator it = mStatic.find(idLower); - - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) return &(it->second); - } return 0; } @@ -289,7 +284,7 @@ namespace MWWorld typename std::map::iterator it = mStatic.find(idLower); - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) { // delete from the static part of mShared typename std::vector::iterator sharedIter = mShared.begin(); typename std::vector::iterator end = sharedIter + mStatic.size(); @@ -566,7 +561,7 @@ namespace MWWorld std::map::const_iterator it = mInt.find(cell.mName); - if (it != mInt.end() && Misc::StringUtils::ciEqual(it->second.mName, id)) { + if (it != mInt.end()) { return &(it->second); } @@ -1129,9 +1124,8 @@ namespace MWWorld { auto it = mStatic.find(Misc::StringUtils::lowerCase(id)); - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) mStatic.erase(it); - } return true; } diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index ab6ba2754..4b9852d65 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -232,19 +232,3 @@ void ESM::CellRef::blank() mPos.rot[i] = 0; } } - -bool ESM::operator== (const RefNum& left, const RefNum& right) -{ - return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile; -} - -bool ESM::operator< (const RefNum& left, const RefNum& right) -{ - if (left.mIndexright.mIndex) - return false; - - return left.mContentFileright.mIndex) + return false; + return left.mContentFilesetAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(static_cast(mBaseMaterial->clone(osg::CopyOp::DEEP_COPY_ALL)), osg::StateAttribute::ON); } void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) @@ -408,7 +408,7 @@ MaterialColorController::MaterialColorController(const MaterialColorController & void MaterialColorController::setDefaults(osg::StateSet *stateset) { - stateset->setAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(static_cast(mBaseMaterial->clone(osg::CopyOp::DEEP_COPY_ALL)), osg::StateAttribute::ON); } void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 1b1e469bc..804a8f8ab 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -277,7 +277,7 @@ Emitter::Emitter(const Emitter ©, const osg::CopyOp ©op) , mPlacer(copy.mPlacer) , mShooter(copy.mShooter) // need a deep copy because the remainder is stored in the object - , mCounter(osg::clone(copy.mCounter.get(), osg::CopyOp::DEEP_COPY_ALL)) + , mCounter(static_cast(copy.mCounter->clone(osg::CopyOp::DEEP_COPY_ALL))) { } diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 29b312670..d06a07273 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -577,7 +577,7 @@ namespace Resource osg::ref_ptr SceneManager::createInstance(const osg::Node *base) { - osg::ref_ptr cloned = osg::clone(base, SceneUtil::CopyOp()); + osg::ref_ptr cloned = static_cast(base->clone(SceneUtil::CopyOp())); // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache cloned->getOrCreateUserDataContainer()->addUserObject(new TemplateRef(base)); diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp index 0df0f4a5b..c3261515d 100644 --- a/components/sceneutil/clone.cpp +++ b/components/sceneutil/clone.cpp @@ -22,20 +22,11 @@ namespace SceneUtil | osg::CopyOp::DEEP_COPY_USERDATA); } - osg::StateSet* CopyOp::operator ()(const osg::StateSet* stateset) const - { - if (!stateset) - return nullptr; - if (stateset->getDataVariance() == osg::StateSet::DYNAMIC) - return osg::clone(stateset, *this); - return const_cast(stateset); - } - osg::Object* CopyOp::operator ()(const osg::Object* node) const { // We should copy node transformations when we copy node - if (const NifOsg::NodeUserData* data = dynamic_cast(node)) - return osg::clone(data, *this); + if (dynamic_cast(node)) + return static_cast(node->clone(*this)); return osg::CopyOp::operator()(node); } @@ -60,7 +51,7 @@ namespace SceneUtil if (dynamic_cast(drawable) || dynamic_cast(drawable)) { - return osg::clone(drawable, *this); + return static_cast(drawable->clone(*this)); } return osg::CopyOp::operator()(drawable); @@ -68,7 +59,7 @@ namespace SceneUtil osgParticle::ParticleProcessor* CopyOp::operator() (const osgParticle::ParticleProcessor* processor) const { - osgParticle::ParticleProcessor* cloned = osg::clone(processor, osg::CopyOp::DEEP_COPY_CALLBACKS); + osgParticle::ParticleProcessor* cloned = static_cast(processor->clone(osg::CopyOp::DEEP_COPY_CALLBACKS)); for (const auto& oldPsNewPsPair : mOldPsToNewPs) { if (processor->getParticleSystem() == oldPsNewPsPair.first) @@ -84,7 +75,7 @@ namespace SceneUtil osgParticle::ParticleSystem* CopyOp::operator ()(const osgParticle::ParticleSystem* partsys) const { - osgParticle::ParticleSystem* cloned = osg::clone(partsys, *this); + osgParticle::ParticleSystem* cloned = static_cast(partsys->clone(*this)); for (const auto& processorPsPair : mProcessorToOldPs) { diff --git a/components/sceneutil/clone.hpp b/components/sceneutil/clone.hpp index 20788799f..cf6d79e68 100644 --- a/components/sceneutil/clone.hpp +++ b/components/sceneutil/clone.hpp @@ -17,7 +17,6 @@ namespace SceneUtil /// @par Defines the cloning behaviour we need: /// * Assigns updated ParticleSystem pointers on cloned emitters and programs. - /// * Creates deep copy of StateSets if they have a DYNAMIC data variance. /// * Deep copies RigGeometry and MorphGeometry so they can animate without affecting clones. /// @warning Do not use an object of this class for more than one copy operation. class CopyOp : public osg::CopyOp @@ -31,7 +30,6 @@ namespace SceneUtil virtual osg::Node* operator() (const osg::Node* node) const; virtual osg::Drawable* operator() (const osg::Drawable* drawable) const; - virtual osg::StateSet* operator() (const osg::StateSet* stateset) const; virtual osg::Object* operator ()(const osg::Object* node) const; private: diff --git a/components/sceneutil/morphgeometry.cpp b/components/sceneutil/morphgeometry.cpp index 01bb35c45..04fd6fb36 100644 --- a/components/sceneutil/morphgeometry.cpp +++ b/components/sceneutil/morphgeometry.cpp @@ -44,7 +44,7 @@ void MorphGeometry::setSourceGeometry(osg::ref_ptr sourceGeom) osg::ref_ptr vbo (new osg::VertexBufferObject); vbo->setUsage(GL_DYNAMIC_DRAW_ARB); - osg::ref_ptr vertexArray = osg::clone(from.getVertexArray(), osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr vertexArray = static_cast(from.getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL)); if (vertexArray) { vertexArray->setVertexBufferObject(vbo); diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 71cb8a2e5..e8a945114 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -600,7 +600,7 @@ bool needvbo(const osg::Geometry* geom) osg::Array* cloneArray(osg::Array* array, osg::VertexBufferObject*& vbo, const osg::Geometry* geom) { - array = osg::clone(array, osg::CopyOp::DEEP_COPY_ALL); + array = static_cast(array->clone(osg::CopyOp::DEEP_COPY_ALL)); if (!vbo && needvbo(geom)) vbo = new osg::VertexBufferObject; if (vbo) @@ -1135,7 +1135,7 @@ osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps, osg::ElementBufferObjec { if (ps->referenceCount() <= 1) return ps; - ps = osg::clone(ps, osg::CopyOp::DEEP_COPY_ALL); + ps = static_cast(ps->clone(osg::CopyOp::DEEP_COPY_ALL)); osg::DrawElements* drawElements = ps->getDrawElements(); if (!drawElements) return ps; diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 627353b99..b9201fdf6 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -77,7 +77,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) osg::ref_ptr vbo (new osg::VertexBufferObject); vbo->setUsage(GL_DYNAMIC_DRAW_ARB); - osg::ref_ptr vertexArray = osg::clone(from.getVertexArray(), osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr vertexArray = static_cast(from.getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL)); if (vertexArray) { vertexArray->setVertexBufferObject(vbo); @@ -86,7 +86,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) if (const osg::Array* normals = from.getNormalArray()) { - osg::ref_ptr normalArray = osg::clone(normals, osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr normalArray = static_cast(normals->clone(osg::CopyOp::DEEP_COPY_ALL)); if (normalArray) { normalArray->setVertexBufferObject(vbo); @@ -97,7 +97,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) if (const osg::Vec4Array* tangents = dynamic_cast(from.getTexCoordArray(7))) { mSourceTangents = tangents; - osg::ref_ptr tangentArray = osg::clone(tangents, osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr tangentArray = static_cast(tangents->clone(osg::CopyOp::DEEP_COPY_ALL)); tangentArray->setVertexBufferObject(vbo); to.setTexCoordArray(7, tangentArray, osg::Array::BIND_PER_VERTEX); } From 13c4e4b2a990516d0413615247baf7bd4e0006b7 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 12 Jun 2020 22:35:38 +0000 Subject: [PATCH 44/81] Fix ifs for ACTIVATE_MSVC --- CI/before_script.msvc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 90654559b..2ecea7ebc 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -957,7 +957,7 @@ fi echo #fi -if ! [ -z $ACTIVATE_MSVC ]; then +if [ -n "$ACTIVATE_MSVC" ]; then echo -n "- Activating MSVC in the current shell... " command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; } @@ -1007,7 +1007,7 @@ if [ -z $VERBOSE ]; then fi fi -if [ -n $ACTIVATE_MSVC ]; then +if [ -n "$ACTIVATE_MSVC" ]; then echo echo "Note: you must manually activate MSVC for the shell in which you want to do the build." echo From 0d2129ca135834d5eab7fefc7179e42f8363b891 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 12 Jun 2020 22:50:06 +0000 Subject: [PATCH 45/81] Add success message to Windows prebuild script --- CI/before_script.msvc.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 90654559b..deeadb9e8 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -1007,6 +1007,9 @@ if [ -z $VERBOSE ]; then fi fi +echo "Script completed successfully." +echo "You now have an OpenMW build system at $(unixPathAsWindows "$(pwd)")" + if [ -n $ACTIVATE_MSVC ]; then echo echo "Note: you must manually activate MSVC for the shell in which you want to do the build." From a35497de0cc18a9797584716d7fea53ea3c1dbb7 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 13 Jun 2020 00:39:57 +0200 Subject: [PATCH 46/81] Remove redundant runSpeed as always equal to walkSpeed --- apps/openmw/mwclass/creature.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 71c96b06b..470a28b7e 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -513,17 +513,12 @@ namespace MWClass const GMST& gmst = getGmst(); - float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() + const float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat()); const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); - bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); - - // The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp) - float runSpeed = walkSpeed; - float moveSpeed; if(getEncumbrance(ptr) > getCapacity(ptr)) @@ -542,15 +537,11 @@ namespace MWClass else if(world->isSwimming(ptr)) { float swimSpeed = walkSpeed; - if(running) - swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat(); moveSpeed = swimSpeed; } - else if(running) - moveSpeed = runSpeed; else moveSpeed = walkSpeed; if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) From 439588d10ec6c53c99ce942fc677b5f829a3902c Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 13 Jun 2020 02:09:10 +0200 Subject: [PATCH 47/81] Remove unused mOffMeshConnectionIds --- components/detournavigator/navigatorimpl.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 66a4d8bb3..3a81adb97 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -64,7 +64,6 @@ namespace DetourNavigator std::map mAgents; std::unordered_map mAvoidIds; std::unordered_map mWaterIds; - std::multimap mOffMeshConnectionIds; void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId); void updateWaterShapeId(const ObjectId id, const ObjectId waterId); From 374b85a00d2f5e1e40b7ca9bc84ac9804236ba77 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 13 Jun 2020 01:04:55 +0200 Subject: [PATCH 48/81] Add Class methods to get walk, run, swim speed --- apps/openmw/mwclass/creature.cpp | 43 +++++++++++++----- apps/openmw/mwclass/creature.hpp | 6 +++ apps/openmw/mwclass/npc.cpp | 76 +++++++++++++++++++++++--------- apps/openmw/mwclass/npc.hpp | 6 +++ apps/openmw/mwworld/class.cpp | 15 +++++++ apps/openmw/mwworld/class.hpp | 6 +++ 6 files changed, 119 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 470a28b7e..88defbaa0 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -507,15 +507,13 @@ namespace MWClass float Creature::getSpeed(const MWWorld::Ptr &ptr) const { - MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) return 0.f; const GMST& gmst = getGmst(); - const float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() - * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat()); - const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); @@ -535,15 +533,9 @@ namespace MWClass moveSpeed = flySpeed; } else if(world->isSwimming(ptr)) - { - float swimSpeed = walkSpeed; - swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); - swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * - gmst.fSwimRunAthleticsMult->mValue.getFloat(); - moveSpeed = swimSpeed; - } + moveSpeed = getSwimSpeed(ptr); else - moveSpeed = walkSpeed; + moveSpeed = getWalkSpeed(ptr); if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) moveSpeed *= 0.75f; @@ -880,4 +872,31 @@ namespace MWClass { MWMechanics::setBaseAISetting(id, setting, value); } + + float Creature::getWalkSpeed(const MWWorld::Ptr& ptr) const + { + const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + const GMST& gmst = getGmst(); + + return gmst.fMinWalkSpeedCreature->mValue.getFloat() + + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() + * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat()); + } + + float Creature::getRunSpeed(const MWWorld::Ptr& ptr) const + { + return getWalkSpeed(ptr); + } + + float Creature::getSwimSpeed(const MWWorld::Ptr& ptr) const + { + const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + const GMST& gmst = getGmst(); + const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects(); + + return getWalkSpeed(ptr) + * (1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude()) + * (gmst.fSwimRunBase->mValue.getFloat() + + 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat()); + } } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 3288c0d11..9071b9a33 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -131,6 +131,12 @@ namespace MWClass /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; + + float getWalkSpeed(const MWWorld::Ptr& ptr) const final; + + float getRunSpeed(const MWWorld::Ptr& ptr) const final; + + float getSwimSpeed(const MWWorld::Ptr& ptr) const final; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 3b9c46c8f..319d5f014 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -947,16 +947,6 @@ namespace MWClass bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr); running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr)); - float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* - (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat()); - walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance; - walkSpeed = std::max(0.0f, walkSpeed); - if(sneaking) - walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat(); - - float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * - gmst.fAthleticsRunBonus->mValue.getFloat() + gmst.fBaseRunMultiplier->mValue.getFloat()); - float moveSpeed; if(getEncumbrance(ptr) > getCapacity(ptr)) moveSpeed = 0.0f; @@ -971,19 +961,11 @@ namespace MWClass moveSpeed = flySpeed; } else if (swimming) - { - float swimSpeed = walkSpeed; - if(running) - swimSpeed = runSpeed; - swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); - swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics)* - gmst.fSwimRunAthleticsMult->mValue.getFloat(); - moveSpeed = swimSpeed; - } + moveSpeed = getSwimSpeed(ptr); else if (running && !sneaking) - moveSpeed = runSpeed; + moveSpeed = getRunSpeed(ptr); else - moveSpeed = walkSpeed; + moveSpeed = getWalkSpeed(ptr); if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) moveSpeed *= 0.75f; @@ -1448,4 +1430,56 @@ namespace MWClass { MWMechanics::setBaseAISetting(id, setting, value); } + + float Npc::getWalkSpeed(const MWWorld::Ptr& ptr) const + { + const GMST& gmst = getGmst(); + const NpcCustomData* npcdata = static_cast(ptr.getRefData().getCustomData()); + const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); + const bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr); + + float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + + 0.01f * npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + * (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat()); + walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance; + walkSpeed = std::max(0.0f, walkSpeed); + if(sneaking) + walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat(); + + return walkSpeed; + } + + float Npc::getRunSpeed(const MWWorld::Ptr& ptr) const + { + const GMST& gmst = getGmst(); + return getWalkSpeed(ptr) + * (0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fAthleticsRunBonus->mValue.getFloat() + + gmst.fBaseRunMultiplier->mValue.getFloat()); + } + + float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const + { + const GMST& gmst = getGmst(); + const MWBase::World* world = MWBase::Environment::get().getWorld(); + const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + const NpcCustomData* npcdata = static_cast(ptr.getRefData().getCustomData()); + const MWMechanics::MagicEffects& mageffects = npcdata->mNpcStats.getMagicEffects(); + const bool swimming = world->isSwimming(ptr); + const bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr); + const bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run) + && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr)); + + float swimSpeed; + + if (running) + swimSpeed = getRunSpeed(ptr); + else + swimSpeed = getWalkSpeed(ptr); + + swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); + swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + + 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat(); + + return swimSpeed; + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index ae4f32d13..d92293acb 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -166,6 +166,12 @@ namespace MWClass virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const; virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; + + float getWalkSpeed(const MWWorld::Ptr& ptr) const final; + + float getRunSpeed(const MWWorld::Ptr& ptr) const final; + + float getSwimSpeed(const MWWorld::Ptr& ptr) const final; }; } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index d7ee59ee2..b59532f2a 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -521,4 +521,19 @@ namespace MWWorld { throw std::runtime_error ("class does not have creature stats"); } + + float Class::getWalkSpeed(const Ptr& /*ptr*/) const + { + return 0; + } + + float Class::getRunSpeed(const Ptr& /*ptr*/) const + { + return 0; + } + + float Class::getSwimSpeed(const Ptr& /*ptr*/) const + { + return 0; + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index fd679c43f..f92dc0373 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -362,6 +362,12 @@ namespace MWWorld virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; + + virtual float getWalkSpeed(const Ptr& ptr) const; + + virtual float getRunSpeed(const Ptr& ptr) const; + + virtual float getSwimSpeed(const Ptr& ptr) const; }; } From b095ca6c867285e3cccb9c8675e21b5b6503d0ea Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 13 Jun 2020 01:56:04 +0200 Subject: [PATCH 49/81] Use actor speed to define area cost for pathfinding --- apps/openmw/mwmechanics/aipackage.cpp | 26 ++++++++++++++- apps/openmw/mwmechanics/aipackage.hpp | 3 ++ apps/openmw/mwmechanics/aiwander.cpp | 9 +++-- apps/openmw/mwmechanics/pathfinding.cpp | 19 ++++++----- apps/openmw/mwmechanics/pathfinding.hpp | 10 +++--- .../detournavigator/navigator.cpp | 33 ++++++++++--------- components/detournavigator/areatype.hpp | 8 +++++ components/detournavigator/findsmoothpath.hpp | 7 +++- components/detournavigator/navigator.hpp | 5 +-- 9 files changed, 84 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 0c06697dc..e322f57ce 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -114,7 +114,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& { const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor); mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()), - pathfindingHalfExtents, getNavigatorFlags(actor)); + pathfindingHalfExtents, getNavigatorFlags(actor), getAreaCosts(actor)); mRotateOnTheRunChecks = 3; // give priority to go directly on target if there is minimal opportunity @@ -402,3 +402,27 @@ DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld:: return result; } + +DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::Ptr& actor) const +{ + DetourNavigator::AreaCosts costs; + const DetourNavigator::Flags flags = getNavigatorFlags(actor); + const MWWorld::Class& actorClass = actor.getClass(); + + if (flags & DetourNavigator::Flag_swim) + costs.mWater = costs.mWater / actorClass.getSwimSpeed(actor); + + if (flags & DetourNavigator::Flag_walk) + { + float walkCost; + if (getTypeId() == TypeIdWander) + walkCost = 1.0 / actorClass.getWalkSpeed(actor); + else + walkCost = 1.0 / actorClass.getRunSpeed(actor); + costs.mDoor = costs.mDoor * walkCost; + costs.mPathgrid = costs.mPathgrid * walkCost; + costs.mGround = costs.mGround * walkCost; + } + + return costs; +} diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index c32fb93aa..477976616 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -4,6 +4,7 @@ #include #include +#include #include "pathfinding.hpp" #include "obstacle.hpp" @@ -167,6 +168,8 @@ namespace MWMechanics DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; + DetourNavigator::AreaCosts getAreaCosts(const MWWorld::Ptr& actor) const; + const TypeId mTypeId; const Options mOptions; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 015859c4b..11c50dc09 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -202,7 +202,7 @@ namespace MWMechanics { const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(), - getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor)); + getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor), getAreaCosts(actor)); } if (mPathFinder.isPathConstructed()) @@ -337,6 +337,7 @@ namespace MWMechanics const auto halfExtents = world->getPathfindingHalfExtents(actor); const auto navigator = world->getNavigator(); const auto navigatorFlags = getNavigatorFlags(actor); + const auto areaCosts = getAreaCosts(actor); do { // Determine a random location within radius of original position @@ -365,7 +366,8 @@ namespace MWMechanics if (isWaterCreature || isFlyingCreature) mPathFinder.buildStraightPath(mDestination); else - mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags); + mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags, + areaCosts); if (mPathFinder.isPathConstructed()) { @@ -496,7 +498,8 @@ namespace MWMechanics if (mUsePathgrid) { const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); - mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor)); + mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor), + getAreaCosts(actor)); } if (mObstacleCheck.isEvading()) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index d00f2615e..4d4b5be51 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -309,12 +309,13 @@ namespace MWMechanics } void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, - const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags) + const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, + const DetourNavigator::AreaCosts& areaCosts) { mPath.clear(); // If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path - if (!buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath))) + if (!buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, areaCosts, std::back_inserter(mPath))) mPath.push_back(endPoint); mConstructed = true; @@ -322,7 +323,7 @@ namespace MWMechanics 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) + const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts) { mPath.clear(); mCell = cell; @@ -330,11 +331,11 @@ namespace MWMechanics bool hasNavMesh = false; if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor)) - hasNavMesh = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); + hasNavMesh = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, areaCosts, std::back_inserter(mPath)); if (hasNavMesh && mPath.empty()) buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, - flags | DetourNavigator::Flag_usePathgrid, std::back_inserter(mPath)); + flags | DetourNavigator::Flag_usePathgrid, areaCosts, std::back_inserter(mPath)); if (mPath.empty()) buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath)); @@ -347,12 +348,12 @@ namespace MWMechanics bool PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, - std::back_insert_iterator> out) + const DetourNavigator::AreaCosts& areaCosts, std::back_insert_iterator> out) { const auto world = MWBase::Environment::get().getWorld(); const auto stepSize = getPathStepSize(actor); const auto navigator = world->getNavigator(); - const auto status = navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out); + const auto status = navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, areaCosts, out); if (status == DetourNavigator::Status::NavMeshNotFound) return false; @@ -369,7 +370,7 @@ namespace MWMechanics } void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, - const DetourNavigator::Flags flags) + const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts) { if (mPath.empty()) return; @@ -383,7 +384,7 @@ namespace MWMechanics const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); std::deque prePath; auto prePathInserter = std::back_inserter(prePath); - const auto status = navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, + const auto status = navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, areaCosts, prePathInserter); if (status == DetourNavigator::Status::NavMeshNotFound) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index cb33471ca..5af822fee 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -90,14 +91,15 @@ namespace MWMechanics 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); + const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, + const DetourNavigator::AreaCosts& areaCosts); 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); + const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts); void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, - const DetourNavigator::Flags flags); + const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts); /// Remove front point if exist and within tolerance void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance); @@ -203,7 +205,7 @@ namespace MWMechanics bool buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, - std::back_insert_iterator> out); + const DetourNavigator::AreaCosts& areaCosts, std::back_insert_iterator> out); }; } diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 276877508..5a92d5d28 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -37,6 +37,7 @@ namespace std::deque mPath; std::back_insert_iterator> mOut; float mStepSize; + AreaCosts mAreaCosts; DetourNavigatorNavigatorTest() : mPlayerPosition(0, 0, 0) @@ -80,7 +81,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty) { - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::NavMeshNotFound); EXPECT_EQ(mPath, std::deque()); } @@ -88,7 +89,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception) { mNavigator->addAgent(mAgentHalfExtents); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::StartPolygonNotFound); } @@ -97,7 +98,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents); mNavigator->removeAgent(mAgentHalfExtents); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::StartPolygonNotFound); } @@ -118,7 +119,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.85963428020477294921875), @@ -168,7 +169,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.85963428020477294921875), @@ -202,7 +203,7 @@ namespace mPath.clear(); mOut = std::back_inserter(mPath); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.87826788425445556640625), @@ -253,7 +254,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.87826788425445556640625), @@ -289,7 +290,7 @@ namespace mPath.clear(); mOut = std::back_inserter(mPath); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.85963428020477294921875), @@ -346,7 +347,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.96328866481781005859375), @@ -402,7 +403,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.9393787384033203125), @@ -455,7 +456,7 @@ namespace mEnd.x() = 0; mEnd.z() = 300; - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim, mAreaCosts, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(0, 215, 185.33331298828125), @@ -501,7 +502,7 @@ namespace mStart.x() = 0; mEnd.x() = 0; - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mOut), + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ @@ -548,7 +549,7 @@ namespace mStart.x() = 0; mEnd.x() = 0; - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mOut), + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ @@ -595,7 +596,7 @@ namespace mStart.x() = 0; mEnd.x() = 0; - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(0, 215, -94.75363922119140625), @@ -644,7 +645,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.85963428020477294921875), @@ -739,7 +740,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.8782780170440673828125), diff --git a/components/detournavigator/areatype.hpp b/components/detournavigator/areatype.hpp index 962a67847..9d99421af 100644 --- a/components/detournavigator/areatype.hpp +++ b/components/detournavigator/areatype.hpp @@ -13,6 +13,14 @@ namespace DetourNavigator AreaType_pathgrid, AreaType_ground = RC_WALKABLE_AREA, }; + + struct AreaCosts + { + float mWater = 1.0f; + float mDoor = 2.0f; + float mPathgrid = 1.0f; + float mGround = 1.0f; + }; } #endif diff --git a/components/detournavigator/findsmoothpath.hpp b/components/detournavigator/findsmoothpath.hpp index 0f8f2c09a..f1de71207 100644 --- a/components/detournavigator/findsmoothpath.hpp +++ b/components/detournavigator/findsmoothpath.hpp @@ -8,6 +8,7 @@ #include "settingsutils.hpp" #include "debug.hpp" #include "status.hpp" +#include "areatype.hpp" #include #include @@ -269,7 +270,7 @@ namespace DetourNavigator template Status findSmoothPath(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, const float stepSize, - const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, + const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const AreaCosts& areaCosts, const Settings& settings, OutputIterator& out) { dtNavMeshQuery navMeshQuery; @@ -278,6 +279,10 @@ namespace DetourNavigator dtQueryFilter queryFilter; queryFilter.setIncludeFlags(includeFlags); + queryFilter.setAreaCost(AreaType_water, areaCosts.mWater); + queryFilter.setAreaCost(AreaType_door, areaCosts.mDoor); + queryFilter.setAreaCost(AreaType_pathgrid, areaCosts.mPathgrid); + queryFilter.setAreaCost(AreaType_ground, areaCosts.mGround); dtPolyRef startRef = 0; osg::Vec3f startPolygonPosition; diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index cfdf92232..391c63022 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -172,7 +172,8 @@ namespace DetourNavigator */ template Status findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start, - const osg::Vec3f& end, const Flags includeFlags, OutputIterator& out) const + const osg::Vec3f& end, const Flags includeFlags, const DetourNavigator::AreaCosts& areaCosts, + OutputIterator& out) const { static_assert( std::is_same< @@ -187,7 +188,7 @@ namespace DetourNavigator const auto settings = getSettings(); return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start), - toNavMeshCoordinates(settings, end), includeFlags, settings, out); + toNavMeshCoordinates(settings, end), includeFlags, areaCosts, settings, out); } /** From a6493ce3292eee305701f7cc987fca7223c9897b Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 13 Jun 2020 01:51:27 +0100 Subject: [PATCH 50/81] Don't exit prebuild script on nonzero exit code when we already check it We have `set -e` enabled, so normally exit the script if a command fails. We also had explicit exit code checks in a few places with user-friendly error messages. These were never printed as the script exited before we could check the exit code due to a bad exit code. --- CI/before_script.msvc.sh | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 90654559b..59bf4cb37 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -213,8 +213,8 @@ run_cmd() { shift if [ -z $VERBOSE ]; then - eval $CMD $@ > output.log 2>&1 - RET=$? + RET=0 + eval $CMD $@ > output.log 2>&1 || RET=$? if [ $RET -ne 0 ]; then if [ -z $APPVEYOR ]; then @@ -230,8 +230,9 @@ run_cmd() { return $RET else - eval $CMD $@ - return $? + RET=0 + eval $CMD $@ || RET=$? + return RET fi } @@ -256,15 +257,16 @@ download() { printf " Downloading $FILE... " if [ -z $VERBOSE ]; then - curl --silent --retry 10 -kLy 5 -o $FILE $URL - RET=$? + RET=0 + curl --silent --retry 10 -kLy 5 -o $FILE $URL || RET=$? else - curl --retry 10 -kLy 5 -o $FILE $URL - RET=$? + RET=0 + curl --retry 10 -kLy 5 -o $FILE $URL || RET=$? fi if [ $RET -ne 0 ]; then echo "Failed!" + wrappedExit $RET else echo "Done." fi @@ -997,8 +999,8 @@ if [ -z $VERBOSE ]; then else echo "- cmake .. $CMAKE_OPTS" fi -run_cmd cmake .. $CMAKE_OPTS -RET=$? +RET=0 +run_cmd cmake .. $CMAKE_OPTS || RET=$? if [ -z $VERBOSE ]; then if [ $RET -eq 0 ]; then echo Done. @@ -1006,6 +1008,9 @@ if [ -z $VERBOSE ]; then echo Failed. fi fi +if [ $RET -ne 0 ]; then + wrappedExit $RET +fi if [ -n $ACTIVATE_MSVC ]; then echo From 6f94848dec1a313f00cedf534738ee13b0db8214 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 14 Jun 2020 23:02:03 +0300 Subject: [PATCH 51/81] Remove 63 UV set limit (now 65535) --- components/nif/data.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index d19c8321e..f8b6da036 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -47,10 +47,9 @@ void ShapeData::read(NIFStream *nif) if(nif->getInt()) nif->getVector4s(colors, verts); - // Only the first 6 bits are used as a count. I think the rest are - // flags of some sort. + // In Morrowind this field only corresponds to the number of UV sets. + // NifTools research is inaccurate. int uvs = nif->getUShort(); - uvs &= 0x3f; if(nif->getInt()) { From 4b831f99da3c65c76213c593aad7e773f90951a6 Mon Sep 17 00:00:00 2001 From: apommel Date: Mon, 15 Jun 2020 10:13:22 +0900 Subject: [PATCH 52/81] Allow vswhere to detect build tools installations --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 90654559b..0f541f53f 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -961,7 +961,7 @@ if ! [ -z $ACTIVATE_MSVC ]; then echo -n "- Activating MSVC in the current shell... " command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; } - MSVC_INSTALLATION_PATH=$(vswhere -legacy -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) + MSVC_INSTALLATION_PATH=$(vswhere -legacy -products '*' -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) if [ $MSVC_REAL_VER -ge 15 ]; then echo "@\"${MSVC_INSTALLATION_PATH}\Common7\Tools\VsDevCmd.bat\" -no_logo -arch=$([ $BITS -eq 64 ] && echo "amd64" || echo "x86") -host_arch=$([ $(uname -m) == 'x86_64' ] && echo "amd64" || echo "x86")" > ActivateMSVC.bat else From f30cb9f8bc0c81cb5a5b96d6e87303b00e65f32d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 15 Jun 2020 12:43:51 +0400 Subject: [PATCH 53/81] Calculate viewNormal only when needed --- files/shaders/objects_fragment.glsl | 9 ++++++++- files/shaders/objects_vertex.glsl | 4 ++++ files/shaders/terrain_fragment.glsl | 11 +++++++++-- files/shaders/terrain_vertex.glsl | 6 +++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 74a0cc80f..ece53f047 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -83,7 +83,9 @@ void main() mat3 tbnTranspose = mat3(normalizedTangent, binormal, normalizedNormal); vec3 viewNormal = gl_NormalMatrix * normalize(tbnTranspose * (normalTex.xyz * 2.0 - 1.0)); -#else +#endif + +#if (!@normalMap && (@parallax || @forcePPL)) vec3 viewNormal = gl_NormalMatrix * normalize(passNormal); #endif @@ -184,7 +186,12 @@ void main() #endif if (matSpec != vec3(0.0)) + { +#if (!normalMap && !@parallax && !forcePPL) + vec3 viewNormal = gl_NormalMatrix * normalize(passNormal); +#endif gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; + } #if @radialFog float depth; // For the less detailed mesh of simple water we need to recalculate depth on per-pixel basis diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index bd97b2526..40c448de9 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -63,7 +63,9 @@ void main(void) euclideanDepth = length(viewPos.xyz); linearDepth = gl_Position.z; +#if (@envMap || !PER_PIXEL_LIGHTING || @shadows_enabled) vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); +#endif #if @envMap vec3 viewVec = normalize(viewPos.xyz); @@ -112,5 +114,7 @@ void main(void) passViewPos = viewPos.xyz; passNormal = gl_Normal.xyz; +#if (@shadows_enabled) setupShadowCoords(viewPos, viewNormal); +#endif } diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index ffc19fac0..21e50b4e9 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -43,8 +43,10 @@ void main() mat3 tbnTranspose = mat3(tangent, binormal, normalizedNormal); vec3 viewNormal = normalize(gl_NormalMatrix * (tbnTranspose * (normalTex.xyz * 2.0 - 1.0))); -#else - vec3 viewNormal = normalize(gl_NormalMatrix * passNormal); +#endif + +#if (!@normalMap && (@parallax || @forcePPL)) + vec3 viewNormal = gl_NormalMatrix * normalize(passNormal); #endif #if @parallax @@ -93,7 +95,12 @@ void main() #endif if (matSpec != vec3(0.0)) + { +#if (!normalMap && !@parallax && !forcePPL) + vec3 viewNormal = gl_NormalMatrix * normalize(passNormal); +#endif gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing; + } #if @radialFog float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); diff --git a/files/shaders/terrain_vertex.glsl b/files/shaders/terrain_vertex.glsl index dc4ea802f..bf337cf54 100644 --- a/files/shaders/terrain_vertex.glsl +++ b/files/shaders/terrain_vertex.glsl @@ -26,8 +26,10 @@ void main(void) gl_ClipVertex = viewPos; euclideanDepth = length(viewPos.xyz); linearDepth = gl_Position.z; - + +#if (!PER_PIXEL_LIGHTING || @shadows_enabled) vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); +#endif #if !PER_PIXEL_LIGHTING lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting); @@ -38,5 +40,7 @@ void main(void) uv = gl_MultiTexCoord0.xy; +#if (@shadows_enabled) setupShadowCoords(viewPos, viewNormal); +#endif } From 761558f6129349680b78fb7f3b45c112aae28a93 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 19 May 2020 01:32:08 +0100 Subject: [PATCH 54/81] Remove test data A dummy command was used to check the script would fail if a command was missing. Not being a real command, it always made the script fail as a command was missing. --- CI/ActivateMSVC.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/ActivateMSVC.ps1 b/CI/ActivateMSVC.ps1 index ca78ef588..ae73acfe1 100644 --- a/CI/ActivateMSVC.ps1 +++ b/CI/ActivateMSVC.ps1 @@ -4,8 +4,8 @@ } $MissingTools = $false -$tools = "cl", "link", "rc", "mt", "awooga" -$descriptions = "MSVC Compiler", "MSVC Linker", "MS Windows Resource Compiler", "MS Windows Manifest Tool", "A made up command" +$tools = "cl", "link", "rc", "mt" +$descriptions = "MSVC Compiler", "MSVC Linker", "MS Windows Resource Compiler", "MS Windows Manifest Tool" for ($i = 0; $i -lt $tools.Length; $i++) { $present = $true try { From 079be5d485083d3646ec8e53ad7cbeb09cbc7b2a Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 19 May 2020 17:50:18 +0100 Subject: [PATCH 55/81] Remove annoying warning --- CI/ActivateMSVC.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CI/ActivateMSVC.ps1 b/CI/ActivateMSVC.ps1 index ae73acfe1..a04a23a93 100644 --- a/CI/ActivateMSVC.ps1 +++ b/CI/ActivateMSVC.ps1 @@ -1,6 +1,8 @@ & "${env:COMSPEC}" /c ActivateMSVC.bat "&&" set | ForEach-Object { - $name, $value = $_ -split '=', 2 - Set-Content env:\"$name" $value + if ($_.Contains("=")) { + $name, $value = $_ -split '=', 2 + Set-Content env:\"$name" $value + } } $MissingTools = $false From 00197e1cd9ee645f6c6fde201231258281dadf36 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 15 Jun 2020 23:53:22 +0200 Subject: [PATCH 56/81] Optimize recast mesh size by vertex deduplication --- .../detournavigator/operators.hpp | 34 ++++++-- .../detournavigator/recastmeshbuilder.cpp | 80 +++++++++++-------- .../detournavigator/recastmeshbuilder.cpp | 42 +++++++++- .../detournavigator/recastmeshbuilder.hpp | 2 +- 4 files changed, 116 insertions(+), 42 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/operators.hpp b/apps/openmw_test_suite/detournavigator/operators.hpp index e34d6278a..92740c65f 100644 --- a/apps/openmw_test_suite/detournavigator/operators.hpp +++ b/apps/openmw_test_suite/detournavigator/operators.hpp @@ -23,17 +23,23 @@ namespace DetourNavigator namespace { template - struct Wrapper { + struct Wrapper + { const T& mValue; }; template - inline testing::Message& writeRange(testing::Message& message, const Range& range) + inline testing::Message& writeRange(testing::Message& message, const Range& range, std::size_t newLine) { - message << "{\n"; + message << "{"; + std::size_t i = 0; for (const auto& v : range) - message << Wrapper::type> {v} << ",\n"; - return message << "}"; + { + if (i++ % newLine == 0) + message << "\n"; + message << Wrapper::type> {v} << ", "; + } + return message << "\n}"; } } @@ -60,22 +66,34 @@ namespace testing return (*this) << std::setprecision(std::numeric_limits::max_exponent10) << value.mValue; } + template <> + inline testing::Message& Message::operator <<(const Wrapper& value) + { + return (*this) << value.mValue; + } + template <> inline testing::Message& Message::operator <<(const std::deque& value) { - return writeRange(*this, value); + return writeRange(*this, value, 1); } template <> inline testing::Message& Message::operator <<(const std::vector& value) { - return writeRange(*this, value); + return writeRange(*this, value, 1); } template <> inline testing::Message& Message::operator <<(const std::vector& value) { - return writeRange(*this, value); + return writeRange(*this, value, 3); + } + + template <> + inline testing::Message& Message::operator <<(const std::vector& value) + { + return writeRange(*this, value, 3); } } diff --git a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp index c86dee6e5..bcbf448ac 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp @@ -104,11 +104,9 @@ namespace -0.5, 0, -0.5, -0.5, 0, 0.5, 0.5, 0, -0.5, - 0.5, 0, -0.5, - -0.5, 0, 0.5, 0.5, 0, 0.5, })); - EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2, 3, 4, 5})); + EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2, 2, 1, 3})); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector({AreaType_ground, AreaType_ground})); } @@ -127,7 +125,7 @@ namespace -1, -2, 1, 1, -2, -1, -1, -2, -1, - })); + })) << recastMesh->getVertices(); EXPECT_EQ(recastMesh->getIndices(), std::vector({ 0, 2, 3, 3, 1, 0, @@ -141,7 +139,7 @@ namespace 2, 6, 7, 7, 6, 4, 4, 5, 7, - })); + })) << recastMesh->getIndices(); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector(12, AreaType_ground)); } @@ -166,37 +164,35 @@ namespace ); const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ - 1, 0, -1, - -1, 0, 1, - -1, 0, -1, - 1, 2, 1, - -1, 2, 1, - 1, 2, -1, - -1, 2, -1, - 1, -2, 1, - -1, -2, 1, - 1, -2, -1, -1, -2, -1, - 1, 0, -1, + -1, -2, 1, + -1, 0, -1, -1, 0, 1, + -1, 2, -1, + -1, 2, 1, + 1, -2, -1, + 1, -2, 1, + 1, 0, -1, 1, 0, 1, - })); + 1, 2, -1, + 1, 2, 1, + })) << recastMesh->getVertices(); EXPECT_EQ(recastMesh->getIndices(), std::vector({ - 0, 1, 2, - 3, 5, 6, - 6, 4, 3, - 3, 7, 9, - 9, 5, 3, - 3, 4, 8, - 8, 7, 3, - 10, 8, 4, - 4, 6, 10, - 10, 6, 5, - 5, 9, 10, - 10, 9, 7, - 7, 8, 10, - 11, 12, 13, - })); + 8, 3, 2, + 11, 10, 4, + 4, 5, 11, + 11, 7, 6, + 6, 10, 11, + 11, 5, 1, + 1, 7, 11, + 0, 1, 5, + 5, 4, 0, + 0, 4, 10, + 10, 6, 0, + 0, 6, 7, + 7, 1, 0, + 8, 3, 9, + })) << recastMesh->getIndices(); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector(14, AreaType_ground)); } @@ -413,4 +409,24 @@ namespace RecastMesh::Water {1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))} })); } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, add_bhv_triangle_mesh_shape_with_duplicated_vertices) + { + btTriangleMesh mesh; + mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); + mesh.addTriangle(btVector3(1, 1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); + btBvhTriangleMeshShape shape(&mesh, true); + + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), btTransform::getIdentity(), AreaType_ground); + const auto recastMesh = builder.create(mGeneration, mRevision); + EXPECT_EQ(recastMesh->getVertices(), std::vector({ + -1, 0, -1, + -1, 0, 1, + 1, 0, -1, + 1, 0, 1, + })) << recastMesh->getVertices(); + EXPECT_EQ(recastMesh->getIndices(), std::vector({2, 1, 0, 2, 1, 3})); + EXPECT_EQ(recastMesh->getAreaTypes(), std::vector({AreaType_ground, AreaType_ground})); + } } diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index f61368357..59f60394d 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -17,11 +17,50 @@ #include #include +#include namespace DetourNavigator { using BulletHelpers::makeProcessTriangleCallback; + namespace + { + void optimizeRecastMesh(std::vector& indices, std::vector& vertices) + { + std::vector> uniqueVertices; + uniqueVertices.reserve(vertices.size() / 3); + + for (std::size_t i = 0, n = vertices.size() / 3; i < n; ++i) + uniqueVertices.emplace_back(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]); + + std::sort(uniqueVertices.begin(), uniqueVertices.end()); + const auto end = std::unique(uniqueVertices.begin(), uniqueVertices.end()); + uniqueVertices.erase(end, uniqueVertices.end()); + + if (uniqueVertices.size() == vertices.size() / 3) + return; + + for (std::size_t i = 0, n = indices.size(); i < n; ++i) + { + const auto index = indices[i]; + const auto vertex = std::make_tuple(vertices[index * 3], vertices[index * 3 + 1], vertices[index * 3 + 2]); + const auto it = std::lower_bound(uniqueVertices.begin(), uniqueVertices.end(), vertex); + assert(it != uniqueVertices.end()); + assert(*it == vertex); + indices[i] = std::distance(uniqueVertices.begin(), it); + } + + vertices.resize(uniqueVertices.size() * 3); + + for (std::size_t i = 0, n = uniqueVertices.size(); i < n; ++i) + { + vertices[i * 3] = std::get<0>(uniqueVertices[i]); + vertices[i * 3 + 1] = std::get<1>(uniqueVertices[i]); + vertices[i * 3 + 2] = std::get<2>(uniqueVertices[i]); + } + } + } + RecastMeshBuilder::RecastMeshBuilder(const Settings& settings, const TileBounds& bounds) : mSettings(settings) , mBounds(bounds) @@ -112,8 +151,9 @@ namespace DetourNavigator mWater.push_back(RecastMesh::Water {cellSize, transform}); } - std::shared_ptr RecastMeshBuilder::create(std::size_t generation, std::size_t revision) const + std::shared_ptr RecastMeshBuilder::create(std::size_t generation, std::size_t revision) { + optimizeRecastMesh(mIndices, mVertices); return std::make_shared(generation, revision, mIndices, mVertices, mAreaTypes, mWater, mSettings.get().mTrianglesPerChunk); } diff --git a/components/detournavigator/recastmeshbuilder.hpp b/components/detournavigator/recastmeshbuilder.hpp index d28558d0f..fc2bbbc02 100644 --- a/components/detournavigator/recastmeshbuilder.hpp +++ b/components/detournavigator/recastmeshbuilder.hpp @@ -34,7 +34,7 @@ namespace DetourNavigator void addWater(const int mCellSize, const btTransform& transform); - std::shared_ptr create(std::size_t generation, std::size_t revision) const; + std::shared_ptr create(std::size_t generation, std::size_t revision); void reset(); From 5bc44cf2ee813a2fa8c11ec9f1a0b01082342d5d Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Tue, 16 Jun 2020 14:37:06 +0300 Subject: [PATCH 57/81] Use sequenced texture units with .dae/collada --- components/resource/scenemanager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index d06a07273..8264e3b1e 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -360,6 +360,7 @@ namespace Resource // Note, for some formats (.obj/.mtl) that reference other (non-image) files a findFileCallback would be necessary. // but findFileCallback does not support virtual files, so we can't implement it. options->setReadFileCallback(new ImageReadCallback(imageManager)); + if (ext == "dae") options->setOptionString("daeUseSequencedTextureUnits"); osgDB::ReaderWriter::ReadResult result = reader->readNode(*file, options); if (!result.success()) From 805d826d5b27132cd3128da322479832dab59593 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 16 Jun 2020 15:31:50 +0400 Subject: [PATCH 58/81] Fix Clang warnings about invalid overrides --- components/terrain/chunkmanager.hpp | 2 +- components/terrain/quadtreeworld.hpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 11e5769de..02a782c62 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -35,7 +35,7 @@ namespace Terrain public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) override; void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; } void setCompositeMapLevel(float level) { mCompositeMapLevel = level; } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 7b395bff6..47cf46138 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -26,22 +26,22 @@ namespace Terrain void accept(osg::NodeVisitor& nv); - virtual void enable(bool enabled); + void enable(bool enabled) override; - virtual void setViewDistance(float distance) { mViewDistance = distance; } + void setViewDistance(float distance) override { mViewDistance = distance; } - void cacheCell(View *view, int x, int y) {} + void cacheCell(View *view, int x, int y) override {} /// @note Not thread safe. - virtual void loadCell(int x, int y); + void loadCell(int x, int y) override; /// @note Not thread safe. - virtual void unloadCell(int x, int y); + void unloadCell(int x, int y) override; - View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); - bool storeView(const View* view, double referenceTime); + View* createView() override; + void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange) override; + bool storeView(const View* view, double referenceTime) override; void rebuildViews() override; - void reportStats(unsigned int frameNumber, osg::Stats* stats); + void reportStats(unsigned int frameNumber, osg::Stats* stats) override; class ChunkManager { From 7ef3a9d8ac57d02fcaaf991e9314222c3f6a1378 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 16 Jun 2020 13:51:25 +0000 Subject: [PATCH 59/81] Remove schoolboy error --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 701218dd2..26676efc8 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -232,7 +232,7 @@ run_cmd() { else RET=0 eval $CMD $@ || RET=$? - return RET + return $RET fi } From 6f8eefcb1635b5ad1b95351e3bc4724ec657e2b9 Mon Sep 17 00:00:00 2001 From: psi29a Date: Tue, 16 Jun 2020 17:53:09 +0000 Subject: [PATCH 60/81] Switching object paging active grid default option to false for now until the light limit problem is resolved. --- files/settings-default.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 686d65adc..c8a147b9e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -110,7 +110,7 @@ max composite geometry size = 4.0 object paging = true # Use object paging for active cells grid -object paging active grid = true +object paging active grid = false # Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory. object paging merge factor = 250 From 9bc0e9b4b7b13a47dce3113087629a4cd8417e64 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 17 Jun 2020 10:13:52 +0400 Subject: [PATCH 61/81] Fix typos in shaders --- files/shaders/objects_fragment.glsl | 2 +- files/shaders/terrain_fragment.glsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index ece53f047..d5716c378 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -187,7 +187,7 @@ void main() if (matSpec != vec3(0.0)) { -#if (!normalMap && !@parallax && !forcePPL) +#if (!@normalMap && !@parallax && !@forcePPL) vec3 viewNormal = gl_NormalMatrix * normalize(passNormal); #endif gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index 21e50b4e9..7409ce045 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -96,7 +96,7 @@ void main() if (matSpec != vec3(0.0)) { -#if (!normalMap && !@parallax && !forcePPL) +#if (!@normalMap && !@parallax && !@forcePPL) vec3 viewNormal = gl_NormalMatrix * normalize(passNormal); #endif gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing; From 3e396a904eb6a40d887fc75406af330d2e31755c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 17 Jun 2020 11:09:17 +0400 Subject: [PATCH 62/81] Reset selected slot when close savegame dialogue to avoid accidental saving/loading --- apps/openmw/mwgui/savegamedialog.cpp | 7 +++++++ apps/openmw/mwgui/savegamedialog.hpp | 1 + 2 files changed, 8 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index f85bfc8d3..c4d608443 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -132,6 +132,13 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } + void SaveGameDialog::onClose() + { + mSaveList->setIndexSelected(MyGUI::ITEM_NONE); + + WindowModal::onClose(); + } + void SaveGameDialog::onOpen() { WindowModal::onOpen(); diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index a9915ee9d..e2c41af70 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -20,6 +20,7 @@ namespace MWGui SaveGameDialog(); virtual void onOpen(); + virtual void onClose(); void setLoadOrSave(bool load); From 1db58575c8426382c41929706ff743d05afc39b0 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 17 Jun 2020 09:58:18 +0200 Subject: [PATCH 63/81] Fixed missed AiPackageTypeId::Wander 'fix' --- apps/openmw/mwmechanics/aipackage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 375589528..952a1b48e 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -415,7 +415,7 @@ DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::P if (flags & DetourNavigator::Flag_walk) { float walkCost; - if (getTypeId() == TypeIdWander) + if (getTypeId() == AiPackageTypeId::Wander) walkCost = 1.0 / actorClass.getWalkSpeed(actor); else walkCost = 1.0 / actorClass.getRunSpeed(actor); From d1a3cc98ffdcb5e40b5cc56a1d5db37ffac2a602 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 16 Jun 2020 18:43:01 +0400 Subject: [PATCH 64/81] Get rid of ECLD and dependencies --- apps/openmw/mwgui/hud.cpp | 4 +-- apps/openmw/mwgui/mapwindow.cpp | 12 ++++----- apps/openmw/mwgui/mapwindow.hpp | 2 +- apps/openmw/mwmechanics/aipackage.cpp | 1 - apps/openmw/mwrender/localmap.cpp | 2 +- apps/openmw/mwworld/scene.cpp | 1 - apps/openmw/mwworld/scene.hpp | 5 +++- components/misc/constants.hpp | 3 +++ .../reference/modding/settings/camera.rst | 17 +++++------- .../reference/modding/settings/cells.rst | 26 +------------------ .../source/reference/modding/settings/map.rst | 11 -------- files/settings-default.cfg | 9 ------- 12 files changed, 22 insertions(+), 71 deletions(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 4f51dac04..fd0fcb798 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -156,9 +156,7 @@ namespace MWGui getWidget(mCrosshair, "Crosshair"); - int mapSize = std::max(1, Settings::Manager::getInt("local map hud widget size", "Map")); - int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map")); - LocalMapBase::init(mMinimap, mCompass, mapSize, cellDistance); + LocalMapBase::init(mMinimap, mCompass); mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 372e0f64d..f0ece76d5 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -183,13 +183,13 @@ namespace MWGui mCustomMarkers.eventMarkersChanged -= MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers); } - void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize, int cellDistance) + void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass) { mLocalMap = widget; mCompass = compass; - mMapWidgetSize = mapWidgetSize; - mCellDistance = cellDistance; - mNumCells = cellDistance * 2 + 1; + mMapWidgetSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map")); + mCellDistance = Constants::CellGridRadius; + mNumCells = mCellDistance * 2 + 1; mLocalMap->setCanvasSize(mMapWidgetSize*mNumCells, mMapWidgetSize*mNumCells); @@ -697,9 +697,7 @@ namespace MWGui mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked); - int mapSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map")); - int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map")); - LocalMapBase::init(mLocalMap, mPlayerArrowLocal, mapSize, cellDistance); + LocalMapBase::init(mLocalMap, mPlayerArrowLocal); mGlobalMap->setVisible(mGlobal); mLocalMap->setVisible(!mGlobal); diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 206998770..7b86f7617 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -72,7 +72,7 @@ namespace MWGui public: LocalMapBase(CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled = true); virtual ~LocalMapBase(); - void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize, int cellDistance); + void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass); void setCellPrefix(const std::string& prefix); void setActiveCell(const int x, const int y, bool interior=false); diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index e322f57ce..64e6c2e7c 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -344,7 +344,6 @@ bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position) Misc::CoordinateConverter(playerCell).toLocal(position); // currently assumes 3 x 3 grid for exterior cells, with player at center cell. - // ToDo: (Maybe) use "exterior cell load distance" setting to get count of actual active cells // AI shuts down actors before they reach edges of 3 x 3 grid. const float distanceFromEdge = 200.0; float minThreshold = (-1.0f * ESM::Land::REAL_SIZE) + distanceFromEdge; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 12401f45e..aaa797ef1 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -74,7 +74,7 @@ LocalMap::LocalMap(osg::Group* root) : mRoot(root) , mMapResolution(Settings::Manager::getInt("local map resolution", "Map")) , mMapWorldSize(Constants::CellSizeInUnits) - , mCellDistance(Settings::Manager::getInt("local map cell distance", "Map")) + , mCellDistance(Constants::CellGridRadius) , mAngle(0.f) , mInterior(false) { diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b311107f4..42fe1a960 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -757,7 +757,6 @@ namespace MWWorld Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics, DetourNavigator::Navigator& navigator) : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNavigator(navigator) - , mHalfGridSize(Settings::Manager::getInt("exterior cell load distance", "Cells")) , mCellLoadingThreshold(1024.f) , mPreloadDistance(Settings::Manager::getInt("preload distance", "Cells")) , mPreloadEnabled(Settings::Manager::getBool("preload enabled", "Cells")) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index b9a39779d..a70d3ccdd 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -11,6 +11,8 @@ #include #include +#include + namespace osg { class Vec3f; @@ -75,7 +77,6 @@ namespace MWWorld MWRender::RenderingManager& mRendering; DetourNavigator::Navigator& mNavigator; std::unique_ptr mPreloader; - int mHalfGridSize; float mCellLoadingThreshold; float mPreloadDistance; bool mPreloadEnabled; @@ -85,6 +86,8 @@ namespace MWWorld bool mPreloadFastTravel; float mPredictionTime; + static const int mHalfGridSize = Constants::CellGridRadius; + osg::Vec3f mLastPlayerPos; std::set mPagedRefs; diff --git a/components/misc/constants.hpp b/components/misc/constants.hpp index af43eb414..1053b1c56 100644 --- a/components/misc/constants.hpp +++ b/components/misc/constants.hpp @@ -24,6 +24,9 @@ const float GravityConst = 8.96f; // Size of one exterior cell in game units const int CellSizeInUnits = 8192; +// Size of active cell grid in cells (it is a square with the (2 * CellGridRadius + 1) cells side) +const int CellGridRadius = 1; + // A label to mark night/day visual switches const std::string NightDayLabel = "NightDaySwitch"; diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index 92267a4a0..8bc36d8fb 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -51,21 +51,20 @@ viewing distance This value controls the maximum visible distance (also called the far clipping plane). Larger values significantly improve rendering in exterior spaces, but also increase the amount of rendered geometry and significantly reduce the frame rate. -This value interacts with the exterior cell load distance setting -in that it's probably undesired for this value to provide visibility into cells that have not yet been loaded. -When cells are visible before loading, the geometry will "pop-in" suddenly, creating a jarring visual effect. -To prevent this effect, this value must be less than:: +Note that when cells are visible before loading (when not using a Distant Land), the geometry will "pop-in" suddenly, +creating a jarring visual effect. To prevent this effect, this value must be less than:: - (8192 * exterior cell load distance - 1024) * 0.93 + (CellSizeInUnits * CellGridRadius - 1024) * 0.93 -The constant 8192 is the size of a cell, and 1024 is the threshold distance for loading a new cell. +The CellSizeInUnits is the size of a game cell in units (8192 by default), CellGridRadius determines how many +neighboring cells to current one to load (1 by default - 3x3 grid), and 1024 is the threshold distance for loading a new cell. Additionally, the field of view setting also interacts with this setting because the view frustum end is a plane, so you can see further at the edges of the screen than you should be able to. This can be observed in game by looking at distant objects and rotating the camera so the objects are near the edge of the screen. As a result, this setting should further be reduced by a factor that depends on the field of view setting. In the default configuration this reduction is 7%, hence the factor of 0.93 above. -Using this factor, approximate values recommended for other exterior cell load distance settings are: +Using this factor, approximate values recommended for other CellGridRadius values are: ======= ======== Cells Viewing @@ -83,10 +82,6 @@ Such situations are unusual and probably not worth the performance penalty intro by loading geometry obscured by fog in the center of the screen. See RenderingManager::configureFog for the relevant source code. -Enabling the distant terrain setting is an alternative to increasing exterior cell load distance. -Note that the distant land setting does not include rendering of distant static objects, -so the resulting visual effect is not the same. - This setting can be adjusted in game from the ridiculously low value of 2048.0 to a maximum of 81920.0 using the View Distance slider in the Detail tab of the Video panel of the Options menu. diff --git a/docs/source/reference/modding/settings/cells.rst b/docs/source/reference/modding/settings/cells.rst index 43e51eb7a..620b545ac 100644 --- a/docs/source/reference/modding/settings/cells.rst +++ b/docs/source/reference/modding/settings/cells.rst @@ -1,30 +1,6 @@ Cells Settings ############## -exterior cell load distance ---------------------------- - -:Type: integer -:Range: >= 1 -:Default: 1 - -This setting determines the number of exterior cells adjacent to the character that will be loaded for rendering. - -.. Warning:: - Values greater than 1 will significantly affect the frame rate and loading times. - This setting is mainly intended for making screenshots of scenic vistas and not for real-time gameplay. - Loading more cells can break certain scripts or quests in the game that expect cells to not be loaded until the player is there. - These limitations will be addressed in a future version with a separate technique for rendering distant cells. - -This setting interacts with viewing distance and field of view settings. - -It is generally very wasteful for this value to load geometry than will almost never be visible -due to viewing distance and fog. For low frame rate screen shots of scenic vistas, -this setting should be set high, and viewing distances adjusted accordingly. - -This setting can only be configured by editing the settings configuration file. - - preload enabled --------------- @@ -60,7 +36,7 @@ consider increasing the number of preloading threads to 2 or 3 for a boost in pr Faster preloading will reduce the chance that a cell could not be completely loaded before the player moves into it, and hence reduce the chance of seeing loading screens or frame drops. This may be especially relevant when the player moves at high speed -and/or a large number of cells are loaded in via 'exterior cell load distance'. +and/or a large number of objects are preloaded due to large viewing distance. A value of 4 or higher is not recommended. With 4 or more threads, improvements will start to diminish due to file reading and synchronization bottlenecks. diff --git a/docs/source/reference/modding/settings/map.rst b/docs/source/reference/modding/settings/map.rst index 3ef376bb9..ac989f3de 100644 --- a/docs/source/reference/modding/settings/map.rst +++ b/docs/source/reference/modding/settings/map.rst @@ -103,14 +103,3 @@ and typically require more panning to see all available portions of the map. This larger size also enables an overall greater level of detail if the local map resolution setting is also increased. This setting can not be configured except by editing the settings configuration file. - -local map cell distance ------------------------ - -:Type: integer -:Range: >= 1 -:Default: 1 - -Similar to "exterior cell load distance" in the Cells section, controls how many cells are rendered on the local map. -Please note that only loaded cells can be rendered, -so this setting must be lower or equal to "exterior cell load distance" to work properly. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 686d65adc..0fb9be787 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -35,10 +35,6 @@ first person field of view = 60.0 [Cells] -# Adjacent exterior cells loaded (>0). Caution: this setting can -# dramatically affect performance, see documentation for details. -exterior cell load distance = 1 - # Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled. preload enabled = true @@ -166,11 +162,6 @@ local map resolution = 256 # Size of local map in GUI window in pixels. (e.g. 256 to 1024). local map widget size = 512 -# Similar to "[Cells] exterior cell load distance", controls -# how many cells are rendered on the local map. Values higher than the default -# may result in longer loading times. -local map cell distance = 1 - # If true, map in world mode, otherwise in local mode global = false From 0e810c8d3224d538b1a6d8cb81aac122e32d8eb7 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 18 Jun 2020 09:12:56 +0400 Subject: [PATCH 65/81] Fix cell borders color --- components/terrain/cellborder.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/terrain/cellborder.cpp b/components/terrain/cellborder.cpp index 6eabadf92..47c567f54 100644 --- a/components/terrain/cellborder.cpp +++ b/components/terrain/cellborder.cpp @@ -1,5 +1,6 @@ #include "cellborder.hpp" +#include #include #include #include @@ -64,6 +65,9 @@ void CellBorder::createCellBorderGeometry(int x, int y) borderGeode->addDrawable(border.get()); osg::StateSet *stateSet = borderGeode->getOrCreateStateSet(); + osg::ref_ptr material (new osg::Material); + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + stateSet->setAttribute(material); osg::PolygonMode* polygonmode = new osg::PolygonMode; polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); From 808c905e1fcab71c558c779a23ab7fc6876fb089 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 18 Jun 2020 14:50:06 +0400 Subject: [PATCH 66/81] Initialize fields to avoid undefined behaviour --- apps/openmw/mwinput/controllermanager.cpp | 1 + apps/openmw/mwrender/objectpaging.hpp | 2 +- apps/openmw/mwscript/globalscripts.cpp | 2 ++ components/terrain/chunkmanager.cpp | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 50a169c5c..3b1f587ce 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -36,6 +36,7 @@ namespace MWInput , mSneakToggleShortcutTimer(0.f) , mGamepadZoom(0) , mGamepadGuiCursorEnabled(true) + , mGuiCursorEnabled(true) , mJoystickLastUsed(false) , mSneakGamepadShortcut(false) , mGamepadPreviewMode(false) diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index c79dd6e06..9c2c54f65 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -80,7 +80,7 @@ namespace MWRender class RefnumMarker : public osg::Object { public: - RefnumMarker() : mNumVertices(0) {} + RefnumMarker() : mNumVertices(0) { mRefnum.unset(); } RefnumMarker(const RefnumMarker ©, osg::CopyOp co) : mRefnum(copy.mRefnum), mNumVertices(copy.mNumVertices) {} META_Object(MWRender, RefnumMarker) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 5dad9d6ec..a7865f0ae 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -24,6 +24,7 @@ namespace { ESM::GlobalScript script; script.mTargetRef.unset(); + script.mRunning = false; if (!ptr.isEmpty()) { if (ptr.getCellRef().hasContentFile()) @@ -42,6 +43,7 @@ namespace ESM::GlobalScript script; script.mTargetId = pair.second; script.mTargetRef = pair.first; + script.mRunning = false; return script; } }; diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 3c3bd0f9d..87966b47c 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -28,6 +28,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T , mSceneManager(sceneMgr) , mTextureManager(textureManager) , mCompositeMapRenderer(renderer) + , mNodeMask(0) , mCompositeMapSize(512) , mCompositeMapLevel(1.f) , mMaxCompGeometrySize(1.f) From 6357bc3dad4e987afc4fcd9cf4aa2ac6009c6a49 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 18 Jun 2020 15:13:02 +0400 Subject: [PATCH 67/81] Catch MyGUI exceptions in the FontLoader destructor --- components/fontloader/fontloader.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 6dcebe0bd..605d55243 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -165,7 +165,14 @@ namespace Gui FontLoader::~FontLoader() { - MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); + try + { + MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the FontLoader destructor: " << e.what(); + } for (std::vector::iterator it = mTextures.begin(); it != mTextures.end(); ++it) delete *it; From 9446cece62d0215bcdbb387605a5f4a6ee3859f7 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Thu, 18 Jun 2020 13:23:39 +0200 Subject: [PATCH 68/81] Issue #5468: Apparently this was always a problem but OP made "nested loading" more visible. This should resolve nested loading correctly. --- apps/openmw/mwgui/loadingscreen.cpp | 8 ++++++-- apps/openmw/mwgui/loadingscreen.hpp | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 436e9c2bc..521b88f29 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -40,6 +40,7 @@ namespace MWGui , mLoadingOnTime(0.0) , mImportantLabel(false) , mVisible(false) + , mNestedLoadingCount(0) , mProgress(0) , mShowWallpaper(true) { @@ -163,11 +164,12 @@ namespace MWGui void LoadingScreen::loadingOn(bool visible) { - mLoadingOnTime = mTimer.time_m(); // Early-out if already on - if (mMainWidget->getVisible()) + if (mNestedLoadingCount++ > 0 && mMainWidget->getVisible()) return; + mLoadingOnTime = mTimer.time_m(); + // Assign dummy bounding sphere callback to avoid the bounding sphere of the entire scene being recomputed after each frame of loading // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); @@ -200,6 +202,8 @@ namespace MWGui void LoadingScreen::loadingOff() { + if (--mNestedLoadingCount > 0) + return; mLoadingBox->setVisible(true); // restore if (mLastRenderTime < mLoadingOnTime) diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 1be1b3a0c..b3e2534ce 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -66,6 +66,7 @@ namespace MWGui bool mImportantLabel; bool mVisible; + int mNestedLoadingCount; size_t mProgress; From a93ea93d9deb86d1496b4a23921aa3c15b1c477a Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 18 Jun 2020 14:46:08 +0100 Subject: [PATCH 69/81] Remove MSVC 2015 specific parts of CI script. Also add error when MSVC 2015 is requested. --- CI/before_script.msvc.sh | 48 +++++----------------------------------- 1 file changed, 6 insertions(+), 42 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 26676efc8..b8570545b 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -161,7 +161,7 @@ Options: Build unit tests / Google test -u Configure for unity builds. - -v <2013/2015/2017/2019> + -v <2017/2019> Choose the Visual Studio version to use. -n Produce NMake makefiles instead of a Visual Studio solution. Cannout be used with -N. @@ -348,21 +348,13 @@ case $VS_VERSION in ;; 14|14.0|2015 ) - GENERATOR="Visual Studio 14 2015" - TOOLSET="vc140" - MSVC_REAL_VER="14" - MSVC_VER="14.0" - MSVC_YEAR="2015" - MSVC_REAL_YEAR="2015" - MSVC_DISPLAY_YEAR="2015" - BOOST_VER="1.67.0" - BOOST_VER_URL="1_67_0" - BOOST_VER_SDK="106700" + echo "Visual Studio 2015 is no longer supported" + wrappedExit 1 ;; 12|12.0|2013 ) echo "Visual Studio 2013 is no longer supported" - exit 1 + wrappedExit 1 ;; esac @@ -507,11 +499,6 @@ if [ -z $SKIP_DOWNLOAD ]; then # Qt if [ -z $APPVEYOR ]; then - if [ "${MSVC_REAL_YEAR}" = "2015" ] && [ "${BITS}" = "32" ]; then - echo "Qt no longer provides MSVC2015 Win32 packages, switch to 64-bit or a newer Visual Studio. Sorry." - exit 1 - fi - download "AQt installer" \ "https://files.pythonhosted.org/packages/f3/bb/aee972f08deecca31bfc46b5aedfad1ce6c7f3aaf1288d685e4a914b53ac/aqtinstall-0.8-py2.py3-none-any.whl" \ "aqtinstall-0.8-py2.py3-none-any.whl" @@ -606,14 +593,8 @@ fi # Appveyor has all the boost we need already BOOST_SDK="c:/Libraries/boost_${BOOST_VER_URL}" - if [ $MSVC_REAL_VER -ge 15 ]; then - LIB_SUFFIX="1" - else - LIB_SUFFIX="0" - fi - add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \ - -DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}" + -DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.1" add_cmake_opts -DBoost_COMPILER="-${TOOLSET}" echo Done. @@ -964,24 +945,7 @@ if [ -n "$ACTIVATE_MSVC" ]; then command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; } MSVC_INSTALLATION_PATH=$(vswhere -legacy -products '*' -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) - if [ $MSVC_REAL_VER -ge 15 ]; then - echo "@\"${MSVC_INSTALLATION_PATH}\Common7\Tools\VsDevCmd.bat\" -no_logo -arch=$([ $BITS -eq 64 ] && echo "amd64" || echo "x86") -host_arch=$([ $(uname -m) == 'x86_64' ] && echo "amd64" || echo "x86")" > ActivateMSVC.bat - else - if [ $(uname -m) == 'x86_64' ]; then - if [ $BITS -eq 64 ]; then - compiler=amd64 - else - compiler=amd64_x86 - fi - else - if [ $BITS -eq 64 ]; then - compiler=x86_amd64 - else - compiler=x86 - fi - fi - echo "@\"${MSVC_INSTALLATION_PATH}\VC\vcvarsall.bat\" $compiler" > ActivateMSVC.bat - fi + echo "@\"${MSVC_INSTALLATION_PATH}\Common7\Tools\VsDevCmd.bat\" -no_logo -arch=$([ $BITS -eq 64 ] && echo "amd64" || echo "x86") -host_arch=$([ $(uname -m) == 'x86_64' ] && echo "amd64" || echo "x86")" > ActivateMSVC.bat cp "../CI/activate_msvc.sh" . sed -i "s/\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g" activate_msvc.sh From 36d0a55600d687bb327966207e82f5c83cc6bc47 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 18 Jun 2020 14:50:07 +0100 Subject: [PATCH 70/81] Add error message when vswhere doesn't find MSVC --- CI/before_script.msvc.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index b8570545b..79885255f 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -945,6 +945,11 @@ if [ -n "$ACTIVATE_MSVC" ]; then command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; } MSVC_INSTALLATION_PATH=$(vswhere -legacy -products '*' -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) + if [ -z "$MSVC_INSTALLATION_PATH" ]; then + echo "vswhere was unable to find MSVC $MSVC_DISPLAY_YEAR" + wrappedExit 1 + fi + echo "@\"${MSVC_INSTALLATION_PATH}\Common7\Tools\VsDevCmd.bat\" -no_logo -arch=$([ $BITS -eq 64 ] && echo "amd64" || echo "x86") -host_arch=$([ $(uname -m) == 'x86_64' ] && echo "amd64" || echo "x86")" > ActivateMSVC.bat cp "../CI/activate_msvc.sh" . From c3dc0e62e86005ea3758a69dbee7de7fb1390487 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Thu, 18 Jun 2020 16:16:16 +0200 Subject: [PATCH 71/81] OP profiling regression fix; Billboards such as those from Westly's tree mod should work again. --- apps/openmw/mwrender/objectpaging.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 71268a52c..202541ea5 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -571,6 +571,7 @@ namespace MWRender copyop.mSqrDistance = (viewPoint - pos).length2(); copyop.mViewVector = (viewPoint - worldCenter); copyop.copy(cnode, trans); + copyop.mNodePath.pop_back(); if (activeGrid) { From 1541c7e5eb05121b45fa0e4d6d53d291636ce64d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 18 Jun 2020 19:19:57 +0400 Subject: [PATCH 72/81] Fix ordering warning --- apps/openmw/mwinput/controllermanager.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/controllermanager.hpp b/apps/openmw/mwinput/controllermanager.hpp index 94faff088..3c1a2ee6e 100644 --- a/apps/openmw/mwinput/controllermanager.hpp +++ b/apps/openmw/mwinput/controllermanager.hpp @@ -56,8 +56,8 @@ namespace MWInput float mSneakToggleShortcutTimer; float mGamepadZoom; bool mGamepadGuiCursorEnabled; - bool mJoystickLastUsed; bool mGuiCursorEnabled; + bool mJoystickLastUsed; bool mSneakGamepadShortcut; bool mGamepadPreviewMode; }; From f94ca28dbe9e1727776a7ab259e5bac30d656120 Mon Sep 17 00:00:00 2001 From: psi29a Date: Thu, 18 Jun 2020 17:23:16 +0000 Subject: [PATCH 73/81] #5463: Optimizer fix, problem was indeed related to tangents not being transformd properly. --- components/sceneutil/optimizer.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index e8a945114..b9da2d1c8 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -358,6 +358,21 @@ void CollectLowestTransformsVisitor::doTransform(osg::Object* obj,osg::Matrix& m { osgUtil::TransformAttributeFunctor tf(matrix); drawable->accept(tf); + + osg::Geometry *geom = drawable->asGeometry(); + osg::Vec4Array* tangents = geom ? dynamic_cast(geom->getTexCoordArray(7)) : nullptr; + if (tangents) + { + for (unsigned int i=0; isize(); ++i) + { + osg::Vec4f& itr = (*tangents)[i]; + osg::Vec3f vec3 (itr.x(), itr.y(), itr.z()); + vec3 = osg::Matrix::transform3x3(tf._im, vec3); + vec3.normalize(); + itr = osg::Vec4f(vec3.x(), vec3.y(), vec3.z(), itr.w()); + } + } + drawable->dirtyBound(); drawable->dirtyDisplayList(); @@ -614,12 +629,12 @@ void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Drawable& drawable) if((geometry) && (isOperationPermissibleForObject(&drawable))) { osg::VertexBufferObject* vbo = nullptr; - if(geometry->getVertexArray() && geometry->getVertexArray()->referenceCount() > 1) { + if(geometry->getVertexArray() && geometry->getVertexArray()->referenceCount() > 1) geometry->setVertexArray(cloneArray(geometry->getVertexArray(), vbo, geometry)); - } - if(geometry->getNormalArray() && geometry->getNormalArray()->referenceCount() > 1) { + if(geometry->getNormalArray() && geometry->getNormalArray()->referenceCount() > 1) geometry->setNormalArray(cloneArray(geometry->getNormalArray(), vbo, geometry)); - } + if(geometry->getTexCoordArray(7) && geometry->getTexCoordArray(7)->referenceCount() > 1) // tangents + geometry->setTexCoordArray(7, cloneArray(geometry->getTexCoordArray(7), vbo, geometry)); } _drawableSet.insert(&drawable); } From 6e397e40085bbaa981f2f7b8f88bdcf43bff2e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Mocquillon?= Date: Sun, 21 Jun 2020 14:45:30 +0000 Subject: [PATCH 74/81] Add a search function to the "Datafiles" tab of the OpenMW launcher --- CHANGELOG.md | 1 + .../contentselector/view/contentselector.cpp | 32 +++++++++++++++++-- .../contentselector/view/contentselector.hpp | 4 +++ files/ui/contentselector.ui | 3 ++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 722f546c8..b3d4c4515 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Bug #5441: Enemies can't push a player character when in critical strike stance Bug #5451: Magic projectiles don't disappear with the caster Bug #5452: Autowalk is being included in savegames + Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5445: Handle NiLines diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 57f1fdcf3..6bb8e6e2c 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -40,16 +40,37 @@ void ContentSelectorView::ContentSelector::buildGameFileView() ui.gameFileView->setCurrentIndex(0); } +class AddOnProxyModel : public QSortFilterProxyModel +{ +public: + explicit AddOnProxyModel(QObject* parent = nullptr) : + QSortFilterProxyModel(parent) + {} + + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override + { + static const QString ContentTypeAddon = QString::number((int)ContentSelectorModel::ContentType_Addon); + + QModelIndex nameIndex = sourceModel()->index(sourceRow, 0, sourceParent); + const QString userRole = sourceModel()->data(nameIndex, Qt::UserRole).toString(); + + return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent) && userRole == ContentTypeAddon; + } +}; + void ContentSelectorView::ContentSelector::buildAddonView() { ui.addonView->setVisible (true); - mAddonProxyModel = new QSortFilterProxyModel(this); - mAddonProxyModel->setFilterRegExp (QString::number((int)ContentSelectorModel::ContentType_Addon)); - mAddonProxyModel->setFilterRole (Qt::UserRole); + mAddonProxyModel = new AddOnProxyModel(this); + mAddonProxyModel->setFilterRegExp(searchFilter()->text()); + mAddonProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); mAddonProxyModel->setDynamicSortFilter (true); mAddonProxyModel->setSourceModel (mContentModel); + connect(ui.searchFilter, SIGNAL(textEdited(QString)), mAddonProxyModel, SLOT(setFilterWildcard(QString))); + connect(ui.searchFilter, SIGNAL(textEdited(QString)), this, SLOT(slotSearchFilterTextChanged(QString))); + ui.addonView->setModel(mAddonProxyModel); connect(ui.addonView, SIGNAL(activated(const QModelIndex&)), this, SLOT(slotAddonTableItemActivated(const QModelIndex&))); @@ -261,3 +282,8 @@ void ContentSelectorView::ContentSelector::slotCopySelectedItemsPaths() clipboard->setText(filepaths); } } + +void ContentSelectorView::ContentSelector::slotSearchFilterTextChanged(const QString& newText) +{ + ui.addonView->setDragEnabled(newText.isEmpty()); +} diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 9c34e24fd..f1058d510 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -48,6 +48,9 @@ namespace ContentSelectorView QToolButton *refreshButton() const { return ui.refreshButton; } + QLineEdit *searchFilter() const + { return ui.searchFilter; } + private: @@ -74,6 +77,7 @@ namespace ContentSelectorView void slotCheckMultiSelectedItems(); void slotUncheckMultiSelectedItems(); void slotCopySelectedItemsPaths(); + void slotSearchFilterTextChanged(const QString& newText); }; } diff --git a/files/ui/contentselector.ui b/files/ui/contentselector.ui index d13cb314e..c099649b3 100644 --- a/files/ui/contentselector.ui +++ b/files/ui/contentselector.ui @@ -66,6 +66,9 @@ + + + From 61a5c6125deb1fceb126add5ca5fa6c345613297 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 22 Jun 2020 12:17:06 +0200 Subject: [PATCH 75/81] #5480: Drop Qt4 support and require Qt 5.12 or later. --- .gitlab-ci.yml | 2 +- CHANGELOG.md | 1 + CI/before_script.linux.sh | 1 - CI/before_script.msvc.sh | 6 +-- CI/before_script.osx.sh | 1 - CMakeLists.txt | 37 +++---------------- apps/launcher/CMakeLists.txt | 22 ++--------- apps/launcher/graphicspage.cpp | 19 ---------- apps/launcher/main.cpp | 12 ------ apps/opencs/CMakeLists.txt | 27 ++------------ apps/opencs/model/prefs/shortcutmanager.cpp | 4 -- apps/opencs/view/doc/newgame.cpp | 7 ---- apps/opencs/view/doc/startup.cpp | 8 ---- apps/opencs/view/doc/view.cpp | 7 ---- apps/opencs/view/prefs/dialogue.cpp | 12 ------ apps/opencs/view/render/scenewidget.cpp | 11 ++---- apps/opencs/view/render/worldspacewidget.cpp | 5 --- apps/opencs/view/tools/reporttable.cpp | 4 -- apps/opencs/view/widget/coloreditor.cpp | 7 ---- apps/opencs/view/widget/scenetoolrun.cpp | 5 --- .../view/widget/scenetoolshapebrush.cpp | 5 --- .../view/widget/scenetooltexturebrush.cpp | 5 --- apps/opencs/view/world/enumdelegate.cpp | 13 +------ apps/opencs/view/world/nestedtable.cpp | 4 -- apps/opencs/view/world/scriptedit.cpp | 10 ----- apps/opencs/view/world/scripterrortable.cpp | 5 --- apps/opencs/view/world/table.cpp | 4 -- apps/wizard/CMakeLists.txt | 26 ++----------- components/CMakeLists.txt | 18 ++------- extern/osgQt/CMakeLists.txt | 7 +--- extern/osgQt/GraphicsWindowQt.cpp | 20 ++-------- 31 files changed, 32 insertions(+), 283 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 514bac1ad..3d798845e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ Debian: before_script: - export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR - apt-get update -yq - - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev + - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev stage: build script: - cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 09dabf311..56069bdc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5445: Handle NiLines + Task #5480: Drop Qt4 support 0.46.0 ------ diff --git a/CI/before_script.linux.sh b/CI/before_script.linux.sh index a1c04cf66..5ab748306 100755 --- a/CI/before_script.linux.sh +++ b/CI/before_script.linux.sh @@ -27,7 +27,6 @@ ${ANALYZE} cmake \ -DBUILD_NIFTEST=${BUILD_OPENMW_CS} \ -DBUILD_UNITTESTS=1 \ -DUSE_SYSTEM_TINYXML=1 \ - -DDESIRED_QT_VERSION=5 \ -DCMAKE_INSTALL_PREFIX=/usr \ -DBINDIR=/usr/games \ -DCMAKE_BUILD_TYPE="None" \ diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 79885255f..78acaead6 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -776,8 +776,7 @@ fi fi cd $QT_SDK - add_cmake_opts -DDESIRED_QT_VERSION=5 \ - -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ + add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ -DCMAKE_PREFIX_PATH="$QT_SDK" if [ $CONFIGURATION == "Debug" ]; then SUFFIX="d" @@ -789,8 +788,7 @@ fi echo Done. else QT_SDK="C:/Qt/5.13/msvc2017${SUFFIX}" - add_cmake_opts -DDESIRED_QT_VERSION=5 \ - -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ + add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ -DCMAKE_PREFIX_PATH="$QT_SDK" if [ $CONFIGURATION == "Debug" ]; then SUFFIX="d" diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index 9fae97af6..01d9d2b80 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -17,7 +17,6 @@ cmake \ -D CMAKE_OSX_SYSROOT="macosx10.14" \ -D CMAKE_BUILD_TYPE=Release \ -D OPENMW_OSX_DEPLOYMENT=TRUE \ --D DESIRED_QT_VERSION=5 \ -D BUILD_ESMTOOL=FALSE \ -G"Unix Makefiles" \ .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 5db93ddd8..edd07bf95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,11 +19,6 @@ else() set(USE_QT TRUE) endif() -if (USE_QT) - set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)") - set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5) -endif() - # set the minimum required version across the board cmake_minimum_required(VERSION 3.1.0) @@ -153,18 +148,12 @@ endif() find_package(OpenGL REQUIRED) if (USE_QT) - message(STATUS "Using Qt${DESIRED_QT_VERSION}") - - if (DESIRED_QT_VERSION MATCHES 4) - find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork QtOpenGL) - else() - find_package(Qt5Widgets REQUIRED) - find_package(Qt5Core REQUIRED) - find_package(Qt5Network REQUIRED) - find_package(Qt5OpenGL REQUIRED) + find_package(Qt5Core 5.12 REQUIRED) + find_package(Qt5Widgets REQUIRED) + find_package(Qt5Network REQUIRED) + find_package(Qt5OpenGL REQUIRED) # Instruct CMake to run moc automatically when needed. #set(CMAKE_AUTOMOC ON) - endif() endif() # Sound setup @@ -291,17 +280,6 @@ if(OSG_STATIC) list(APPEND OPENSCENEGRAPH_LIBRARIES ${OSGPlugins_LIBRARIES}) endif() -if(QT_STATIC) - if(WIN32) - if(DESIRED_QT_VERSION MATCHES 4) - # QtCore needs WSAAsyncSelect from Ws2_32.lib - set(QT_QTCORE_LIBRARY ${QT_QTCORE_LIBRARY} Ws2_32.lib) - message("QT_QTCORE_LIBRARY: ${QT_QTCORE_LIBRARY}") - endif() - endif() -endif() - - set(BOOST_COMPONENTS system filesystem program_options iostreams) if(WIN32) set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) @@ -517,11 +495,6 @@ if(WIN32) INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug) INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) - IF(DESIRED_QT_VERSION MATCHES 5) - INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug) - INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) - ENDIF() - INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/resources" DESTINATION "." CONFIGURATIONS Debug) INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/resources" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) @@ -785,7 +758,7 @@ if (WIN32) endif() # Apple bundling -if (OPENMW_OSX_DEPLOYMENT AND APPLE AND DESIRED_QT_VERSION MATCHES 5) +if (OPENMW_OSX_DEPLOYMENT AND APPLE) 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 () diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index bfc08a7d6..329d06a57 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -70,16 +70,9 @@ if(WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) -if (DESIRED_QT_VERSION MATCHES 4) - include(${QT_USE_FILE}) - QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc) - QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) - QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI}) -else() - QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc) - QT5_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) - QT5_WRAP_UI(UI_HDRS ${LAUNCHER_UI}) -endif() +QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc) +QT5_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) +QT5_WRAP_UI(UI_HDRS ${LAUNCHER_UI}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) if(NOT WIN32) @@ -105,14 +98,7 @@ target_link_libraries(openmw-launcher components ) -if (DESIRED_QT_VERSION MATCHES 4) - target_link_libraries(openmw-launcher ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY}) - if(WIN32) - target_link_libraries(openmw-launcher ${QT_QTMAIN_LIBRARY}) - endif(WIN32) -else() - target_link_libraries(openmw-launcher Qt5::Widgets Qt5::Core) -endif() +target_link_libraries(openmw-launcher Qt5::Widgets Qt5::Core) if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index d48e627e1..45c093bee 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -3,10 +3,7 @@ #include #include #include - -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include -#endif #ifdef MAC_OS_X_VERSION_MIN_REQUIRED #undef MAC_OS_X_VERSION_MIN_REQUIRED @@ -54,13 +51,11 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings: bool Launcher::GraphicsPage::setupSDL() { -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) bool sdlConnectSuccessful = initSDL(); if (!sdlConnectSuccessful) { return false; } -#endif int displays = SDL_GetNumVideoDisplays(); @@ -81,10 +76,8 @@ bool Launcher::GraphicsPage::setupSDL() screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1)); } -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) // Disconnect from SDL processes quitSDL(); -#endif return true; } @@ -323,7 +316,6 @@ QRect Launcher::GraphicsPage::getMaximumResolution() { QRect max; -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) for (QScreen* screen : QGuiApplication::screens()) { QRect res = screen->geometry(); @@ -332,17 +324,6 @@ QRect Launcher::GraphicsPage::getMaximumResolution() if (res.height() > max.height()) max.setHeight(res.height()); } -#else - int screens = QApplication::desktop()->screenCount(); - for (int i = 0; i < screens; ++i) - { - QRect res = QApplication::desktop()->screenGeometry(i); - if (res.width() > max.width()) - max.setWidth(res.width()); - if (res.height() > max.height()) - max.setHeight(res.height()); - } -#endif return max; } diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index cf720f8b8..f15abafce 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -13,18 +13,11 @@ #endif // MAC_OS_X_VERSION_MIN_REQUIRED #include "maindialog.hpp" -#include "sdlinit.hpp" int main(int argc, char *argv[]) { try { -// Note: we should init SDL2 before Qt4 to avoid crashes on Linux, -// but we should init SDL2 after Qt5 to avoid input issues on MacOS X. -#if QT_VERSION < QT_VERSION_CHECK(5,0,0) - initSDL(); -#endif - QApplication app(argc, argv); // Internationalization @@ -50,11 +43,6 @@ int main(int argc, char *argv[]) int exitCode = app.exec(); -#if QT_VERSION < QT_VERSION_CHECK(5,0,0) - // Disconnect from SDL processes - quitSDL(); -#endif - return exitCode; } catch (std::exception& e) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 5dc2fb26f..401e7896d 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -149,16 +149,9 @@ if(WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) -if (DESIRED_QT_VERSION MATCHES 4) - include(${QT_USE_FILE}) - qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) - qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT}) - qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) -else() - qt5_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) - qt5_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT}) - qt5_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) -endif() +qt5_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) +qt5_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT}) +qt5_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) # for compiled .ui files include_directories(${CMAKE_CURRENT_BINARY_DIR}) @@ -236,19 +229,7 @@ target_link_libraries(openmw-cs components ) -if (DESIRED_QT_VERSION MATCHES 4) - target_link_libraries(openmw-cs - ${QT_QTGUI_LIBRARY} - ${QT_QTCORE_LIBRARY} - ${QT_QTNETWORK_LIBRARY} - ${QT_QTOPENGL_LIBRARY}) - - if (WIN32) - target_link_libraries(openmw-cs ${QT_QTMAIN_LIBRARY}) - endif() -else() - target_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL) -endif() +target_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL) if (WIN32) target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY}) diff --git a/apps/opencs/model/prefs/shortcutmanager.cpp b/apps/opencs/model/prefs/shortcutmanager.cpp index c4b46958d..f39492c6c 100644 --- a/apps/opencs/model/prefs/shortcutmanager.cpp +++ b/apps/opencs/model/prefs/shortcutmanager.cpp @@ -685,7 +685,6 @@ namespace CSMPrefs std::make_pair((int)Qt::Key_ContrastAdjust , "ContrastAdjust"), std::make_pair((int)Qt::Key_LaunchG , "LaunchG"), std::make_pair((int)Qt::Key_LaunchH , "LaunchH"), -#if QT_VERSION >= QT_VERSION_CHECK(5,7,0) std::make_pair((int)Qt::Key_TouchpadToggle , "TouchpadToggle"), std::make_pair((int)Qt::Key_TouchpadOn , "TouchpadOn"), std::make_pair((int)Qt::Key_TouchpadOff , "TouchpadOff"), @@ -706,7 +705,6 @@ namespace CSMPrefs std::make_pair((int)Qt::Key_Find , "Find"), std::make_pair((int)Qt::Key_Undo , "Undo"), std::make_pair((int)Qt::Key_Redo , "Redo"), -#endif std::make_pair((int)Qt::Key_AltGr , "AltGr"), std::make_pair((int)Qt::Key_Multi_key , "Multi_key"), std::make_pair((int)Qt::Key_Kanji , "Kanji"), @@ -770,9 +768,7 @@ namespace CSMPrefs std::make_pair((int)Qt::Key_Sleep , "Sleep"), std::make_pair((int)Qt::Key_Play , "Play"), std::make_pair((int)Qt::Key_Zoom , "Zoom"), -#if QT_VERSION >= QT_VERSION_CHECK(5,7,0) std::make_pair((int)Qt::Key_Exit , "Exit"), -#endif std::make_pair((int)Qt::Key_Context1 , "Context1"), std::make_pair((int)Qt::Key_Context2 , "Context2"), std::make_pair((int)Qt::Key_Context3 , "Context3"), diff --git a/apps/opencs/view/doc/newgame.cpp b/apps/opencs/view/doc/newgame.cpp index 8e976c68f..7b247652e 100644 --- a/apps/opencs/view/doc/newgame.cpp +++ b/apps/opencs/view/doc/newgame.cpp @@ -5,10 +5,7 @@ #include #include #include - -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include -#endif #include "filewidget.hpp" #include "adjusterwidget.hpp" @@ -50,11 +47,7 @@ CSVDoc::NewGameDialogue::NewGameDialogue() connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), mAdjusterWidget, SLOT (setName (const QString&, bool))); -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) QRect scr = QGuiApplication::primaryScreen()->geometry(); -#else - QRect scr = QApplication::desktop()->screenGeometry(); -#endif QRect rect = geometry(); move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); } diff --git a/apps/opencs/view/doc/startup.cpp b/apps/opencs/view/doc/startup.cpp index beb2cf7d8..3a1950a6e 100644 --- a/apps/opencs/view/doc/startup.cpp +++ b/apps/opencs/view/doc/startup.cpp @@ -9,10 +9,7 @@ #include #include #include - -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include -#endif QPushButton *CSVDoc::StartupDialogue::addButton (const QString& label, const QIcon& icon) { @@ -123,12 +120,7 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2) setLayout (layout); -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) QRect scr = QGuiApplication::primaryScreen()->geometry(); -#else - QRect scr = QApplication::desktop()->screenGeometry(); -#endif - QRect rect = geometry(); move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index ce04f6ada..ac7c8ebf9 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -14,10 +14,7 @@ #include #include #include - -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include -#endif #include "../../model/doc/document.hpp" #include "../../model/prefs/state.hpp" @@ -1071,11 +1068,7 @@ void CSVDoc::View::updateWidth(bool isGrowLimit, int minSubViewWidth) if (isGrowLimit) rect = dw->screenGeometry(this); else -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) rect = QGuiApplication::screens().at(dw->screenNumber(this))->geometry(); -#else - rect = dw->screenGeometry(dw->screen(dw->screenNumber(this))); -#endif if (!mScrollbarOnly && mScroll && mSubViews.size() > 1) { diff --git a/apps/opencs/view/prefs/dialogue.cpp b/apps/opencs/view/prefs/dialogue.cpp index 853031ccd..7e41fcf82 100644 --- a/apps/opencs/view/prefs/dialogue.cpp +++ b/apps/opencs/view/prefs/dialogue.cpp @@ -1,4 +1,3 @@ - #include "dialogue.hpp" #include @@ -7,10 +6,7 @@ #include #include #include - -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include -#endif #include @@ -39,11 +35,7 @@ void CSVPrefs::Dialogue::buildCategorySelector (QSplitter *main) { QString label = QString::fromUtf8 (iter->second.getKey().c_str()); -#if QT_VERSION >= QT_VERSION_CHECK(5,11,0) maxWidth = std::max (maxWidth, metrics.horizontalAdvance (label)); -#else - maxWidth = std::max (maxWidth, metrics.width (label)); -#endif list->addItem (label); } @@ -116,11 +108,7 @@ void CSVPrefs::Dialogue::show() } else { -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) QRect scr = QGuiApplication::primaryScreen()->geometry(); -#else - QRect scr = QApplication::desktop()->screenGeometry(); -#endif // otherwise place at the centre of the screen QPoint screenCenter = scr.center(); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index e50d7b2cd..07131cb7b 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -143,14 +143,9 @@ void RenderWidget::toggleRenderStats() CompositeViewer::CompositeViewer() : mSimulationTime(0.0) { -#if QT_VERSION >= 0x050000 - // Qt5 is currently crashing and reporting "Cannot make QOpenGLContext current in a different thread" when the viewer is run multi-threaded, this is regression from Qt4 - osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::SingleThreaded; -#else - osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::DrawThreadPerContext; -#endif - - setThreadingModel(threadingModel); + // TODO: Upgrade osgQt to support osgViewer::ViewerBase::DrawThreadPerContext + // https://gitlab.com/OpenMW/openmw/-/issues/5481 + setThreadingModel(osgViewer::ViewerBase::SingleThreaded); #if OSG_VERSION_GREATER_OR_EQUAL(3,5,5) setUseConfigureAffinity(false); diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 8e54b9a81..32f4a8ef3 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -646,13 +646,8 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) if (mDragging) { -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) mDragX = event->localPos().x(); mDragY = height() - event->localPos().y(); -#else - mDragX = event->posF().x(); - mDragY = height() - event->posF().y(); -#endif } } else diff --git a/apps/opencs/view/tools/reporttable.cpp b/apps/opencs/view/tools/reporttable.cpp index a970af168..69caba547 100644 --- a/apps/opencs/view/tools/reporttable.cpp +++ b/apps/opencs/view/tools/reporttable.cpp @@ -144,11 +144,7 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document, : CSVWorld::DragRecordTable (document, parent), mModel (document.getReport (id)), mRefreshAction (0), mRefreshState (refreshState) { -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); -#else - horizontalHeader()->setResizeMode (QHeaderView::Interactive); -#endif horizontalHeader()->setStretchLastSection (true); verticalHeader()->hide(); setSortingEnabled (true); diff --git a/apps/opencs/view/widget/coloreditor.cpp b/apps/opencs/view/widget/coloreditor.cpp index cd19e54a3..1cc649313 100644 --- a/apps/opencs/view/widget/coloreditor.cpp +++ b/apps/opencs/view/widget/coloreditor.cpp @@ -5,10 +5,7 @@ #include #include #include - -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include -#endif #include "colorpickerpopup.hpp" @@ -99,11 +96,7 @@ QPoint CSVWidget::ColorEditor::calculatePopupPosition() { QRect editorGeometry = geometry(); QRect popupGeometry = mColorPicker->geometry(); -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) QRect screenGeometry = QGuiApplication::primaryScreen()->geometry(); -#else - QRect screenGeometry = QApplication::desktop()->screenGeometry(); -#endif // Center the popup horizontally relative to the editor int localPopupX = (editorGeometry.width() - popupGeometry.width()) / 2; diff --git a/apps/opencs/view/widget/scenetoolrun.cpp b/apps/opencs/view/widget/scenetoolrun.cpp index 1e2d44e7a..b53282036 100644 --- a/apps/opencs/view/widget/scenetoolrun.cpp +++ b/apps/opencs/view/widget/scenetoolrun.cpp @@ -64,13 +64,8 @@ CSVWidget::SceneToolRun::SceneToolRun (SceneToolbar *parent, const QString& tool mTable->setShowGrid (false); mTable->verticalHeader()->hide(); mTable->horizontalHeader()->hide(); -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents); -#else - mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch); - mTable->horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents); -#endif mTable->setSelectionMode (QAbstractItemView::NoSelection); layout->addWidget (mTable); diff --git a/apps/opencs/view/widget/scenetoolshapebrush.cpp b/apps/opencs/view/widget/scenetoolshapebrush.cpp index e4647d600..4b2d20004 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.cpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.cpp @@ -180,13 +180,8 @@ CSVWidget::SceneToolShapeBrush::SceneToolShapeBrush (SceneToolbar *parent, const mTable->setShowGrid (true); mTable->verticalHeader()->hide(); mTable->horizontalHeader()->hide(); -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch); -#else - mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch); - mTable->horizontalHeader()->setResizeMode (1, QHeaderView::Stretch); -#endif mTable->setSelectionMode (QAbstractItemView::NoSelection); layout->addWidget (mTable); diff --git a/apps/opencs/view/widget/scenetooltexturebrush.cpp b/apps/opencs/view/widget/scenetooltexturebrush.cpp index 35937f1a6..272a5de42 100644 --- a/apps/opencs/view/widget/scenetooltexturebrush.cpp +++ b/apps/opencs/view/widget/scenetooltexturebrush.cpp @@ -243,13 +243,8 @@ CSVWidget::SceneToolTextureBrush::SceneToolTextureBrush (SceneToolbar *parent, c mTable->setShowGrid (true); mTable->verticalHeader()->hide(); mTable->horizontalHeader()->hide(); -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch); -#else - mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch); - mTable->horizontalHeader()->setResizeMode (1, QHeaderView::Stretch); -#endif mTable->setSelectionMode (QAbstractItemView::NoSelection); layout->addWidget (mTable); diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 0f920db94..eb1d63105 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -108,13 +108,8 @@ void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewIte const QModelIndex& index) const { int valueIndex = getValueIndex(index); - if (valueIndex != -1) - { -#if QT_VERSION >= QT_VERSION_CHECK(5,7,0) + if (valueIndex != - QStyleOptionViewItem itemOption(option); -#else - QStyleOptionViewItemV4 itemOption(option); -#endif itemOption.text = mValues.at(valueIndex).second; QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter); } @@ -134,13 +129,7 @@ QSize CSVWorld::EnumDelegate::sizeHint(const QStyleOptionViewItem &option, const itemOption.state = option.state; const QString &valueText = mValues.at(valueIndex).second; - -#if QT_VERSION >= QT_VERSION_CHECK(5,11,0) QSize valueSize = QSize(itemOption.fontMetrics.horizontalAdvance(valueText), itemOption.fontMetrics.height()); -#else - QSize valueSize = QSize(itemOption.fontMetrics.width(valueText), itemOption.fontMetrics.height()); -#endif - itemOption.currentText = valueText; return QApplication::style()->sizeFromContents(QStyle::CT_ComboBox, &itemOption, valueSize); } diff --git a/apps/opencs/view/world/nestedtable.cpp b/apps/opencs/view/world/nestedtable.cpp index 1b72211e8..d52f7ca73 100644 --- a/apps/opencs/view/world/nestedtable.cpp +++ b/apps/opencs/view/world/nestedtable.cpp @@ -33,11 +33,7 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionMode (QAbstractItemView::ExtendedSelection); -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); -#else - horizontalHeader()->setResizeMode (QHeaderView::Interactive); -#endif verticalHeader()->hide(); int columns = model->columnCount(QModelIndex()); diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index 996e80da4..9083359d2 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -205,12 +205,7 @@ bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) const void CSVWorld::ScriptEdit::setTabWidth() { // Set tab width to specified number of characters using current font. -#if QT_VERSION >= QT_VERSION_CHECK(5,11,0) setTabStopDistance(mTabCharCount * fontMetrics().horizontalAdvance(' ')); -#else - setTabStopWidth(mTabCharCount * fontMetrics().width(' ')); -#endif - } void CSVWorld::ScriptEdit::wrapLines(bool wrap) @@ -290,12 +285,7 @@ int CSVWorld::ScriptEdit::lineNumberAreaWidth() ++digits; } -#if QT_VERSION >= QT_VERSION_CHECK(5,11,0) int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits; -#else - int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits; -#endif - return space; } diff --git a/apps/opencs/view/world/scripterrortable.cpp b/apps/opencs/view/world/scripterrortable.cpp index ca7cbd159..45809b28c 100644 --- a/apps/opencs/view/world/scripterrortable.cpp +++ b/apps/opencs/view/world/scripterrortable.cpp @@ -83,13 +83,8 @@ CSVWorld::ScriptErrorTable::ScriptErrorTable (const CSMDoc::Document& document, QStringList headers; headers << "Severity" << "Line" << "Description"; setHorizontalHeaderLabels (headers); -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) horizontalHeader()->setSectionResizeMode (0, QHeaderView::ResizeToContents); horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents); -#else - horizontalHeader()->setResizeMode (0, QHeaderView::ResizeToContents); - horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents); -#endif horizontalHeader()->setStretchLastSection (true); verticalHeader()->hide(); setColumnHidden (3, true); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index e2bc87a72..dcf143a66 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -262,11 +262,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); setModel (mProxyModel); -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); -#else - horizontalHeader()->setResizeMode (QHeaderView::Interactive); -#endif verticalHeader()->hide(); setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionMode (QAbstractItemView::ExtendedSelection); diff --git a/apps/wizard/CMakeLists.txt b/apps/wizard/CMakeLists.txt index 8d97bbcbf..10e06d1ff 100644 --- a/apps/wizard/CMakeLists.txt +++ b/apps/wizard/CMakeLists.txt @@ -79,16 +79,9 @@ if(WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) -if (DESIRED_QT_VERSION MATCHES 4) - include(${QT_USE_FILE}) - QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/wizard/wizard.qrc) - QT4_WRAP_CPP(MOC_SRCS ${WIZARD_HEADER_MOC}) - QT4_WRAP_UI(UI_HDRS ${WIZARD_UI}) -else() - QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/wizard/wizard.qrc) - QT5_WRAP_CPP(MOC_SRCS ${WIZARD_HEADER_MOC}) - QT5_WRAP_UI(UI_HDRS ${WIZARD_UI}) -endif() +QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/wizard/wizard.qrc) +QT5_WRAP_CPP(MOC_SRCS ${WIZARD_HEADER_MOC}) +QT5_WRAP_UI(UI_HDRS ${WIZARD_UI}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) @@ -109,23 +102,12 @@ target_link_libraries(openmw-wizard components ) -if (DESIRED_QT_VERSION MATCHES 4) - target_link_libraries(openmw-wizard - ${QT_QTGUI_LIBRARY} - ${QT_QTCORE_LIBRARY}) - - if (WIN32) - target_link_libraries(openmw-wizard ${QT_QTMAIN_LIBRARY}) - endif() -else() - target_link_libraries(openmw-wizard Qt5::Widgets Qt5::Core) -endif() +target_link_libraries(openmw-wizard Qt5::Widgets Qt5::Core) if (OPENMW_USE_UNSHIELD) target_link_libraries(openmw-wizard ${LIBUNSHIELD_LIBRARIES}) endif() - if(DPKG_PROGRAM) INSTALL(TARGETS openmw-wizard RUNTIME DESTINATION games COMPONENT openmw-wizard) endif() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index d5ab8d8fe..1d4a84ce1 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -200,14 +200,8 @@ if (USE_QT) helpviewer ) - if (DESIRED_QT_VERSION MATCHES 4) - include(${QT_USE_FILE}) - QT4_WRAP_UI(ESM_UI_HDR ${ESM_UI}) - QT4_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) - else() - QT5_WRAP_UI(ESM_UI_HDR ${ESM_UI}) - QT5_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) - endif() + QT5_WRAP_UI(ESM_UI_HDR ${ESM_UI}) + QT5_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) endif() if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -252,13 +246,7 @@ if (WIN32) endif() if (USE_QT) - if (DESIRED_QT_VERSION MATCHES 4) - target_link_libraries(components - ${QT_QTCORE_LIBRARY} - ${QT_QTGUI_LIBRARY}) - else() - target_link_libraries(components Qt5::Widgets Qt5::Core) - endif() + target_link_libraries(components Qt5::Widgets Qt5::Core) endif() if (GIT_CHECKOUT) diff --git a/extern/osgQt/CMakeLists.txt b/extern/osgQt/CMakeLists.txt index 78a4e6034..6cf2dc935 100644 --- a/extern/osgQt/CMakeLists.txt +++ b/extern/osgQt/CMakeLists.txt @@ -8,12 +8,7 @@ set(OSGQT_SOURCE_FILES add_library(${OSGQT_LIBRARY} STATIC ${OSGQT_SOURCE_FILES}) -if (DESIRED_QT_VERSION MATCHES 4) - include(${QT_USE_FILE}) - target_link_libraries(${OSGQT_LIBRARY} ${QT_QTCORE_LIBRARY} ${QT_QTOPENGL_LIBRARY}) -else() - target_link_libraries(${OSGQT_LIBRARY} Qt5::Core Qt5::OpenGL) -endif() +target_link_libraries(${OSGQT_LIBRARY} Qt5::Core Qt5::OpenGL) link_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/extern/osgQt/GraphicsWindowQt.cpp b/extern/osgQt/GraphicsWindowQt.cpp index aa9b4bbdb..e7cc88085 100644 --- a/extern/osgQt/GraphicsWindowQt.cpp +++ b/extern/osgQt/GraphicsWindowQt.cpp @@ -17,18 +17,11 @@ #include #include #include - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) #include -#endif using namespace osgQt; -#if (QT_VERSION < QT_VERSION_CHECK(5, 2, 0)) - #define GETDEVICEPIXELRATIO() 1.0 -#else - #define GETDEVICEPIXELRATIO() devicePixelRatio() -#endif +#define GETDEVICEPIXELRATIO() devicePixelRatio() GLWidget::GLWidget( QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f) : QGLWidget(parent, shareWidget, f), _gw( NULL ) @@ -119,13 +112,11 @@ bool GLWidget::event( QEvent* event ) enqueueDeferredEvent(QEvent::ParentChange); return true; } -#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) else if (event->type() == QEvent::PlatformSurface && static_cast(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) { if (_gw) _gw->close(); } -#endif // perform regular event handling return QGLWidget::event( event ); @@ -209,11 +200,7 @@ bool GraphicsWindowQt::init( QWidget* parent, const QGLWidget* shareWidget, Qt:: // WindowFlags Qt::WindowFlags flags = f | Qt::Window | Qt::CustomizeWindowHint; if ( _traits->windowDecoration ) - flags |= Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowSystemMenuHint -#if (QT_VERSION_CHECK(4, 5, 0) <= QT_VERSION) - | Qt::WindowCloseButtonHint -#endif - ; + flags |= Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; // create widget _widget = new GLWidget( traits2qglFormat( _traits.get() ), parent, shareWidget, flags ); @@ -527,11 +514,10 @@ 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 + // 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 From a4c9a5cb0039ae07e34796f72f6d51d6a1c2a021 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 22 Jun 2020 12:41:51 +0200 Subject: [PATCH 76/81] Use docker image debian:bullseye as our target --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3d798845e..47cf7fb18 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,7 @@ Debian: tags: - docker - linux - image: gcc + image: debian:bullseye cache: key: apt-cache paths: From 6846f5a21226102dd517121712915d8d0cbb3134 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 22 Jun 2020 12:43:36 +0200 Subject: [PATCH 77/81] While we are at it, go full OSG 3.6 --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 47cf7fb18..c652e6026 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ Debian: before_script: - export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR - apt-get update -yq - - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev + - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev stage: build script: - cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi From 8d3b1b90b110d988a9fe1835a5df06ff421fb526 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 22 Jun 2020 12:49:59 +0200 Subject: [PATCH 78/81] do not assume gcc is available; added build-essential --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c652e6026..1f8bc6417 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ Debian: before_script: - export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR - apt-get update -yq - - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev + - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake build-essential libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev stage: build script: - cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi From 8f9c3e28d955b2798bdf4c4b042b3f63cbf0bd30 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 22 Jun 2020 13:48:35 +0200 Subject: [PATCH 79/81] Handle typo --- apps/opencs/view/world/enumdelegate.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index eb1d63105..8027cec62 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -108,7 +108,8 @@ void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewIte const QModelIndex& index) const { int valueIndex = getValueIndex(index); - if (valueIndex != - + if (valueIndex != -1) + { QStyleOptionViewItem itemOption(option); itemOption.text = mValues.at(valueIndex).second; QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter); From 2fb7c27976472bc987a45c3f2e100135865554b3 Mon Sep 17 00:00:00 2001 From: psi29a Date: Mon, 22 Jun 2020 13:20:31 +0000 Subject: [PATCH 80/81] Resolved by !235 when dropping Qt4 in Task #5480 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56069bdc1..060c857e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.47.0 ------ + Bug #1662: Qt4 and Windows binaries crash if there's a non-ASCII character in a file path/config path Bug #1952: Incorrect particle lighting Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #3676: NiParticleColorModifier isn't applied properly From ccedde3e0a3b83492e4d7279a5031240a9f11342 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 22 Jun 2020 15:51:56 +0200 Subject: [PATCH 81/81] Get OpenMW ready for Focal update on GH --- .travis.yml | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5ce71a7b4..b45c59550 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,10 +14,10 @@ addons: apt: sources: - sourceline: 'ppa:openmw/openmw' - - ubuntu-toolchain-r-test + # - ubuntu-toolchain-r-test # for GCC-10 packages: [ # Dev - cmake, clang-tools, gcc-9, g++-9, ccache, + build-essential, cmake, clang-tools, ccache, # Boost libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg @@ -41,21 +41,14 @@ matrix: os: osx osx_image: xcode10.2 if: branch != coverity_scan - - name: OpenMW (all) on Ubuntu Bionic GCC-7 + - name: OpenMW (all) on Ubuntu Focal GCC-9 os: linux - dist: bionic + dist: focal sudo: required if: branch != coverity_scan - - name: OpenMW (all) on Ubuntu Bionic GCC-9 + - name: OpenMW (openmw) on Ubuntu Focal Clang-10 with Static Analysis os: linux - dist: bionic - sudo: required - env: - - MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" - if: branch != coverity_scan - - name: OpenMW (openmw) on Ubuntu Bionic Clang-6 with Static Analysis - os: linux - dist: bionic + dist: focal sudo: required env: - MATRIX_EVAL="CC=clang && CXX=clang++" @@ -63,9 +56,9 @@ matrix: - BUILD_OPENMW_CS="OFF" if: branch != coverity_scan compiler: clang - - name: OpenMW (openmw-cs) on Ubuntu Bionic Clang-6 with Static Analysis + - name: OpenMW (openmw-cs and tools) on Ubuntu Focal Clang-10 with Static Analysis os: linux - dist: bionic + dist: focal sudo: required env: - MATRIX_EVAL="CC=clang && CXX=clang++" @@ -75,11 +68,13 @@ matrix: compiler: clang - name: OpenMW Components Coverity Scan os: linux - dist: bionic + dist: focal sudo: required if: branch = coverity_scan # allow_failures: -# - name: OpenMW (openmw) on Ubuntu Bionic Clang-6 with Static Analysis +# - name: OpenMW (openmw) on Ubuntu Focal with GCC-10 +# env: +# - MATRIX_EVAL="CC=gcc-10 && CXX=g++-10" before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi