From 6bfdf0e57fd67142fcaad37388b002ad57b6770a Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Fri, 4 Dec 2020 22:21:23 +0100 Subject: [PATCH 01/51] Add more information on mouse over level --- apps/openmw/mwgui/statswindow.cpp | 9 +++++++++ apps/openmw/mwmechanics/npcstats.cpp | 5 +++++ apps/openmw/mwmechanics/npcstats.hpp | 2 ++ files/mygui/openmw_tooltips.layout | 6 ++++++ 4 files changed, 22 insertions(+) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 2a3e2cd85..e02b2f45f 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -335,6 +335,15 @@ namespace MWGui { int max = MWBase::Environment::get().getWorld()->getStore().get().find("iLevelUpTotal")->mValue.getInteger(); getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); + + std::string detail; + for (int i = 0; i < ESM::Attribute::Length; ++i) + { + if (auto increase = PCstats.getLevelUpAttributeIncrease(i)) + detail += (detail.empty() ? "" : "\n") + ESM::Attribute::sAttributeNames[i] + " x" + MyGUI::utility::toString(increase); + } + if (!detail.empty()) + levelWidget->setUserString("Caption_LevelDetailText", detail); levelWidget->setUserString("RangePosition_LevelProgress", MyGUI::utility::toString(PCstats.getLevelProgress())); levelWidget->setUserString("Range_LevelProgress", MyGUI::utility::toString(max)); levelWidget->setUserString("Caption_LevelProgressText", MyGUI::utility::toString(PCstats.getLevelProgress()) + "/" diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 5d19368bf..71453cd07 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -322,6 +322,11 @@ void MWMechanics::NpcStats::updateHealth() setHealth(floor(0.5f * (strength + endurance))); } +int MWMechanics::NpcStats::getLevelUpAttributeIncrease(int attribute) const +{ + return mSkillIncreases[attribute]; +} + int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const { int num = mSkillIncreases[attribute]; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 9bd8e20ad..cab52cb28 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -87,6 +87,8 @@ namespace MWMechanics int getLevelProgress() const; + int getLevelUpAttributeIncrease(int attribute) const; + int getLevelupAttributeMultiplier(int attribute) const; int getSkillIncreasesForSpecialization(int spec) const; diff --git a/files/mygui/openmw_tooltips.layout b/files/mygui/openmw_tooltips.layout index 70246fa4e..59050f932 100644 --- a/files/mygui/openmw_tooltips.layout +++ b/files/mygui/openmw_tooltips.layout @@ -267,6 +267,12 @@ + + + + + + From 2d3d22025a5f278ffa350fd6d69dd8bb0dab59eb Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Fri, 4 Dec 2020 22:28:36 +0100 Subject: [PATCH 02/51] Avoid height for empty message in AutoSizedTextBox --- components/widgets/box.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/widgets/box.cpp b/components/widgets/box.cpp index 23a108ee9..3e8f62b4b 100644 --- a/components/widgets/box.cpp +++ b/components/widgets/box.cpp @@ -32,7 +32,7 @@ namespace Gui MyGUI::IntSize AutoSizedTextBox::getRequestedSize() { - return getTextSize(); + return getCaption().empty() ? MyGUI::IntSize{0, 0} : getTextSize(); } void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) From 86c31ded533a737d681297e63f5917060993a9f3 Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Mon, 7 Dec 2020 14:56:29 +0100 Subject: [PATCH 03/51] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0c6dd5ee..e1e71256a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ Bug #5688: Water shader broken indoors with enable indoor shadows = false Bug #5703: OpenMW-CS menu system crashing on XFCE Feature #390: 3rd person look "over the shoulder" + Feature #1536: Show more information about level on menu Feature #2386: Distant Statics in the form of Object Paging Feature #2404: Levelled List can not be placed into a container Feature #4894: Consider actors as obstacles for pathfinding From 807367ca3f771a99b4ab270c31f6ae9cbfdb3360 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 7 Dec 2020 19:09:58 +0400 Subject: [PATCH 04/51] Mark mock methods as overrides (requires GTest 1.10) --- apps/openmw_test_suite/CMakeLists.txt | 4 ++-- .../nifloader/testbulletnifloader.cpp | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index cd2d2e80a..5658a5eef 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -1,5 +1,5 @@ -find_package(GTest REQUIRED) -find_package(GMock REQUIRED) +find_package(GTest 1.10 REQUIRED) +find_package(GMock 1.10 REQUIRED) if (GTEST_FOUND AND GMOCK_FOUND) include_directories(SYSTEM ${GTEST_INCLUDE_DIRS}) diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 7da8f0fd5..355bfdb1c 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -293,22 +293,22 @@ namespace struct NifFileMock : Nif::File { - MOCK_CONST_METHOD1(getRecord, Nif::Record* (std::size_t)); - MOCK_CONST_METHOD0(numRecords, std::size_t ()); - MOCK_CONST_METHOD1(getRoot, Nif::Record* (std::size_t)); - MOCK_CONST_METHOD0(numRoots, std::size_t ()); - MOCK_CONST_METHOD1(getString, std::string (uint32_t)); - MOCK_METHOD1(setUseSkinning, void (bool)); - MOCK_CONST_METHOD0(getUseSkinning, bool ()); - MOCK_CONST_METHOD0(getFilename, std::string ()); - MOCK_CONST_METHOD0(getVersion, unsigned int ()); - MOCK_CONST_METHOD0(getUserVersion, unsigned int ()); - MOCK_CONST_METHOD0(getBethVersion, unsigned int ()); + MOCK_METHOD(Nif::Record*, getRecord, (std::size_t), (const, override)); + MOCK_METHOD(std::size_t, numRecords, (), (const, override)); + MOCK_METHOD(Nif::Record*, getRoot, (std::size_t), (const, override)); + MOCK_METHOD(std::size_t, numRoots, (), (const, override)); + MOCK_METHOD(std::string, getString, (uint32_t), (const, override)); + MOCK_METHOD(void, setUseSkinning, (bool), (override)); + MOCK_METHOD(bool, getUseSkinning, (), (const, override)); + MOCK_METHOD(std::string, getFilename, (), (const, override)); + MOCK_METHOD(unsigned int, getVersion, (), (const, override)); + MOCK_METHOD(unsigned int, getUserVersion, (), (const, override)); + MOCK_METHOD(unsigned int, getBethVersion, (), (const, override)); }; struct RecordMock : Nif::Record { - MOCK_METHOD1(read, void (Nif::NIFStream *nif)); + MOCK_METHOD(void, read, (Nif::NIFStream *nif), (override)); }; struct TestBulletNifLoader : Test From 61a4a0807b4ed2398b8169b477132b12a1a3a106 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 9 Dec 2020 00:10:58 +0200 Subject: [PATCH 05/51] Load master index in refId to mContentFile, keep master index also in mIndex --- apps/opencs/model/world/refcollection.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index d8f6b391b..126a0ea78 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -64,10 +64,12 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool // ignore content file number std::map::iterator iter = cache.begin(); - ref.mRefNum.mIndex = ref.mRefNum.mIndex & 0x00ffffff; + unsigned int thisIndex = ref.mRefNum.mIndex & 0x00ffffff; + if (ref.mRefNum.mContentFile != -1 && !base) ref.mRefNum.mContentFile = ref.mRefNum.mIndex >> 24; + for (; iter != cache.end(); ++iter) { - if (ref.mRefNum.mIndex == iter->first.mIndex) + if (thisIndex == iter->first.mIndex) break; } From 49c6e50c31082b00ce54c76d6ff7a7fe0c1e4d7d Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Tue, 8 Dec 2020 22:23:11 +0100 Subject: [PATCH 06/51] Print '--version' and '--help' messages without timestamps --- CHANGELOG.md | 1 + apps/openmw/main.cpp | 4 ++-- components/debug/debugging.cpp | 9 +++++++++ components/debug/debugging.hpp | 3 +++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e54515676..b09c60abb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ Feature #390: 3rd person look "over the shoulder" Feature #2386: Distant Statics in the form of Object Paging Feature #2404: Levelled List can not be placed into a container + Feature #2686: Timestamps in openmw.log Feature #4894: Consider actors as obstacles for pathfinding Feature #5043: Head Bobbing Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index aca050592..89aa2b9fd 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -135,7 +135,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat if (variables.count ("help")) { - std::cout << desc << std::endl; + getRawStdout() << desc << std::endl; return false; } @@ -144,7 +144,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat cfgMgr.readConfiguration(variables, desc, true); Version::Version v = Version::getOpenmwVersion(variables["resources"].as().mPath.string()); - std::cout << v.describe() << std::endl; + getRawStdout() << v.describe() << std::endl; return false; } diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index 88219dcbe..987a3db7e 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -1,6 +1,7 @@ #include "debugging.hpp" #include +#include #include @@ -133,11 +134,19 @@ namespace Debug } } +static std::unique_ptr rawStdout = nullptr; + +std::ostream& getRawStdout() +{ + return rawStdout ? *rawStdout : std::cout; +} + int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& appName) { #if defined _WIN32 (void)Debug::attachParentConsole(); #endif + rawStdout = std::make_unique(std::cout.rdbuf()); // Some objects used to redirect cout and cerr // Scope must be here, so this still works inside the catch block for logging exceptions diff --git a/components/debug/debugging.hpp b/components/debug/debugging.hpp index 39390446f..d8849cd89 100644 --- a/components/debug/debugging.hpp +++ b/components/debug/debugging.hpp @@ -135,6 +135,9 @@ namespace Debug #endif } +// Can be used to print messages without timestamps +std::ostream& getRawStdout(); + int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& appName); #endif From fd4a62ce35d5d2dc65e4b6353c38192a6f381554 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 9 Dec 2020 00:32:01 +0000 Subject: [PATCH 07/51] Use correct variable types when loading config for tests --- apps/openmw_test_suite/mwworld/test_store.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index 77aaccfdd..f3b2bb3dc 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -58,10 +59,10 @@ struct ContentFileTest : public ::testing::Test boost::program_options::options_description desc("Allowed options"); desc.add_options() - ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()->composing()) - ("content", boost::program_options::value >()->default_value(std::vector(), "") - ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") - ("data-local", boost::program_options::value()->default_value("")); + ("data", boost::program_options::value()->default_value(Files::EscapePathContainer(), "data")->multitoken()->composing()) + ("content", boost::program_options::value()->default_value(Files::EscapeStringVector(), "") + ->multitoken()->composing(), "content file(s): esm/esp, or omwgame/omwaddon") + ("data-local", boost::program_options::value()->default_value(Files::EscapePath(), "")); boost::program_options::notify(variables); @@ -69,12 +70,12 @@ struct ContentFileTest : public ::testing::Test Files::PathContainer dataDirs, dataLocal; if (!variables["data"].empty()) { - dataDirs = Files::PathContainer(variables["data"].as()); + dataDirs = Files::EscapePath::toPathContainer(variables["data"].as()); } - std::string local = variables["data-local"].as(); + Files::PathContainer::value_type local(variables["data-local"].as().mPath); if (!local.empty()) { - dataLocal.push_back(Files::PathContainer::value_type(local)); + dataLocal.push_back(local); } mConfigurationManager.processPaths (dataDirs); @@ -85,7 +86,7 @@ struct ContentFileTest : public ::testing::Test Files::Collections collections (dataDirs, true); - std::vector contentFiles = variables["content"].as >(); + std::vector contentFiles = variables["content"].as().toStdStringVector(); for (auto & contentFile : contentFiles) mContentFiles.push_back(collections.getPath(contentFile)); } From 525292b18418989c6ae37351bffb17cd30f42b4e Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 10 Dec 2020 19:02:38 +0100 Subject: [PATCH 08/51] Add graphic herbalism to the launcher --- CHANGELOG.md | 1 + apps/launcher/advancedpage.cpp | 2 ++ docs/source/reference/modding/settings/game.rst | 11 +++++++++++ files/ui/advancedpage.ui | 10 ++++++++++ 4 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b09c60abb..b16480b02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,7 @@ Feature #5649: Skyrim SE compressed BSA format support Feature #5672: Make stretch menu background configuration more accessible Feature #5692: Improve spell/magic item search to factor in magic effect names + Feature #5730: Add graphic herbalism option to the launcher and documents Task #5480: Drop Qt4 support Task #5520: Improve cell name autocompleter implementation diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index b35e78639..ed0407235 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -153,6 +153,7 @@ bool Launcher::AdvancedPage::loadSettings() if (showOwnedIndex >= 0 && showOwnedIndex <= 3) showOwnedComboBox->setCurrentIndex(showOwnedIndex); loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); + loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game"); } // Bug fixes @@ -279,6 +280,7 @@ void Launcher::AdvancedPage::saveSettings() if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game")) mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex); saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); + saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game"); } // Bug fixes diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index fd93eba61..4e1fe1318 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -428,3 +428,14 @@ even if the fighting NPC is knocked out. This setting allows the player to steal items from fighting NPCs that were knocked out if enabled. This setting can be controlled in Advanced tab of the launcher. + +graphic herbalism +----------------- + +:Type: boolean +:Range: True/False +:Default: True + +Some mods add harvestable container models. When this setting is enabled, activating a container using a harvestable model will visually harvest from it instead of opening the menu. + +When this setting is turned off or when activating a regular container, the menu will open as usual. diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 3f53180da..a3601ce94 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -724,6 +724,16 @@ True: In non-combat mode camera is positioned behind the character's shoulder. C + + + + <html><head/><body><p>If this setting is true, containers supporting graphic herbalism will do so instead of opening the menu.</p></body></html> + + + Enable graphic herbalism + + + From 7156b11dbc4e924a3d97654ed657f1d5b3dae88f Mon Sep 17 00:00:00 2001 From: psi29a Date: Thu, 10 Dec 2020 21:30:05 +0000 Subject: [PATCH 09/51] be explicit about narrowing to resolve: error: type 'btScalar *' (aka 'float *') cannot be narrowed to 'bool' in initializer list [-Wc++11-narrowing] --- apps/openmw_test_suite/detournavigator/recastmeshobject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw_test_suite/detournavigator/recastmeshobject.cpp b/apps/openmw_test_suite/detournavigator/recastmeshobject.cpp index a3606f827..621db51a8 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshobject.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshobject.cpp @@ -15,7 +15,7 @@ namespace struct DetourNavigatorRecastMeshObjectTest : Test { btBoxShape mBoxShape {btVector3(1, 2, 3)}; - btCompoundShape mCompoundShape {btVector3(1, 2, 3)}; + btCompoundShape mCompoundShape {true}; btTransform mTransform {btQuaternion(btVector3(1, 2, 3), 1), btVector3(1, 2, 3)}; DetourNavigatorRecastMeshObjectTest() From 842ea9d6ed06cbfc3044387f96582fda2b6316c2 Mon Sep 17 00:00:00 2001 From: Coleman Smith Date: Thu, 10 Dec 2020 21:36:46 +0000 Subject: [PATCH 10/51] simplifying a bit - attaching gradient camera directly to the main camera - using Vec4ub --- apps/opencs/model/prefs/state.cpp | 13 ++ apps/opencs/view/render/cellarrow.cpp | 4 +- apps/opencs/view/render/scenewidget.cpp | 154 ++++++++++++++++++++++-- apps/opencs/view/render/scenewidget.hpp | 7 +- 4 files changed, 168 insertions(+), 10 deletions(-) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 39aae48bd..588be9ccb 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -210,6 +210,19 @@ void CSMPrefs::State::declare() setTooltip("Size of the orthographic frustum, greater value will allow the camera to see more of the world."). setRange(10, 10000); declareDouble ("object-marker-alpha", "Object Marker Transparency", 0.5).setPrecision(2).setRange(0,1); + declareBool("scene-use-gradient", "Use Gradient Background", true); + declareColour ("scene-day-background-colour", "Day Background Colour", QColor (110, 120, 128, 255)); + declareColour ("scene-day-gradient-colour", "Day Gradient Colour", QColor (47, 51, 51, 255)). + setTooltip("Sets the gradient color to use in conjunction with the day background color. Ignored if " + "the gradient option is disabled."); + declareColour ("scene-bright-background-colour", "Scene Bright Background Colour", QColor (79, 87, 92, 255)); + declareColour ("scene-bright-gradient-colour", "Scene Bright Gradient Colour", QColor (47, 51, 51, 255)). + setTooltip("Sets the gradient color to use in conjunction with the bright background color. Ignored if " + "the gradient option is disabled."); + declareColour ("scene-night-background-colour", "Scene Night Background Colour", QColor (64, 77, 79, 255)); + declareColour ("scene-night-gradient-colour", "Scene Night Gradient Colour", QColor (47, 51, 51, 255)). + setTooltip("Sets the gradient color to use in conjunction with the night background color. Ignored if " + "the gradient option is disabled."); declareCategory ("Tooltips"); declareBool ("scene", "Show Tooltips in 3D scenes", true); diff --git a/apps/opencs/view/render/cellarrow.cpp b/apps/opencs/view/render/cellarrow.cpp index b6fee1545..ac260fe83 100644 --- a/apps/opencs/view/render/cellarrow.cpp +++ b/apps/opencs/view/render/cellarrow.cpp @@ -151,9 +151,9 @@ void CSVRender::CellArrow::buildShape() osg::Vec4Array *colours = new osg::Vec4Array; for (int i=0; i<6; ++i) - colours->push_back (osg::Vec4f (1.0f, 0.0f, 0.0f, 1.0f)); + colours->push_back (osg::Vec4f (0.11, 0.6f, 0.95f, 1.0f)); for (int i=0; i<6; ++i) - colours->push_back (osg::Vec4f (0.8f, (i==2 || i==5) ? 0.6f : 0.4f, 0.0f, 1.0f)); + colours->push_back (osg::Vec4f (0.08f, 0.44f, 0.7f, 1.0f)); geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 3abc01d2e..b2648da67 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -69,7 +69,6 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f) setLayout(layout); mView->getCamera()->setGraphicsContext(window); - mView->getCamera()->setClearColor( osg::Vec4(0.2, 0.2, 0.6, 1.0) ); mView->getCamera()->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) ); SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager; @@ -212,6 +211,25 @@ SceneWidget::SceneWidget(std::shared_ptr resourceSyste mOrbitCamControl->setConstRoll( CSMPrefs::get()["3D Scene Input"]["navi-orbit-const-roll"].isTrue() ); + // set up gradient view or configured clear color + QColor bgColour = CSMPrefs::get()["Rendering"]["scene-day-background-colour"].toColor(); + + if (CSMPrefs::get()["Rendering"]["scene-use-gradient"].isTrue()) { + QColor gradientColour = CSMPrefs::get()["Rendering"]["scene-day-gradient-colour"].toColor(); + mGradientCamera = createGradientCamera(bgColour, gradientColour); + + mView->getCamera()->setClearMask(0); + mView->getCamera()->addChild(mGradientCamera.get()); + } + else { + mView->getCamera()->setClearColor(osg::Vec4( + bgColour.redF(), + bgColour.greenF(), + bgColour.blueF(), + 1.0f + )); + } + // we handle lighting manually mView->setLightingMode(osgViewer::View::NO_LIGHT); @@ -249,6 +267,81 @@ SceneWidget::~SceneWidget() mResourceSystem->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState()); } + +osg::ref_ptr SceneWidget::createGradientRectangle(QColor bgColour, QColor gradientColour) +{ + osg::ref_ptr geometry = new osg::Geometry; + + osg::ref_ptr vertices = new osg::Vec3Array; + + vertices->push_back(osg::Vec3(0.0f, 0.0f, -1.0f)); + vertices->push_back(osg::Vec3(1.0f, 0.0f, -1.0f)); + vertices->push_back(osg::Vec3(0.0f, 1.0f, -1.0f)); + vertices->push_back(osg::Vec3(1.0f, 1.0f, -1.0f)); + + geometry->setVertexArray(vertices); + + osg::ref_ptr primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLES, 0); + + // triangle 1 + primitives->push_back (0); + primitives->push_back (1); + primitives->push_back (2); + + // triangle 2 + primitives->push_back (2); + primitives->push_back (1); + primitives->push_back (3); + + geometry->addPrimitiveSet(primitives); + + osg::ref_ptr colours = new osg::Vec4ubArray; + colours->push_back(osg::Vec4ub(gradientColour.red(), gradientColour.green(), gradientColour.blue(), 1.0f)); + colours->push_back(osg::Vec4ub(gradientColour.red(), gradientColour.green(), gradientColour.blue(), 1.0f)); + colours->push_back(osg::Vec4ub(bgColour.red(), bgColour.green(), bgColour.blue(), 1.0f)); + colours->push_back(osg::Vec4ub(bgColour.red(), bgColour.green(), bgColour.blue(), 1.0f)); + + osg::Vec4ub bgVec = osg::Vec4ub(bgColour.red(), bgColour.green(), bgColour.blue(), 1.0f); + + geometry->setColorArray(colours, osg::Array::BIND_PER_VERTEX); + + geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + geometry->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + + return geometry; +} + + +osg::ref_ptr SceneWidget::createGradientCamera(QColor bgColour, QColor gradientColour) +{ + osg::ref_ptr camera = new osg::Camera(); + camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + camera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1.0f, 0, 1.0f)); + camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + camera->setViewMatrix(osg::Matrix::identity()); + + camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + camera->setAllowEventFocus(false); + + // draw subgraph before main camera view. + camera->setRenderOrder(osg::Camera::PRE_RENDER); + + camera->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + + osg::ref_ptr gradientQuad = createGradientRectangle(bgColour, gradientColour); + + camera->addChild(gradientQuad); + return camera; +} + + +void SceneWidget::updateGradientCamera(QColor bgColour, QColor gradientColour) +{ + osg::ref_ptr gradientRect = createGradientRectangle(bgColour, gradientColour); + // Replaces previous rectangle + mGradientCamera->setChild(0, gradientRect.get()); +} + void SceneWidget::setLighting(Lighting *lighting) { if (mLighting) @@ -276,12 +369,59 @@ void SceneWidget::setAmbient(const osg::Vec4f& ambient) void SceneWidget::selectLightingMode (const std::string& mode) { - if (mode=="day") - setLighting (&mLightingDay); - else if (mode=="night") - setLighting (&mLightingNight); - else if (mode=="bright") - setLighting (&mLightingBright); + QColor backgroundColour; + QColor gradientColour; + if (mode == "day") + { + backgroundColour = CSMPrefs::get()["Rendering"]["scene-day-background-colour"].toColor(); + gradientColour = CSMPrefs::get()["Rendering"]["scene-day-gradient-colour"].toColor(); + setLighting(&mLightingDay); + } + else if (mode == "night") + { + backgroundColour = CSMPrefs::get()["Rendering"]["scene-night-background-colour"].toColor(); + gradientColour = CSMPrefs::get()["Rendering"]["scene-night-gradient-colour"].toColor(); + setLighting(&mLightingNight); + } + else if (mode == "bright") + { + backgroundColour = CSMPrefs::get()["Rendering"]["scene-bright-background-colour"].toColor(); + gradientColour = CSMPrefs::get()["Rendering"]["scene-bright-gradient-colour"].toColor(); + setLighting(&mLightingBright); + } + if (CSMPrefs::get()["Rendering"]["scene-use-gradient"].isTrue()) { + if (mGradientCamera.get() != nullptr) { + // we can go ahead and update since this camera still exists + updateGradientCamera(backgroundColour, gradientColour); + + if (!mView->getCamera()->containsNode(mGradientCamera.get())) + { + // need to re-attach the gradient camera + mView->getCamera()->setClearMask(0); + mView->getCamera()->addChild(mGradientCamera.get()); + } + } + else { + // need to create the gradient camera + mGradientCamera = createGradientCamera(backgroundColour, gradientColour); + mView->getCamera()->setClearMask(0); + mView->getCamera()->addChild(mGradientCamera.get()); + } + } + else { + // Fall back to using the clear color for the camera + mView->getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + mView->getCamera()->setClearColor(osg::Vec4( + backgroundColour.redF(), + backgroundColour.greenF(), + backgroundColour.blueF(), + 1.0f + )); + if (mGradientCamera.get() != nullptr && mView->getCamera()->containsNode(mGradientCamera.get())) { + // Remove the child to prevent the gradient from rendering + mView->getCamera()->removeChild(mGradientCamera.get()); + } + } } CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent) diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 6a94254b9..d7d9dba0c 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -100,10 +100,15 @@ namespace CSVRender void mouseMoveEvent (QMouseEvent *event) override; void wheelEvent (QWheelEvent *event) override; + osg::ref_ptr createGradientRectangle(QColor bgColour, QColor gradientColour); + osg::ref_ptr createGradientCamera(QColor bgColour, QColor gradientColour); + void updateGradientCamera(QColor bgColour, QColor gradientColour); + std::shared_ptr mResourceSystem; Lighting* mLighting; - + + osg::ref_ptr mGradientCamera; osg::Vec4f mDefaultAmbient; bool mHasDefaultAmbient; bool mIsExterior; From d35715e5dcc7023cbac7949415c10ba94bc56c34 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Thu, 10 Dec 2020 22:39:54 +0100 Subject: [PATCH 11/51] add olcoal to authors and feature 5199 to changelog --- AUTHORS.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 09ab78412..e8134a681 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -49,6 +49,7 @@ Programmers Cédric Mocquillon Chris Boyce (slothlife) Chris Robinson (KittyCat) + Coleman Smith (olcoal) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) crussell187 diff --git a/CHANGELOG.md b/CHANGELOG.md index b16480b02..219234a38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ Feature #2686: Timestamps in openmw.log Feature #4894: Consider actors as obstacles for pathfinding Feature #5043: Head Bobbing + Feature #5199: Improve Scene Colors 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 From 3b9db413463dfc30d97836bf164066ead187c305 Mon Sep 17 00:00:00 2001 From: Coleman Smith Date: Thu, 10 Dec 2020 17:03:10 -0800 Subject: [PATCH 12/51] removing unneeded variable --- apps/opencs/view/render/scenewidget.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index b2648da67..4d73cde15 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -301,8 +301,6 @@ osg::ref_ptr SceneWidget::createGradientRectangle(QColor bgColour colours->push_back(osg::Vec4ub(bgColour.red(), bgColour.green(), bgColour.blue(), 1.0f)); colours->push_back(osg::Vec4ub(bgColour.red(), bgColour.green(), bgColour.blue(), 1.0f)); - osg::Vec4ub bgVec = osg::Vec4ub(bgColour.red(), bgColour.green(), bgColour.blue(), 1.0f); - geometry->setColorArray(colours, osg::Array::BIND_PER_VERTEX); geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); From 32601e0ae48da869347b9509d4d5e05049c0f6c3 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Fri, 11 Dec 2020 08:56:28 +0300 Subject: [PATCH 13/51] Properly reserve body parts for skirts (bug #5731) --- CHANGELOG.md | 1 + apps/opencs/model/world/actoradapter.cpp | 47 ++++++------------------ 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 219234a38..d399bcb30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ Bug #5688: Water shader broken indoors with enable indoor shadows = false Bug #5695: ExplodeSpell for actors doesn't target the ground Bug #5703: OpenMW-CS menu system crashing on XFCE + Bug #5731: Editor: skirts are invisible on characters Feature #390: 3rd person look "over the shoulder" Feature #2386: Distant Statics in the form of Object Paging Feature #2404: Levelled List can not be placed into a container diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 5ed80a1e4..8558aa9bc 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -593,56 +593,33 @@ namespace CSMWorld } else if (type == UniversalId::Type_Clothing) { - int priority = 0; - // TODO: reserve bodyparts for robes and skirts auto& clothing = dynamic_cast&>(record).get(); + std::vector parts; if (clothing.mData.mType == ESM::Clothing::Robe) { - auto reservedList = std::vector(); - - ESM::PartReference pr; - pr.mMale = ""; - pr.mFemale = ""; - - ESM::PartReferenceType parts[] = { + parts = { ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, - ESM::PRT_RForearm, ESM::PRT_LForearm + ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_Cuirass }; - size_t parts_size = sizeof(parts)/sizeof(parts[0]); - for(size_t p = 0;p < parts_size;++p) - { - pr.mPart = parts[p]; - reservedList.push_back(pr); - } - - priority = parts_size; - addParts(reservedList, priority); } else if (clothing.mData.mType == ESM::Clothing::Skirt) { - auto reservedList = std::vector(); + parts = {ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg}; + } + std::vector reservedList; + for (const auto& p : parts) + { ESM::PartReference pr; - pr.mMale = ""; - pr.mFemale = ""; - - ESM::PartReferenceType parts[] = { - ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg - }; - size_t parts_size = sizeof(parts)/sizeof(parts[0]); - for(size_t p = 0;p < parts_size;++p) - { - pr.mPart = parts[p]; - reservedList.push_back(pr); - } - - priority = parts_size; - addParts(reservedList, priority); + pr.mPart = p; + reservedList.emplace_back(pr); } + int priority = parts.size(); addParts(clothing.mParts.mParts, priority); + addParts(reservedList, priority); // Changing parts could affect what is picked for rendering data->addOtherDependency(itemId); From 899b8422faccc8d77ab88e5d2a38035e25c97d3f Mon Sep 17 00:00:00 2001 From: Frederic Chardon Date: Thu, 10 Dec 2020 22:00:33 +0100 Subject: [PATCH 14/51] explicitely use a reference, auto can't infer it and make a copy --- apps/openmw/mwworld/worldimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d00fabb66..c24597b84 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1496,7 +1496,7 @@ namespace MWWorld mProjectileManager->update(duration); - const auto results = mPhysics->applyQueuedMovement(duration, mDiscardMovements, frameStart, frameNumber, stats); + const auto& results = mPhysics->applyQueuedMovement(duration, mDiscardMovements, frameStart, frameNumber, stats); mProjectileManager->processHits(); mDiscardMovements = false; From 460e5abb55d6233814c043de7f6475368573ccd1 Mon Sep 17 00:00:00 2001 From: Frederic Chardon Date: Thu, 10 Dec 2020 21:54:47 +0100 Subject: [PATCH 15/51] Update physics object position after spawning. --- apps/openmw/mwworld/scene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 47b62862f..631253bd4 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -253,7 +253,7 @@ namespace bool operator() (const MWWorld::Ptr& ptr) { if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) - ptr.getClass().adjustPosition (ptr, false); + ptr.getClass().adjustPosition (ptr, true); return true; } }; From 15291f15d3e5ce7e5eb777e235a308c3eb34cc02 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Mon, 16 Nov 2020 19:37:30 +0300 Subject: [PATCH 16/51] Make actor collision box components a struct --- apps/openmw/mwphysics/actor.cpp | 2 +- apps/openmw/mwphysics/physicssystem.cpp | 2 +- .../nifloader/testbulletnifloader.cpp | 35 ++++++++++--------- components/nifbullet/bulletnifloader.cpp | 12 +++---- components/resource/bulletshape.cpp | 6 ++-- components/resource/bulletshape.hpp | 8 +++-- 6 files changed, 34 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 77b07cde5..b1f219e33 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -20,7 +20,7 @@ namespace MWPhysics Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, PhysicsTaskScheduler* scheduler) : mStandingOnPtr(nullptr), mCanWaterWalk(false), mWalkingOnWater(false) - , mCollisionObject(nullptr), mMeshTranslation(shape->mCollisionBoxTranslate), mHalfExtents(shape->mCollisionBoxHalfExtents) + , mCollisionObject(nullptr), mMeshTranslation(shape->mCollisionBox.center), mHalfExtents(shape->mCollisionBox.extents) , mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false) , mInternalCollisionMode(true) , mExternalCollisionMode(true) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 324b1db2d..8106a78ea 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -645,7 +645,7 @@ namespace MWPhysics osg::ref_ptr shape = mShapeManager->getShape(mesh); // Try to get shape from basic model as fallback for creatures - if (!ptr.getClass().isNpc() && shape && shape->mCollisionBoxHalfExtents.length2() == 0) + if (!ptr.getClass().isNpc() && shape && shape->mCollisionBox.extents.length2() == 0) { const std::string fallbackModel = ptr.getClass().getModel(ptr); if (fallbackModel != mesh) diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 7da8f0fd5..3a854bf5c 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -162,8 +162,8 @@ namespace Resource { return compareObjects(lhs.mCollisionShape, rhs.mCollisionShape) && compareObjects(lhs.mAvoidCollisionShape, rhs.mAvoidCollisionShape) - && lhs.mCollisionBoxHalfExtents == rhs.mCollisionBoxHalfExtents - && lhs.mCollisionBoxTranslate == rhs.mCollisionBoxTranslate + && lhs.mCollisionBox.extents == rhs.mCollisionBox.extents + && lhs.mCollisionBox.center == rhs.mCollisionBox.center && lhs.mAnimatedShapes == rhs.mAnimatedShapes; } @@ -172,7 +172,8 @@ namespace Resource return stream << "Resource::BulletShape {" << value.mCollisionShape << ", " << value.mAvoidCollisionShape << ", " - << "osg::Vec3f {" << value.mCollisionBoxHalfExtents << "}" << ", " + << "osg::Vec3f {" << value.mCollisionBox.extents << "}" << ", " + << "osg::Vec3f {" << value.mCollisionBox.center << "}" << ", " << value.mAnimatedShapes << "}"; } @@ -433,8 +434,8 @@ namespace const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; - expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); - expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3); + expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3); std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); std::unique_ptr shape(new btCompoundShape); shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); @@ -458,8 +459,8 @@ namespace const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; - expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); - expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3); + expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3); std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); std::unique_ptr shape(new btCompoundShape); shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); @@ -488,8 +489,8 @@ namespace const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; - expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); - expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3); + expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3); std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); std::unique_ptr shape(new btCompoundShape); shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); @@ -523,8 +524,8 @@ namespace const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; - expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); - expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3); + expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3); std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); std::unique_ptr shape(new btCompoundShape); shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); @@ -558,8 +559,8 @@ namespace const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; - expected.mCollisionBoxHalfExtents = osg::Vec3f(4, 5, 6); - expected.mCollisionBoxTranslate = osg::Vec3f(-4, -5, -6); + expected.mCollisionBox.extents = osg::Vec3f(4, 5, 6); + expected.mCollisionBox.center = osg::Vec3f(-4, -5, -6); std::unique_ptr box(new btBoxShape(btVector3(4, 5, 6))); std::unique_ptr shape(new btCompoundShape); shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-4, -5, -6)), box.release()); @@ -581,8 +582,8 @@ namespace const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; - expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); - expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3); + expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3); EXPECT_EQ(*result, expected); } @@ -615,8 +616,8 @@ namespace const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; - expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); - expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3); + expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3); EXPECT_EQ(*result, expected); } diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index db9d5ae43..b24d08fd8 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -145,12 +145,12 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) { if (findBoundingBox(node, filename)) { - const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents); - const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate); + const btVector3 extents = Misc::Convert::toBullet(mShape->mCollisionBox.extents); + const btVector3 center = Misc::Convert::toBullet(mShape->mCollisionBox.center); std::unique_ptr compound (new btCompoundShape); - std::unique_ptr boxShape(new btBoxShape(halfExtents)); + std::unique_ptr boxShape(new btBoxShape(extents)); btTransform transform = btTransform::getIdentity(); - transform.setOrigin(origin); + transform.setOrigin(center); compound->addChildShape(transform, boxShape.get()); boxShape.release(); @@ -208,8 +208,8 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node, const std::string& switch (type) { case Nif::NiBoundingVolume::Type::BOX_BV: - mShape->mCollisionBoxHalfExtents = node->bounds.box.extents; - mShape->mCollisionBoxTranslate = node->bounds.box.center; + mShape->mCollisionBox.extents = node->bounds.box.extents; + mShape->mCollisionBox.center = node->bounds.box.center; break; default: { diff --git a/components/resource/bulletshape.cpp b/components/resource/bulletshape.cpp index 7dd0964e8..fc68c5545 100644 --- a/components/resource/bulletshape.cpp +++ b/components/resource/bulletshape.cpp @@ -20,8 +20,7 @@ BulletShape::BulletShape() BulletShape::BulletShape(const BulletShape ©, const osg::CopyOp ©op) : mCollisionShape(duplicateCollisionShape(copy.mCollisionShape)) , mAvoidCollisionShape(duplicateCollisionShape(copy.mAvoidCollisionShape)) - , mCollisionBoxHalfExtents(copy.mCollisionBoxHalfExtents) - , mCollisionBoxTranslate(copy.mCollisionBoxTranslate) + , mCollisionBox(copy.mCollisionBox) , mAnimatedShapes(copy.mAnimatedShapes) { } @@ -106,8 +105,7 @@ BulletShapeInstance::BulletShapeInstance(osg::ref_ptr source) : BulletShape() , mSource(source) { - mCollisionBoxHalfExtents = source->mCollisionBoxHalfExtents; - mCollisionBoxTranslate = source->mCollisionBoxTranslate; + mCollisionBox = source->mCollisionBox; mAnimatedShapes = source->mAnimatedShapes; diff --git a/components/resource/bulletshape.hpp b/components/resource/bulletshape.hpp index e77c96327..7d9577ba0 100644 --- a/components/resource/bulletshape.hpp +++ b/components/resource/bulletshape.hpp @@ -27,10 +27,14 @@ namespace Resource btCollisionShape* mCollisionShape; btCollisionShape* mAvoidCollisionShape; + struct CollisionBox + { + osg::Vec3f extents; + osg::Vec3f center; + }; // Used for actors. mCollisionShape is used for actors only when we need to autogenerate collision box for creatures. // For now, use one file <-> one resource for simplicity. - osg::Vec3f mCollisionBoxHalfExtents; - osg::Vec3f mCollisionBoxTranslate; + CollisionBox mCollisionBox; // Stores animated collision shapes. If any collision nodes in the NIF are animated, then mCollisionShape // will be a btCompoundShape (which consists of one or more child shapes). From a314f196eb792e3f1bcb90f8ec40a287c9192d19 Mon Sep 17 00:00:00 2001 From: fredzio Date: Sat, 12 Dec 2020 18:02:46 +0100 Subject: [PATCH 17/51] Unconditionally call actor->resetPosition in adjustPosition. Revert broken change that would force adjust all actors in cell upon loading. That break floating corpses (and probably others things). --- apps/openmw/mwworld/scene.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 631253bd4..47b62862f 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -253,7 +253,7 @@ namespace bool operator() (const MWWorld::Ptr& ptr) { if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) - ptr.getClass().adjustPosition (ptr, true); + ptr.getClass().adjustPosition (ptr, false); return true; } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c24597b84..f59c92c6a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1344,12 +1344,8 @@ namespace MWWorld } moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z()); - if (force) // force physics to use the new position - { - auto actor = mPhysics->getActor(ptr); - if(actor) - actor->resetPosition(); - } + if (ptr.getClass().isActor()) + mPhysics->getActor(ptr)->resetPosition(); } void World::fixPosition() From 28c79227df36a3b27cce106837650842dbcec034 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 12 Dec 2020 21:04:59 +0200 Subject: [PATCH 18/51] Editor changelog additions --- CHANGELOG.md | 2 ++ CHANGELOG_PR.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ac8168eb..d896ca55c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.47.0 ------ + Bug #832: OpenMW-CS: Handle deleted references 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 #2069: Fireflies in Fireflies invade Morrowind look wrong @@ -67,6 +68,7 @@ Bug #5644: Summon effects running on the player during game initialization cause crashes Bug #5656: Sneaking characters block hits while standing Bug #5661: Region sounds don't play at the right interval + Bug #5675: OpenMW-cs. FRMR subrecords are saved with the wrong MastIdx Bug #5688: Water shader broken indoors with enable indoor shadows = false Bug #5695: ExplodeSpell for actors doesn't target the ground Bug #5703: OpenMW-CS menu system crashing on XFCE diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index 6a16b7658..8a7948bb7 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -34,7 +34,9 @@ Bug Fixes: - Morrowind legacy madness: Using a key on a trapped door/container now only disarms the trap if the door/container is locked (#5370) Editor Bug Fixes: +- Deleted and moved objects within a cell are now saved properly (#832) - Verifier no longer checks for alleged 'race' entries in clothing body parts (#5400) +- Loading mods now keeps the master index (#5675) - Flicker and crashing on XFCE4 fixed (#5703) Miscellaneous: From 73afc55462ba1fc11ebebf83cbeb9cfcb2738bd1 Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Sat, 12 Dec 2020 17:29:29 +0100 Subject: [PATCH 19/51] Fork the current process to monitor exe, generate minidump on crash --- components/CMakeLists.txt | 8 +- .../crashcatcher/windows_crashcatcher.cpp | 200 ++++++++++++++++++ .../crashcatcher/windows_crashcatcher.hpp | 79 +++++++ .../crashcatcher/windows_crashmonitor.cpp | 185 ++++++++++++++++ .../crashcatcher/windows_crashmonitor.hpp | 49 +++++ components/crashcatcher/windows_crashshm.hpp | 44 ++++ components/debug/debugging.cpp | 9 +- 7 files changed, 571 insertions(+), 3 deletions(-) create mode 100644 components/crashcatcher/windows_crashcatcher.cpp create mode 100644 components/crashcatcher/windows_crashcatcher.hpp create mode 100644 components/crashcatcher/windows_crashmonitor.cpp create mode 100644 components/crashcatcher/windows_crashmonitor.hpp create mode 100644 components/crashcatcher/windows_crashshm.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 3c1899037..10a5d22fb 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -151,7 +151,13 @@ add_component_dir (fallback fallback validate ) -if(NOT WIN32 AND NOT ANDROID) +if(WIN32) + add_component_dir (crashcatcher + windows_crashcatcher + windows_crashmonitor + windows_crashshm + ) +elseif(NOT ANDROID) add_component_dir (crashcatcher crashcatcher ) diff --git a/components/crashcatcher/windows_crashcatcher.cpp b/components/crashcatcher/windows_crashcatcher.cpp new file mode 100644 index 000000000..3291631c1 --- /dev/null +++ b/components/crashcatcher/windows_crashcatcher.cpp @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include + +#include "windows_crashcatcher.hpp" +#include "windows_crashmonitor.hpp" +#include "windows_crashshm.hpp" +#include + +namespace Crash +{ + + HANDLE duplicateHandle(HANDLE handle) + { + HANDLE duplicate; + if (!DuplicateHandle(GetCurrentProcess(), handle, + GetCurrentProcess(), &duplicate, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + throw std::runtime_error("Crash monitor could not duplicate handle"); + } + return duplicate; + } + + CrashCatcher* CrashCatcher::sInstance = nullptr; + + CrashCatcher::CrashCatcher(int argc, char **argv, const std::string& crashLogPath) + { + assert(sInstance == nullptr); // don't allow two instances + + sInstance = this; + + HANDLE shmHandle = nullptr; + for (int i=0; i= argc - 1) + throw std::runtime_error("Crash monitor is missing the SHM handle argument"); + + sscanf(argv[i + 1], "%p", &shmHandle); + break; + } + + if (!shmHandle) + { + setupIpc(); + startMonitorProcess(crashLogPath); + installHandler(); + } + else + { + CrashMonitor(shmHandle).run(); + exit(0); + } + } + + CrashCatcher::~CrashCatcher() + { + sInstance = nullptr; + + if (mShm && mSignalMonitorEvent) + { + shmLock(); + mShm->mEvent = CrashSHM::Event::Shutdown; + shmUnlock(); + + SetEvent(mSignalMonitorEvent); + } + + if (mShmHandle) + CloseHandle(mShmHandle); + } + + void CrashCatcher::setupIpc() + { + SECURITY_ATTRIBUTES attributes; + ZeroMemory(&attributes, sizeof(attributes)); + attributes.bInheritHandle = TRUE; + + mSignalAppEvent = CreateEventW(&attributes, FALSE, FALSE, NULL); + mSignalMonitorEvent = CreateEventW(&attributes, FALSE, FALSE, NULL); + + mShmHandle = CreateFileMappingW(INVALID_HANDLE_VALUE, &attributes, PAGE_READWRITE, HIWORD(sizeof(CrashSHM)), LOWORD(sizeof(CrashSHM)), NULL); + if (mShmHandle == nullptr) + throw std::runtime_error("Failed to allocate crash catcher shared memory"); + + mShm = reinterpret_cast(MapViewOfFile(mShmHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashSHM))); + if (mShm == nullptr) + throw std::runtime_error("Failed to map crash catcher shared memory"); + + mShmMutex = CreateMutexW(&attributes, FALSE, NULL); + if (mShmMutex == nullptr) + throw std::runtime_error("Failed to create crash catcher shared memory mutex"); + } + + void CrashCatcher::shmLock() + { + if (WaitForSingleObject(mShmMutex, CrashCatcherTimeout) != WAIT_OBJECT_0) + throw std::runtime_error("SHM lock timed out"); + } + + void CrashCatcher::shmUnlock() + { + ReleaseMutex(mShmMutex); + } + + void CrashCatcher::waitMonitor() + { + if (WaitForSingleObject(mSignalAppEvent, CrashCatcherTimeout) != WAIT_OBJECT_0) + throw std::runtime_error("Waiting for monitor failed"); + } + + void CrashCatcher::signalMonitor() + { + SetEvent(mSignalMonitorEvent); + } + + void CrashCatcher::installHandler() + { + SetUnhandledExceptionFilter(vectoredExceptionHandler); + } + + void CrashCatcher::startMonitorProcess(const std::string& crashLogPath) + { + WCHAR executablePath[MAX_PATH + 1]; + GetModuleFileNameW(NULL, executablePath, MAX_PATH + 1); + + memset(mShm->mStartup.mLogFilePath, 0, sizeof(mShm->mStartup.mLogFilePath)); + int length = crashLogPath.length(); + if (length > MAX_PATH) length = MAX_PATH; + strncpy(mShm->mStartup.mLogFilePath, crashLogPath.c_str(), length); + mShm->mStartup.mLogFilePath[length] = '\0'; + + // note that we don't need to lock the SHM here, the other process has not started yet + mShm->mEvent = CrashSHM::Event::Startup; + mShm->mStartup.mShmMutex = duplicateHandle(mShmMutex); + mShm->mStartup.mAppProcessHandle = duplicateHandle(GetCurrentProcess()); + mShm->mStartup.mSignalApp = duplicateHandle(mSignalAppEvent); + mShm->mStartup.mSignalMonitor = duplicateHandle(mSignalMonitorEvent); + + std::wstringstream ss; + ss << "--crash-monitor " << std::hex << duplicateHandle(mShmHandle); + std::wstring argumetns(ss.str()); + + STARTUPINFOW si; + ZeroMemory(&si, sizeof(si)); + + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + + if (!CreateProcessW(executablePath, &argumetns[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) + throw std::runtime_error("Could not start crash monitor process"); + + waitMonitor(); + } + + LONG CrashCatcher::vectoredExceptionHandler(PEXCEPTION_POINTERS info) + { + switch (info->ExceptionRecord->ExceptionCode) + { + case EXCEPTION_SINGLE_STEP: + case EXCEPTION_BREAKPOINT: + case DBG_PRINTEXCEPTION_C: + return EXCEPTION_EXECUTE_HANDLER; + } + if (!sInstance) + return EXCEPTION_EXECUTE_HANDLER; + + sInstance->handleVectoredException(info); + + _Exit(1); + + return EXCEPTION_CONTINUE_SEARCH; + } + + void CrashCatcher::handleVectoredException(PEXCEPTION_POINTERS info) + { + shmLock(); + + mShm->mEvent = CrashSHM::Event::Crashed; + mShm->mCrashed.mThreadId = GetCurrentThreadId(); + mShm->mCrashed.mContext = *info->ContextRecord; + mShm->mCrashed.mExceptionRecord = *info->ExceptionRecord; + + shmUnlock(); + + signalMonitor(); + + // must remain until monitor has finished + waitMonitor(); + + std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(mShm->mStartup.mLogFilePath) + "'.\n Please report this to https://gitlab.com/OpenMW/openmw/issues !"; + SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr); + } + +} // namespace Crash diff --git a/components/crashcatcher/windows_crashcatcher.hpp b/components/crashcatcher/windows_crashcatcher.hpp new file mode 100644 index 000000000..e1857271e --- /dev/null +++ b/components/crashcatcher/windows_crashcatcher.hpp @@ -0,0 +1,79 @@ +#ifndef WINDOWS_CRASHCATCHER_HPP +#define WINDOWS_CRASHCATCHER_HPP + +#include + +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#include + +#include + +namespace Crash +{ + + // The implementation spawns the current executable as a monitor process which waits + // for a global synchronization event which is sent when the parent process crashes. + // The monitor process then extracts crash information from the parent process while + // the parent process waits for the monitor process to finish. The crashed process + // quits and the monitor writes the crash information to a file. + // + // To detect unexpected shutdowns of the application which are not handled by the + // crash handler, the monitor periodically checks the exit code of the parent + // process and exits if it does not return STILL_ACTIVE. You can test this by closing + // the main openmw process in task manager. + + static constexpr const int CrashCatcherTimeout = 2500; + + struct CrashSHM; + + class CrashCatcher final + { + public: + + CrashCatcher(int argc, char **argv, const std::string& crashLogPath); + ~CrashCatcher(); + + private: + + static CrashCatcher* sInstance; + + // mapped SHM area + CrashSHM* mShm = nullptr; + // the handle is allocated by the catcher and passed to the monitor + // process via the command line which maps the SHM and sends / receives + // events through it + HANDLE mShmHandle = nullptr; + // mutex which guards SHM area + HANDLE mShmMutex = nullptr; + + // triggered when the monitor signals the application + HANDLE mSignalAppEvent = INVALID_HANDLE_VALUE; + + // triggered when the application wants to wake the monitor process + HANDLE mSignalMonitorEvent = INVALID_HANDLE_VALUE; + + void setupIpc(); + + void shmLock(); + + void shmUnlock(); + + void startMonitorProcess(const std::string& crashLogPath); + + void waitMonitor(); + + void signalMonitor(); + + void installHandler(); + + void handleVectoredException(PEXCEPTION_POINTERS info); + + public: + + static LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS info); + }; + +} // namespace Crash + +#endif // WINDOWS_CRASHCATCHER_HPP diff --git a/components/crashcatcher/windows_crashmonitor.cpp b/components/crashcatcher/windows_crashmonitor.cpp new file mode 100644 index 000000000..56f8bbdd4 --- /dev/null +++ b/components/crashcatcher/windows_crashmonitor.cpp @@ -0,0 +1,185 @@ +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include + +#include +#include +#include + +#include "windows_crashcatcher.hpp" +#include "windows_crashmonitor.hpp" +#include "windows_crashshm.hpp" +#include + +namespace Crash +{ + + CrashMonitor::CrashMonitor(HANDLE shmHandle) + : mShmHandle(shmHandle) + { + mShm = reinterpret_cast(MapViewOfFile(mShmHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashSHM))); + if (mShm == nullptr) + throw std::runtime_error("Failed to map crash monitor shared memory"); + + // accessing SHM without lock is OK here, the parent waits for a signal before continuing + + mShmMutex = mShm->mStartup.mShmMutex; + mAppProcessHandle = mShm->mStartup.mAppProcessHandle; + mSignalAppEvent = mShm->mStartup.mSignalApp; + mSignalMonitorEvent = mShm->mStartup.mSignalMonitor; + } + + CrashMonitor::~CrashMonitor() + { + if (mShm) + UnmapViewOfFile(mShm); + + // the handles received from the app are duplicates, we must close them + + if (mShmHandle) + CloseHandle(mShmHandle); + + if (mShmMutex) + CloseHandle(mShmMutex); + + if (mSignalAppEvent) + CloseHandle(mSignalAppEvent); + + if (mSignalMonitorEvent) + CloseHandle(mSignalMonitorEvent); + } + + void CrashMonitor::shmLock() + { + if (WaitForSingleObject(mShmMutex, CrashCatcherTimeout) != WAIT_OBJECT_0) + throw std::runtime_error("SHM monitor lock timed out"); + } + + void CrashMonitor::shmUnlock() + { + ReleaseMutex(mShmMutex); + } + + void CrashMonitor::signalApp() const + { + SetEvent(mSignalAppEvent); + } + + bool CrashMonitor::waitApp() const + { + return WaitForSingleObject(mSignalMonitorEvent, CrashCatcherTimeout) == WAIT_OBJECT_0; + } + + bool CrashMonitor::isAppAlive() const + { + DWORD code = 0; + GetExitCodeProcess(mAppProcessHandle, &code); + return code == STILL_ACTIVE; + } + + void CrashMonitor::run() + { + try + { + // app waits for monitor start up, let it continue + signalApp(); + + bool running = true; + while (isAppAlive() && running) + { + if (waitApp()) + { + shmLock(); + + switch (mShm->mEvent) + { + case CrashSHM::Event::None: + break; + case CrashSHM::Event::Crashed: + handleCrash(); + running = false; + break; + case CrashSHM::Event::Shutdown: + running = false; + break; + case CrashSHM::Event::Startup: + break; + } + + shmUnlock(); + } + } + + } + catch (...) + { + Log(Debug::Error) << "Exception in crash monitor, exiting"; + } + signalApp(); + } + + std::wstring utf8ToUtf16(const std::string& utf8) + { + const int nLenWide = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size(), nullptr, 0); + + std::wstring utf16; + utf16.resize(nLenWide); + if (MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size(), utf16.data(), nLenWide) != nLenWide) + return {}; + + return utf16; + } + + void CrashMonitor::handleCrash() + { + DWORD processId = GetProcessId(mAppProcessHandle); + + try + { + HMODULE dbghelp = LoadLibraryA("dbghelp.dll"); + if (dbghelp == NULL) + return; + + using MiniDumpWirteDumpFn = BOOL (WINAPI*)( + HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam + ); + + MiniDumpWirteDumpFn miniDumpWriteDump = (MiniDumpWirteDumpFn)GetProcAddress(dbghelp, "MiniDumpWriteDump"); + if (miniDumpWriteDump == NULL) + return; + + const std::wstring utf16Path = utf8ToUtf16(mShm->mStartup.mLogFilePath); + if (utf16Path.empty()) + return; + + HANDLE hCrashLog = CreateFileW(utf16Path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hCrashLog == NULL || hCrashLog == INVALID_HANDLE_VALUE) + return; + if (auto err = GetLastError(); err != ERROR_ALREADY_EXISTS && err != 0) + return; + + EXCEPTION_POINTERS exp; + exp.ContextRecord = &mShm->mCrashed.mContext; + exp.ExceptionRecord = &mShm->mCrashed.mExceptionRecord; + MINIDUMP_EXCEPTION_INFORMATION infos = {}; + infos.ThreadId = mShm->mCrashed.mThreadId; + infos.ExceptionPointers = &exp; + infos.ClientPointers = FALSE; + MINIDUMP_TYPE type = (MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithHandleData); + miniDumpWriteDump(mAppProcessHandle, processId, hCrashLog, type, &infos, 0, 0); + } + catch (const std::exception&e) + { + Log(Debug::Error) << "CrashMonitor: " << e.what(); + } + catch (...) + { + Log(Debug::Error) << "CrashMonitor: unknown exception"; + } + } + +} // namespace Crash diff --git a/components/crashcatcher/windows_crashmonitor.hpp b/components/crashcatcher/windows_crashmonitor.hpp new file mode 100644 index 000000000..678d38435 --- /dev/null +++ b/components/crashcatcher/windows_crashmonitor.hpp @@ -0,0 +1,49 @@ +#ifndef WINDOWS_CRASHMONITOR_HPP +#define WINDOWS_CRASHMONITOR_HPP + +#include + +namespace Crash +{ + +struct CrashSHM; + +class CrashMonitor final +{ +public: + + CrashMonitor(HANDLE shmHandle); + + ~CrashMonitor(); + + void run(); + +private: + + HANDLE mAppProcessHandle = nullptr; + + // triggered when the monitor process wants to wake the parent process (received via SHM) + HANDLE mSignalAppEvent = nullptr; + // triggered when the application wants to wake the monitor process (received via SHM) + HANDLE mSignalMonitorEvent = nullptr; + + CrashSHM* mShm = nullptr; + HANDLE mShmHandle = nullptr; + HANDLE mShmMutex = nullptr; + + void signalApp() const; + + bool waitApp() const; + + bool isAppAlive() const; + + void shmLock(); + + void shmUnlock(); + + void handleCrash(); +}; + +} // namespace Crash + +#endif // WINDOWS_CRASHMONITOR_HPP diff --git a/components/crashcatcher/windows_crashshm.hpp b/components/crashcatcher/windows_crashshm.hpp new file mode 100644 index 000000000..80e47f07b --- /dev/null +++ b/components/crashcatcher/windows_crashshm.hpp @@ -0,0 +1,44 @@ +#ifndef WINDOWS_CRASHSHM_HPP +#define WINDOWS_CRASHSHM_HPP + +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#include + +namespace Crash +{ + + // Used to communicate between the app and the monitor, fields are is overwritten with each event. + + struct CrashSHM + { + enum class Event + { + None, + Startup, + Crashed, + Shutdown + }; + + Event mEvent; + + struct Startup + { + HANDLE mAppProcessHandle; + HANDLE mSignalApp; + HANDLE mSignalMonitor; + HANDLE mShmMutex; + char mLogFilePath[MAX_PATH + 1]; + } mStartup; + + struct Crashed + { + DWORD mThreadId; + CONTEXT mContext; + EXCEPTION_RECORD mExceptionRecord; + } mCrashed; + }; + +} // namespace Crash + +#endif // WINDOWS_CRASHSHM_HPP diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index 987a3db7e..657c04d0f 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -2,10 +2,12 @@ #include #include +#include #include #ifdef _WIN32 +# include # undef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # include @@ -187,13 +189,16 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c std::cerr.rdbuf (&cerrsb); #endif +#if defined(_WIN32) + Crash::CrashCatcher crashy(argc, argv, (cfgMgr.getLogPath() / crashLogName).make_preferred().string()); +#else // install the crash handler as soon as possible. note that the log path // does not depend on config being read. crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / crashLogName).string()); - +#endif ret = innerApplication(argc, argv); } - catch (std::exception& e) + catch (const std::exception& e) { #if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) if (!isatty(fileno(stdin))) From 256aa5e71d59b817753dbe440ac3ee06e186dc7a Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 12 Dec 2020 22:23:20 +0200 Subject: [PATCH 20/51] Use const auto& --- components/resource/animation.cpp | 4 ++-- components/resource/keyframemanager.cpp | 2 +- components/sceneutil/osgacontroller.cpp | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/components/resource/animation.cpp b/components/resource/animation.cpp index 34ae162ee..d2d7d08d5 100644 --- a/components/resource/animation.cpp +++ b/components/resource/animation.cpp @@ -10,7 +10,7 @@ namespace Resource mStartTime(0.0f) { const osgAnimation::ChannelList& channels = anim.getChannels(); - for (const osg::ref_ptr channel: channels) + for (const auto& channel: channels) addChannel(channel.get()->clone()); } @@ -31,7 +31,7 @@ namespace Resource bool Animation::update (double time) { - for (const osg::ref_ptr channel: mChannels) + for (const auto& channel: mChannels) { channel->update(time, 1.0f, 0); } diff --git a/components/resource/keyframemanager.cpp b/components/resource/keyframemanager.cpp index af0f365ee..41b10bd65 100644 --- a/components/resource/keyframemanager.cpp +++ b/components/resource/keyframemanager.cpp @@ -44,7 +44,7 @@ namespace Resource std::string loopstop = animationName + std::string(": loop stop"); const osgAnimation::ChannelList& channels = animation->getChannels(); - for (const osg::ref_ptr channel: channels) + for (const auto& channel: channels) { mergedAnimationTrack->addChannel(channel.get()->clone()); // is ->clone needed? } diff --git a/components/sceneutil/osgacontroller.cpp b/components/sceneutil/osgacontroller.cpp index 87e6f02fe..d77b8d666 100644 --- a/components/sceneutil/osgacontroller.cpp +++ b/components/sceneutil/osgacontroller.cpp @@ -31,7 +31,7 @@ namespace SceneUtil void LinkVisitor::link(osgAnimation::UpdateMatrixTransform* umt) { const osgAnimation::ChannelList& channels = mAnimation->getChannels(); - for (const osg::ref_ptr channel: channels) + for (const auto& channel: channels) { const std::string& channelName = channel->getName(); const std::string& channelTargetName = channel->getTargetName(); @@ -125,13 +125,13 @@ namespace SceneUtil } //Find the root transform track in animation - for (const osg::ref_ptr mergedAnimationTrack : mMergedAnimationTracks) + for (const auto& mergedAnimationTrack : mMergedAnimationTracks) { if (mergedAnimationTrack->getName() != animationName) continue; const osgAnimation::ChannelList& channels = mergedAnimationTrack->getChannels(); - for (const osg::ref_ptr channel: channels) + for (const auto& channel: channels) { if (channel->getTargetName() != "root" || channel->getName() != "transform") continue; @@ -150,7 +150,7 @@ namespace SceneUtil void OsgAnimationController::update(float time, std::string animationName) { - for (const osg::ref_ptr mergedAnimationTrack : mMergedAnimationTracks) + for (const auto& mergedAnimationTrack : mMergedAnimationTracks) { if (mergedAnimationTrack->getName() == animationName) mergedAnimationTrack->update(time); } @@ -162,7 +162,7 @@ namespace SceneUtil { if (mNeedToLink) { - for (const osg::ref_ptr mergedAnimationTrack : mMergedAnimationTracks) + for (const auto& mergedAnimationTrack : mMergedAnimationTracks) { if (!mLinker.valid()) mLinker = new LinkVisitor(); mLinker->setAnimation(mergedAnimationTrack); From e2041de9690f4257c2b2b01a4cf9704a6da23068 Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Sat, 12 Dec 2020 21:47:50 +0100 Subject: [PATCH 21/51] Use the incremental approach to handle long path --- components/crashcatcher/windows_crashcatcher.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/components/crashcatcher/windows_crashcatcher.cpp b/components/crashcatcher/windows_crashcatcher.cpp index 3291631c1..129907fc3 100644 --- a/components/crashcatcher/windows_crashcatcher.cpp +++ b/components/crashcatcher/windows_crashcatcher.cpp @@ -126,8 +126,13 @@ namespace Crash void CrashCatcher::startMonitorProcess(const std::string& crashLogPath) { - WCHAR executablePath[MAX_PATH + 1]; - GetModuleFileNameW(NULL, executablePath, MAX_PATH + 1); + std::wstring executablePath; + DWORD copied = 0; + do { + executablePath.resize(executablePath.size() + MAX_PATH); + copied = GetModuleFileNameW(nullptr, &executablePath[0], executablePath.size()); + } while (copied >= executablePath.size()); + executablePath.resize(copied); memset(mShm->mStartup.mLogFilePath, 0, sizeof(mShm->mStartup.mLogFilePath)); int length = crashLogPath.length(); @@ -152,7 +157,7 @@ namespace Crash PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi)); - if (!CreateProcessW(executablePath, &argumetns[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) + if (!CreateProcessW(&executablePath[0], &argumetns[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) throw std::runtime_error("Could not start crash monitor process"); waitMonitor(); From 3eb2b321239b294c4922e38c0af88c344ab136a7 Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Sun, 13 Dec 2020 14:09:14 +0100 Subject: [PATCH 22/51] Fix typpo issue on arguments --- components/crashcatcher/windows_crashcatcher.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/crashcatcher/windows_crashcatcher.cpp b/components/crashcatcher/windows_crashcatcher.cpp index 129907fc3..8495e84f8 100644 --- a/components/crashcatcher/windows_crashcatcher.cpp +++ b/components/crashcatcher/windows_crashcatcher.cpp @@ -149,7 +149,7 @@ namespace Crash std::wstringstream ss; ss << "--crash-monitor " << std::hex << duplicateHandle(mShmHandle); - std::wstring argumetns(ss.str()); + std::wstring arguments(ss.str()); STARTUPINFOW si; ZeroMemory(&si, sizeof(si)); @@ -157,7 +157,7 @@ namespace Crash PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi)); - if (!CreateProcessW(&executablePath[0], &argumetns[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) + if (!CreateProcessW(&executablePath[0], &arguments[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) throw std::runtime_error("Could not start crash monitor process"); waitMonitor(); From f400116bcd139c44cb8622670a0403f1f3222580 Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Sun, 13 Dec 2020 14:09:44 +0100 Subject: [PATCH 23/51] Use 32767 characters for log path --- components/crashcatcher/windows_crashcatcher.cpp | 2 +- components/crashcatcher/windows_crashshm.hpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/components/crashcatcher/windows_crashcatcher.cpp b/components/crashcatcher/windows_crashcatcher.cpp index 8495e84f8..dc10f4dcb 100644 --- a/components/crashcatcher/windows_crashcatcher.cpp +++ b/components/crashcatcher/windows_crashcatcher.cpp @@ -136,7 +136,7 @@ namespace Crash memset(mShm->mStartup.mLogFilePath, 0, sizeof(mShm->mStartup.mLogFilePath)); int length = crashLogPath.length(); - if (length > MAX_PATH) length = MAX_PATH; + if (length > MAX_LONG_PATH) length = MAX_LONG_PATH; strncpy(mShm->mStartup.mLogFilePath, crashLogPath.c_str(), length); mShm->mStartup.mLogFilePath[length] = '\0'; diff --git a/components/crashcatcher/windows_crashshm.hpp b/components/crashcatcher/windows_crashshm.hpp index 80e47f07b..47929a45f 100644 --- a/components/crashcatcher/windows_crashshm.hpp +++ b/components/crashcatcher/windows_crashshm.hpp @@ -9,6 +9,7 @@ namespace Crash { // Used to communicate between the app and the monitor, fields are is overwritten with each event. + static constexpr const int MAX_LONG_PATH = 0x7fff; struct CrashSHM { @@ -28,7 +29,7 @@ namespace Crash HANDLE mSignalApp; HANDLE mSignalMonitor; HANDLE mShmMutex; - char mLogFilePath[MAX_PATH + 1]; + char mLogFilePath[MAX_LONG_PATH]; } mStartup; struct Crashed From adeb4fe02f92637395c4b1742f2d60405105c75c Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Sun, 13 Dec 2020 14:10:44 +0100 Subject: [PATCH 24/51] Handle case where the log path has more that MAX_PATH characters --- components/crashcatcher/windows_crashmonitor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/crashcatcher/windows_crashmonitor.cpp b/components/crashcatcher/windows_crashmonitor.cpp index 56f8bbdd4..8976deb2e 100644 --- a/components/crashcatcher/windows_crashmonitor.cpp +++ b/components/crashcatcher/windows_crashmonitor.cpp @@ -152,10 +152,13 @@ namespace Crash if (miniDumpWriteDump == NULL) return; - const std::wstring utf16Path = utf8ToUtf16(mShm->mStartup.mLogFilePath); + std::wstring utf16Path = utf8ToUtf16(mShm->mStartup.mLogFilePath); if (utf16Path.empty()) return; + if (utf16Path.length() > MAX_PATH) + utf16Path = LR"(\\?\)" + utf16Path; + HANDLE hCrashLog = CreateFileW(utf16Path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (hCrashLog == NULL || hCrashLog == INVALID_HANDLE_VALUE) return; From 112437cf287b7873ec04b54dbf01f64822740b69 Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Sun, 13 Dec 2020 14:13:07 +0100 Subject: [PATCH 25/51] Change crash file to dmp on window to avoid renaming it --- components/debug/debugging.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index 657c04d0f..e9bcf8ad7 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -165,7 +165,6 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c #endif const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log"; - const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.log"; boost::filesystem::ofstream logfile; int ret = 0; @@ -190,8 +189,10 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c #endif #if defined(_WIN32) + const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.dmp"; Crash::CrashCatcher crashy(argc, argv, (cfgMgr.getLogPath() / crashLogName).make_preferred().string()); #else + const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.log"; // install the crash handler as soon as possible. note that the log path // does not depend on config being read. crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / crashLogName).string()); From cc5c6fe3ad642394bdf64983ee1e3367c896cd4c Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Sun, 13 Dec 2020 19:03:04 +0100 Subject: [PATCH 26/51] Use data() method --- components/crashcatcher/windows_crashcatcher.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/crashcatcher/windows_crashcatcher.cpp b/components/crashcatcher/windows_crashcatcher.cpp index dc10f4dcb..4c3dfa8f6 100644 --- a/components/crashcatcher/windows_crashcatcher.cpp +++ b/components/crashcatcher/windows_crashcatcher.cpp @@ -130,7 +130,7 @@ namespace Crash DWORD copied = 0; do { executablePath.resize(executablePath.size() + MAX_PATH); - copied = GetModuleFileNameW(nullptr, &executablePath[0], executablePath.size()); + copied = GetModuleFileNameW(nullptr, executablePath.data(), executablePath.size()); } while (copied >= executablePath.size()); executablePath.resize(copied); @@ -157,7 +157,7 @@ namespace Crash PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi)); - if (!CreateProcessW(&executablePath[0], &arguments[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) + if (!CreateProcessW(executablePath.data(), arguments.data(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) throw std::runtime_error("Could not start crash monitor process"); waitMonitor(); From 201999c4a99866a64795aef6852f066663e79625 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Mon, 14 Dec 2020 03:01:26 +0300 Subject: [PATCH 27/51] Make sure adjustPosition() is safe to call for any actor --- apps/openmw/mwworld/worldimp.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f59c92c6a..3756ea04b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1345,7 +1345,11 @@ namespace MWWorld moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z()); if (ptr.getClass().isActor()) - mPhysics->getActor(ptr)->resetPosition(); + { + MWPhysics::Actor* actor = mPhysics->getActor(ptr); + if (actor) + actor->resetPosition(); + } } void World::fixPosition() From ab789e37e1136465c8bfdb1ccb1b3aee6a9f52cf Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Mon, 14 Dec 2020 03:45:03 +0300 Subject: [PATCH 28/51] Wizard: fix "file is already opened" warning --- apps/wizard/mainwizard.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/wizard/mainwizard.cpp b/apps/wizard/mainwizard.cpp index 160c6f721..d92f5b029 100644 --- a/apps/wizard/mainwizard.cpp +++ b/apps/wizard/mainwizard.cpp @@ -157,6 +157,8 @@ void Wizard::MainWizard::setupGameSettings() mGameSettings.readUserFile(stream); } + file.close(); + // Now the rest QStringList paths; paths.append(userPath + QLatin1String("openmw.cfg")); From 1c83e4936d30d4873b785b62d43e3718547fbd0e Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sun, 13 Dec 2020 02:35:57 +0300 Subject: [PATCH 29/51] Read BSShaderTextureSet and NiColorInterpolator Accept boolean-based and 4D vector-based NiInterpolators in ValueInterpolator constructor --- components/nif/controlled.cpp | 5 +++++ components/nif/controlled.hpp | 18 ++++++++++++++++++ components/nif/controller.cpp | 11 +++++++++++ components/nif/controller.hpp | 8 ++++++++ components/nif/niffile.cpp | 2 ++ components/nif/record.hpp | 4 +++- components/nif/recordptr.hpp | 2 ++ components/nifosg/controller.hpp | 4 +++- 8 files changed, 52 insertions(+), 2 deletions(-) diff --git a/components/nif/controlled.cpp b/components/nif/controlled.cpp index ab2b8dc17..c8116ce74 100644 --- a/components/nif/controlled.cpp +++ b/components/nif/controlled.cpp @@ -47,6 +47,11 @@ namespace Nif data.post(nif); } + void BSShaderTextureSet::read(NIFStream *nif) + { + nif->getSizedStrings(textures, nif->getUInt()); + } + void NiParticleModifier::read(NIFStream *nif) { next.read(nif); diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index 57d538f83..ac48e5049 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -66,6 +66,24 @@ public: void post(NIFFile *nif) override; }; +struct BSShaderTextureSet : public Record +{ + enum TextureType + { + TextureType_Base = 0, + TextureType_Normal = 1, + TextureType_Glow = 2, + TextureType_Parallax = 3, + TextureType_Env = 4, + TextureType_EnvMask = 5, + TextureType_Subsurface = 6, + TextureType_BackLighting = 7 + }; + std::vector textures; + + void read(NIFStream *nif) override; +}; + struct NiParticleModifier : public Record { NiParticleModifierPtr next; diff --git a/components/nif/controller.cpp b/components/nif/controller.cpp index 1e909120e..704c4928e 100644 --- a/components/nif/controller.cpp +++ b/components/nif/controller.cpp @@ -325,4 +325,15 @@ namespace Nif data.post(nif); } + void NiColorInterpolator::read(NIFStream *nif) + { + defaultVal = nif->getVector4(); + data.read(nif); + } + + void NiColorInterpolator::post(NIFFile *nif) + { + data.post(nif); + } + } diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 6b84d3749..71afdef26 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -230,5 +230,13 @@ struct NiTransformInterpolator : public Interpolator void post(NIFFile *nif) override; }; +struct NiColorInterpolator : public Interpolator +{ + osg::Vec4f defaultVal; + NiColorDataPtr data; + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + } // Namespace #endif diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 67c864f47..c96bf30d6 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -129,6 +129,8 @@ static std::map makeFactory() factory["NiPoint3Interpolator"] = {&construct , RC_NiPoint3Interpolator }; factory["NiTransformController"] = {&construct , RC_NiKeyframeController }; factory["NiTransformInterpolator"] = {&construct , RC_NiTransformInterpolator }; + factory["NiColorInterpolator"] = {&construct , RC_NiColorInterpolator }; + factory["BSShaderTextureSet"] = {&construct , RC_BSShaderTextureSet }; return factory; } diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 2217c588e..041122731 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -118,7 +118,9 @@ enum RecordType RC_NiFloatInterpolator, RC_NiPoint3Interpolator, RC_NiBoolInterpolator, - RC_NiTransformInterpolator + RC_NiTransformInterpolator, + RC_NiColorInterpolator, + RC_BSShaderTextureSet }; /// Base class for all records diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index ed8f7ef6b..0f5615a68 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -147,6 +147,7 @@ struct NiSkinPartition; struct NiFloatInterpolator; struct NiPoint3Interpolator; struct NiTransformInterpolator; +struct BSShaderTextureSet; using NodePtr = RecordPtrT; using ExtraPtr = RecordPtrT; @@ -174,6 +175,7 @@ using NiSkinPartitionPtr = RecordPtrT; using NiFloatInterpolatorPtr = RecordPtrT; using NiPoint3InterpolatorPtr = RecordPtrT; using NiTransformInterpolatorPtr = RecordPtrT; +using BSShaderTextureSetPtr = RecordPtrT; using NodeList = RecordListT; using PropertyList = RecordListT; diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 0063b2ec0..96beafcbb 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -66,7 +66,9 @@ namespace NifOsg std::conjunction_v< std::disjunction< std::is_same, - std::is_same + std::is_same, + std::is_same, + std::is_same >, std::is_same >, From 5310dd6807a25564399fbff4cda9442d88bac800 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sun, 13 Dec 2020 02:46:33 +0300 Subject: [PATCH 30/51] Clean up particle vertex handling --- components/nif/controller.hpp | 2 +- components/nifosg/nifloader.cpp | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 71afdef26..4016188ce 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -37,7 +37,7 @@ public: float lifetime; float lifespan; float timestamp; - int vertex; + unsigned short vertex; }; float velocity; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 0db15237e..b172e386e 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -963,6 +963,9 @@ namespace NifOsg if (particle.lifespan <= 0) continue; + if (particle.vertex >= particledata->vertices.size()) + continue; + ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime)); osgParticle::Particle* created = partsys->createParticle(&particletemplate); @@ -971,16 +974,16 @@ namespace NifOsg // Note this position and velocity is not correct for a particle system with absolute reference frame, // which can not be done in this loader since we are not attached to the scene yet. Will be fixed up post-load in the SceneManager. created->setVelocity(particle.velocity); - const osg::Vec3f& position = particledata->vertices.at(particle.vertex); + const osg::Vec3f& position = particledata->vertices[particle.vertex]; created->setPosition(position); osg::Vec4f partcolor (1.f,1.f,1.f,1.f); - if (particle.vertex < int(particledata->colors.size())) - partcolor = particledata->colors.at(particle.vertex); + if (particle.vertex < particledata->colors.size()) + partcolor = particledata->colors[particle.vertex]; float size = partctrl->size; - if (particle.vertex < int(particledata->sizes.size())) - size *= particledata->sizes.at(particle.vertex); + if (particle.vertex < particledata->sizes.size()) + size *= particledata->sizes[particle.vertex]; created->setSizeRange(osgParticle::rangef(size, size)); box.expandBy(osg::BoundingSphere(position, size)); From 8ca324af0ad83cfc6d863322420ae8542cb33971 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sun, 13 Dec 2020 02:49:12 +0300 Subject: [PATCH 31/51] Handle emissive TexEnv creation in one place --- components/nifosg/nifloader.cpp | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index b172e386e..3120b0d74 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -466,19 +466,12 @@ namespace NifOsg texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); - osg::ref_ptr texEnv = new osg::TexEnvCombine; - texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); - texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); - texEnv->setCombine_RGB(osg::TexEnvCombine::ADD); - texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); - texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); - int texUnit = 3; // FIXME osg::StateSet* stateset = node->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(texUnit, texGen, osg::StateAttribute::ON); - stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON); stateset->addUniform(new osg::Uniform("envMapColor", osg::Vec4f(1,1,1,1))); } @@ -1484,6 +1477,17 @@ namespace NifOsg return image; } + osg::ref_ptr createEmissiveTexEnv() + { + osg::ref_ptr texEnv(new osg::TexEnvCombine); + texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); + texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); + texEnv->setCombine_RGB(osg::TexEnvCombine::ADD); + texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); + texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); + return texEnv; + } + void handleTextureProperty(const Nif::NiTexturingProperty* texprop, const std::string& nodeName, osg::StateSet* stateset, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector& boundTextures, int animflags) { if (!boundTextures.empty()) @@ -1570,14 +1574,7 @@ namespace NifOsg if (i == Nif::NiTexturingProperty::GlowTexture) { - osg::TexEnvCombine* texEnv = new osg::TexEnvCombine; - texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); - texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); - texEnv->setCombine_RGB(osg::TexEnvCombine::ADD); - texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); - texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); - - stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON); } else if (i == Nif::NiTexturingProperty::DarkTexture) { From 8fd45d85ec0ae1c209c51dab942bcfba7d8be14e Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sun, 13 Dec 2020 03:16:05 +0300 Subject: [PATCH 32/51] Unify NiGeometry/NiGeometryData handling --- .../nifloader/testbulletnifloader.cpp | 22 +++- components/nif/node.hpp | 121 +++++------------- components/nif/recordptr.hpp | 10 +- components/nifbullet/bulletnifloader.cpp | 51 ++++---- components/nifosg/nifloader.cpp | 81 ++++++------ 5 files changed, 115 insertions(+), 170 deletions(-) diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 3a854bf5c..5769b1d0a 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -259,14 +259,19 @@ namespace value.isBone = false; } - void init(Nif::NiTriShape& value) + void init(Nif::NiGeometry& value) { init(static_cast(value)); - value.recType = Nif::RC_NiTriShape; - value.data = Nif::NiTriShapeDataPtr(nullptr); + value.data = Nif::NiGeometryDataPtr(nullptr); value.skin = Nif::NiSkinInstancePtr(nullptr); } + void init(Nif::NiTriShape& value) + { + init(static_cast(value)); + value.recType = Nif::RC_NiTriShape; + } + void init(Nif::NiSkinInstance& value) { value.data = Nif::NiSkinDataPtr(nullptr); @@ -361,13 +366,15 @@ namespace init(mNiStringExtraData2); init(mController); + mNiTriShapeData.recType = Nif::RC_NiTriShapeData; mNiTriShapeData.vertices = {osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0)}; mNiTriShapeData.triangles = {0, 1, 2}; - mNiTriShape.data = Nif::NiTriShapeDataPtr(&mNiTriShapeData); + mNiTriShape.data = Nif::NiGeometryDataPtr(&mNiTriShapeData); + mNiTriShapeData2.recType = Nif::RC_NiTriShapeData; mNiTriShapeData2.vertices = {osg::Vec3f(0, 0, 1), osg::Vec3f(1, 0, 1), osg::Vec3f(1, 1, 1)}; mNiTriShapeData2.triangles = {0, 1, 2}; - mNiTriShape2.data = Nif::NiTriShapeDataPtr(&mNiTriShapeData2); + mNiTriShape2.data = Nif::NiGeometryDataPtr(&mNiTriShapeData2); } }; @@ -873,7 +880,7 @@ namespace TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape) { - mNiTriShape.data = Nif::NiTriShapeDataPtr(nullptr); + mNiTriShape.data = Nif::NiGeometryDataPtr(nullptr); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); @@ -888,7 +895,8 @@ namespace TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_triangles_should_return_shape_with_null_collision_shape) { - mNiTriShape.data->triangles.clear(); + auto data = static_cast(mNiTriShape.data.getPtr()); + data->triangles.clear(); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 58397bdca..3106e368b 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -241,80 +241,52 @@ struct NiNode : Node struct NiGeometry : Node { + /* Possible flags: + 0x40 - mesh has no vertex normals ? + + Only flags included in 0x47 (ie. 0x01, 0x02, 0x04 and 0x40) have + been observed so far. + */ + struct MaterialData { - std::vector materialNames; - std::vector materialExtraData; - unsigned int activeMaterial{0}; - bool materialNeedsUpdate{false}; + std::vector names; + std::vector extra; + unsigned int active{0}; + bool needsUpdate{false}; void read(NIFStream *nif) { if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,0)) return; - unsigned int numMaterials = 0; + unsigned int num = 0; if (nif->getVersion() <= NIFStream::generateVersion(20,1,0,3)) - numMaterials = nif->getBoolean(); // Has Shader + num = nif->getBoolean(); // Has Shader else if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5)) - numMaterials = nif->getUInt(); - if (numMaterials) + num = nif->getUInt(); + if (num) { - nif->getStrings(materialNames, numMaterials); - nif->getInts(materialExtraData, numMaterials); + nif->getStrings(names, num); + nif->getInts(extra, num); } if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5)) - activeMaterial = nif->getUInt(); + active = nif->getUInt(); if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS) - { - materialNeedsUpdate = nif->getBoolean(); - if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3) - nif->skip(8); - } + needsUpdate = nif->getBoolean(); } }; + NiGeometryDataPtr data; NiSkinInstancePtr skin; - MaterialData materialData; -}; - -struct NiTriShape : NiGeometry -{ - /* Possible flags: - 0x40 - mesh has no vertex normals ? - - Only flags included in 0x47 (ie. 0x01, 0x02, 0x04 and 0x40) have - been observed so far. - */ - - NiTriShapeDataPtr data; - - void read(NIFStream *nif) override - { - Node::read(nif); - data.read(nif); - skin.read(nif); - materialData.read(nif); - } - - void post(NIFFile *nif) override - { - Node::post(nif); - data.post(nif); - skin.post(nif); - if (!skin.empty()) - nif->setUseSkinning(true); - } -}; - -struct NiTriStrips : NiGeometry -{ - NiTriStripsDataPtr data; + MaterialData material; void read(NIFStream *nif) override { Node::read(nif); data.read(nif); skin.read(nif); - materialData.read(nif); + material.read(nif); + if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3) + nif->skip(8); } void post(NIFFile *nif) override @@ -322,31 +294,15 @@ struct NiTriStrips : NiGeometry Node::post(nif); data.post(nif); skin.post(nif); - if (!skin.empty()) + if (recType != RC_NiParticles && !skin.empty()) nif->setUseSkinning(true); } }; -struct NiLines : NiGeometry -{ - NiLinesDataPtr data; - - void read(NIFStream *nif) override - { - Node::read(nif); - data.read(nif); - skin.read(nif); - } - - void post(NIFFile *nif) override - { - Node::post(nif); - data.post(nif); - skin.post(nif); - if (!skin.empty()) - nif->setUseSkinning(true); - } -}; +struct NiTriShape : NiGeometry {}; +struct NiTriStrips : NiGeometry {}; +struct NiLines : NiGeometry {}; +struct NiParticles : NiGeometry { }; struct NiCamera : Node { @@ -401,25 +357,6 @@ struct NiCamera : Node } }; -struct NiParticles : NiGeometry -{ - NiParticlesDataPtr data; - void read(NIFStream *nif) override - { - Node::read(nif); - data.read(nif); - skin.read(nif); - materialData.read(nif); - } - - void post(NIFFile *nif) override - { - Node::post(nif); - data.post(nif); - skin.post(nif); - } -}; - // A node used as the base to switch between child nodes, such as for LOD levels. struct NiSwitchNode : public NiNode { diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 0f5615a68..dd6e35708 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -134,20 +134,18 @@ struct NiMorphData; class NiPixelData; class NiColorData; struct NiKeyframeData; -class NiTriShapeData; class NiTriStripsData; class NiSkinInstance; class NiSourceTexture; -class NiParticlesData; class NiPalette; struct NiParticleModifier; -struct NiLinesData; struct NiBoolData; struct NiSkinPartition; struct NiFloatInterpolator; struct NiPoint3Interpolator; struct NiTransformInterpolator; struct BSShaderTextureSet; +struct NiGeometryData; using NodePtr = RecordPtrT; using ExtraPtr = RecordPtrT; @@ -162,12 +160,8 @@ using NiPixelDataPtr = RecordPtrT; using NiFloatDataPtr = RecordPtrT; using NiColorDataPtr = RecordPtrT; using NiKeyframeDataPtr = RecordPtrT; -using NiTriShapeDataPtr = RecordPtrT; -using NiTriStripsDataPtr = RecordPtrT; -using NiLinesDataPtr = RecordPtrT; using NiSkinInstancePtr = RecordPtrT; using NiSourceTexturePtr = RecordPtrT; -using NiParticlesDataPtr = RecordPtrT; using NiPalettePtr = RecordPtrT; using NiParticleModifierPtr = RecordPtrT; using NiBoolDataPtr = RecordPtrT; @@ -176,12 +170,14 @@ using NiFloatInterpolatorPtr = RecordPtrT; using NiPoint3InterpolatorPtr = RecordPtrT; using NiTransformInterpolatorPtr = RecordPtrT; using BSShaderTextureSetPtr = RecordPtrT; +using NiGeometryDataPtr = RecordPtrT; using NodeList = RecordListT; using PropertyList = RecordListT; using ExtraList = RecordListT; using NiSourceTextureList = RecordListT; using NiFloatInterpolatorList = RecordListT; +using NiTriStripsDataList = RecordListT; } // Namespace #endif diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index b24d08fd8..a14672636 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -34,11 +34,10 @@ bool pathFileNameStartsWithX(const std::string& path) void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data, const osg::Matrixf &transform) { - mesh.preallocateVertices(static_cast(data.vertices.size())); - mesh.preallocateIndices(static_cast(data.triangles.size())); - const std::vector &vertices = data.vertices; const std::vector &triangles = data.triangles; + mesh.preallocateVertices(static_cast(vertices.size())); + mesh.preallocateIndices(static_cast(triangles.size())); for (std::size_t i = 0; i < triangles.size(); i += 3) { @@ -54,8 +53,6 @@ void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, co { const std::vector &vertices = data.vertices; const std::vector> &strips = data.strips; - if (vertices.empty() || strips.empty()) - return; mesh.preallocateVertices(static_cast(vertices.size())); int numTriangles = 0; for (const std::vector& strip : strips) @@ -102,12 +99,12 @@ void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, co } } -void fillTriangleMesh(btTriangleMesh& mesh, const Nif::Node* nifNode, const osg::Matrixf &transform = osg::Matrixf()) +void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiGeometry* geometry, const osg::Matrixf &transform = osg::Matrixf()) { - if (nifNode->recType == Nif::RC_NiTriShape) - fillTriangleMesh(mesh, static_cast(nifNode)->data.get(), transform); - else if (nifNode->recType == Nif::RC_NiTriStrips) - fillTriangleMesh(mesh, static_cast(nifNode)->data.get(), transform); + if (geometry->recType == Nif::RC_NiTriShape) + fillTriangleMesh(mesh, static_cast(geometry->data.get()), transform); + else if (geometry->recType == Nif::RC_NiTriStrips) + fillTriangleMesh(mesh, static_cast(geometry->data.get()), transform); } } @@ -341,23 +338,31 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons if ((flags & 0x800)) return; - if (nifNode->recType == Nif::RC_NiTriShape) + auto niGeometry = static_cast(nifNode); + if (niGeometry->data.empty() || niGeometry->data->vertices.empty()) + return; + + if (niGeometry->recType == Nif::RC_NiTriShape) { - const Nif::NiTriShape* shape = static_cast(nifNode); - if (!shape->skin.empty()) - isAnimated = false; - if (shape->data.empty() || shape->data->triangles.empty()) + if (niGeometry->data->recType != Nif::RC_NiTriShapeData) + return; + + auto data = static_cast(niGeometry->data.getPtr()); + if (data->triangles.empty()) return; } - else + else if (niGeometry->recType == Nif::RC_NiTriStrips) { - const Nif::NiTriStrips* shape = static_cast(nifNode); - if (!shape->skin.empty()) - isAnimated = false; - if (shape->data.empty() || shape->data->strips.empty()) + if (niGeometry->data->recType != Nif::RC_NiTriStripsData) + return; + + auto data = static_cast(niGeometry->data.getPtr()); + if (data->strips.empty()) return; } + if (!niGeometry->skin.empty()) + isAnimated = false; if (isAnimated) { @@ -366,7 +371,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons std::unique_ptr childMesh(new btTriangleMesh); - fillTriangleMesh(*childMesh, nifNode); + fillTriangleMesh(*childMesh, niGeometry); std::unique_ptr childShape(new Resource::TriangleMeshShape(childMesh.get(), true)); childMesh.release(); @@ -394,7 +399,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons if (!mAvoidStaticMesh) mAvoidStaticMesh.reset(new btTriangleMesh(false)); - fillTriangleMesh(*mAvoidStaticMesh, nifNode, transform); + fillTriangleMesh(*mAvoidStaticMesh, niGeometry, transform); } else { @@ -402,7 +407,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons mStaticMesh.reset(new btTriangleMesh(false)); // Static shape, just transform all vertices into position - fillTriangleMesh(*mStaticMesh, nifNode, transform); + fillTriangleMesh(*mStaticMesh, niGeometry, transform); } } diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 3120b0d74..7ebb9bf8d 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -939,11 +939,11 @@ namespace NifOsg // Load the initial state of the particle system, i.e. the initial particles and their positions, velocity and colors. void handleParticleInitialState(const Nif::Node* nifNode, osgParticle::ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl) { - const auto particleNode = static_cast(nifNode); - if (particleNode->data.empty()) + auto particleNode = static_cast(nifNode); + if (particleNode->data.empty() || particleNode->data->recType != Nif::RC_NiParticlesData) return; - const Nif::NiParticlesData* particledata = particleNode->data.getPtr(); + auto particledata = static_cast(particleNode->data.getPtr()); osg::BoundingBox box; @@ -1173,51 +1173,50 @@ namespace NifOsg void handleNiGeometry(const Nif::Node *nifNode, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - const Nif::NiGeometryData* niGeometryData = nullptr; - if (nifNode->recType == Nif::RC_NiTriShape) + const Nif::NiGeometry* niGeometry = static_cast(nifNode); + if (niGeometry->data.empty()) + return; + const Nif::NiGeometryData* niGeometryData = niGeometry->data.getPtr(); + + if (niGeometry->recType == Nif::RC_NiTriShape) { - const Nif::NiTriShape* triShape = static_cast(nifNode); - if (!triShape->data.empty()) - { - const Nif::NiTriShapeData* data = triShape->data.getPtr(); - niGeometryData = static_cast(data); - if (!data->triangles.empty()) - geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(), - (unsigned short*)data->triangles.data())); - } + if (niGeometryData->recType != Nif::RC_NiTriShapeData) + return; + auto triangles = static_cast(niGeometryData)->triangles; + if (triangles.empty()) + return; + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, triangles.size(), + (unsigned short*)triangles.data())); } - else if (nifNode->recType == Nif::RC_NiTriStrips) + else if (niGeometry->recType == Nif::RC_NiTriStrips) { - const Nif::NiTriStrips* triStrips = static_cast(nifNode); - if (!triStrips->data.empty()) + if (niGeometryData->recType != Nif::RC_NiTriStripsData) + return; + auto data = static_cast(niGeometryData); + bool hasGeometry = false; + for (const auto& strip : data->strips) { - const Nif::NiTriStripsData* data = triStrips->data.getPtr(); - niGeometryData = static_cast(data); - if (!data->strips.empty()) - { - for (const auto& strip : data->strips) - { - if (strip.size() >= 3) - geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), - (unsigned short*)strip.data())); - } - } + if (strip.size() < 3) + continue; + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), + (unsigned short*)strip.data())); + hasGeometry = true; } + if (!hasGeometry) + return; } - else if (nifNode->recType == Nif::RC_NiLines) + else if (niGeometry->recType == Nif::RC_NiLines) { - const Nif::NiLines* lines = static_cast(nifNode); - if (!lines->data.empty()) - { - const Nif::NiLinesData* data = lines->data.getPtr(); - niGeometryData = static_cast(data); - const auto& line = data->lines; - if (!line.empty()) - geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, line.size(), (unsigned short*)line.data())); - } + if (niGeometryData->recType != Nif::RC_NiLinesData) + return; + auto data = static_cast(niGeometryData); + const auto& line = data->lines; + if (line.empty()) + return; + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, line.size(), + (unsigned short*)line.data())); } - if (niGeometryData) - handleNiGeometryData(geometry, niGeometryData, boundTextures, nifNode->name); + handleNiGeometryData(geometry, niGeometryData, boundTextures, nifNode->name); // osg::Material properties are handled here for two reasons: // - if there are no vertex colors, we need to disable colorMode. @@ -1225,7 +1224,7 @@ namespace NifOsg // above the actual renderable would be tedious. std::vector drawableProps; collectDrawableProperties(nifNode, drawableProps); - applyDrawableProperties(parentNode, drawableProps, composite, niGeometryData && !niGeometryData->colors.empty(), animflags); + applyDrawableProperties(parentNode, drawableProps, composite, !niGeometryData->colors.empty(), animflags); } void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) From 42226533d8d2a3bde55e88c4f934b0a915013f68 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sun, 13 Dec 2020 03:45:29 +0300 Subject: [PATCH 33/51] Handle BSLODTriShape Its levels of detail are currently not handled --- components/nif/niffile.cpp | 1 + components/nif/node.hpp | 11 +++++++++++ components/nif/record.hpp | 3 ++- components/nifbullet/bulletnifloader.cpp | 8 +++++--- components/nifosg/nifloader.cpp | 3 ++- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index c96bf30d6..8aa0b6035 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -131,6 +131,7 @@ static std::map makeFactory() factory["NiTransformInterpolator"] = {&construct , RC_NiTransformInterpolator }; factory["NiColorInterpolator"] = {&construct , RC_NiColorInterpolator }; factory["BSShaderTextureSet"] = {&construct , RC_BSShaderTextureSet }; + factory["BSLODTriShape"] = {&construct , RC_BSLODTriShape }; return factory; } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 3106e368b..75041e187 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -300,6 +300,17 @@ struct NiGeometry : Node }; struct NiTriShape : NiGeometry {}; +struct BSLODTriShape : NiTriShape +{ + unsigned int lod0, lod1, lod2; + void read(NIFStream *nif) override + { + NiTriShape::read(nif); + lod0 = nif->getUInt(); + lod1 = nif->getUInt(); + lod2 = nif->getUInt(); + } +}; struct NiTriStrips : NiGeometry {}; struct NiLines : NiGeometry {}; struct NiParticles : NiGeometry { }; diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 041122731..6834c554e 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -120,7 +120,8 @@ enum RecordType RC_NiBoolInterpolator, RC_NiTransformInterpolator, RC_NiColorInterpolator, - RC_BSShaderTextureSet + RC_BSShaderTextureSet, + RC_BSLODTriShape }; /// Base class for all records diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index a14672636..033cfcab6 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -101,7 +101,7 @@ void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, co void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiGeometry* geometry, const osg::Matrixf &transform = osg::Matrixf()) { - if (geometry->recType == Nif::RC_NiTriShape) + if (geometry->recType == Nif::RC_NiTriShape || geometry->recType == Nif::RC_BSLODTriShape) fillTriangleMesh(mesh, static_cast(geometry->data.get()), transform); else if (geometry->recType == Nif::RC_NiTriStrips) fillTriangleMesh(mesh, static_cast(geometry->data.get()), transform); @@ -309,7 +309,9 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! // It must be ignored completely. // (occurs in tr_ex_imp_wall_arch_04.nif) - if(!node->hasBounds && (node->recType == Nif::RC_NiTriShape || node->recType == Nif::RC_NiTriStrips)) + if(!node->hasBounds && (node->recType == Nif::RC_NiTriShape + || node->recType == Nif::RC_NiTriStrips + || node->recType == Nif::RC_BSLODTriShape)) { handleNiTriShape(node, flags, getWorldTransform(node), isAnimated, avoid); } @@ -342,7 +344,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons if (niGeometry->data.empty() || niGeometry->data->vertices.empty()) return; - if (niGeometry->recType == Nif::RC_NiTriShape) + if (niGeometry->recType == Nif::RC_NiTriShape || niGeometry->recType == Nif::RC_BSLODTriShape) { if (niGeometry->data->recType != Nif::RC_NiTriShapeData) return; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 7ebb9bf8d..c3f4e50cf 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -67,6 +67,7 @@ namespace case Nif::RC_NiTriShape: case Nif::RC_NiTriStrips: case Nif::RC_NiLines: + case Nif::RC_BSLODTriShape: return true; } return false; @@ -1178,7 +1179,7 @@ namespace NifOsg return; const Nif::NiGeometryData* niGeometryData = niGeometry->data.getPtr(); - if (niGeometry->recType == Nif::RC_NiTriShape) + if (niGeometry->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_BSLODTriShape) { if (niGeometryData->recType != Nif::RC_NiTriShapeData) return; From c0b9823372e3da7e91d57c8c2029a957efa5a481 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sun, 13 Dec 2020 04:04:14 +0300 Subject: [PATCH 34/51] Read BSShaderProperty and handle NiGeometry properties --- components/nif/niffile.cpp | 1 + components/nif/node.hpp | 9 ++++++++- components/nif/property.cpp | 12 ++++++++++++ components/nif/property.hpp | 7 +++++++ components/nif/record.hpp | 3 ++- components/nif/recordptr.hpp | 4 ++++ components/nifosg/nifloader.cpp | 14 ++++++++++++++ 7 files changed, 48 insertions(+), 2 deletions(-) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 8aa0b6035..665533c91 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -132,6 +132,7 @@ static std::map makeFactory() factory["NiColorInterpolator"] = {&construct , RC_NiColorInterpolator }; factory["BSShaderTextureSet"] = {&construct , RC_BSShaderTextureSet }; factory["BSLODTriShape"] = {&construct , RC_BSLODTriShape }; + factory["BSShaderProperty"] = {&construct , RC_BSShaderProperty }; return factory; } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 75041e187..08f066e36 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -278,6 +278,8 @@ struct NiGeometry : Node NiGeometryDataPtr data; NiSkinInstancePtr skin; MaterialData material; + BSShaderPropertyPtr shaderprop; + NiAlphaPropertyPtr alphaprop; void read(NIFStream *nif) override { @@ -286,7 +288,10 @@ struct NiGeometry : Node skin.read(nif); material.read(nif); if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3) - nif->skip(8); + { + shaderprop.read(nif); + alphaprop.read(nif); + } } void post(NIFFile *nif) override @@ -294,6 +299,8 @@ struct NiGeometry : Node Node::post(nif); data.post(nif); skin.post(nif); + shaderprop.post(nif); + alphaprop.post(nif); if (recType != RC_NiParticles && !skin.empty()) nif->setUseSkinning(true); } diff --git a/components/nif/property.cpp b/components/nif/property.cpp index e6ae71c24..55113d7c8 100644 --- a/components/nif/property.cpp +++ b/components/nif/property.cpp @@ -99,6 +99,18 @@ void NiTexturingProperty::post(NIFFile *nif) shaderTextures[i].post(nif); } +void BSShaderProperty::read(NIFStream *nif) +{ + NiShadeProperty::read(nif); + if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) + { + type = nif->getUInt(); + flags1 = nif->getUInt(); + flags2 = nif->getUInt(); + envMapIntensity = nif->getFloat(); + } +} + void NiFogProperty::read(NIFStream *nif) { Property::read(nif); diff --git a/components/nif/property.hpp b/components/nif/property.hpp index c821b7c37..5bc0c52db 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -118,6 +118,13 @@ struct NiShadeProperty : public Property } }; +struct BSShaderProperty : public NiShadeProperty +{ + unsigned int type{0u}, flags1{0u}, flags2{0u}; + float envMapIntensity{0.f}; + void read(NIFStream *nif) override; +}; + struct NiDitherProperty : public Property { unsigned short flags; diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 6834c554e..efacd8246 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -121,7 +121,8 @@ enum RecordType RC_NiTransformInterpolator, RC_NiColorInterpolator, RC_BSShaderTextureSet, - RC_BSLODTriShape + RC_BSLODTriShape, + RC_BSShaderProperty }; /// Base class for all records diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index dd6e35708..4792895e2 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -146,6 +146,8 @@ struct NiPoint3Interpolator; struct NiTransformInterpolator; struct BSShaderTextureSet; struct NiGeometryData; +struct BSShaderProperty; +class NiAlphaProperty; using NodePtr = RecordPtrT; using ExtraPtr = RecordPtrT; @@ -171,6 +173,8 @@ using NiPoint3InterpolatorPtr = RecordPtrT; using NiTransformInterpolatorPtr = RecordPtrT; using BSShaderTextureSetPtr = RecordPtrT; using NiGeometryDataPtr = RecordPtrT; +using BSShaderPropertyPtr = RecordPtrT; +using NiAlphaPropertyPtr = RecordPtrT; using NodeList = RecordListT; using PropertyList = RecordListT; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index c3f4e50cf..5f68b1229 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -96,6 +96,15 @@ namespace } } } + + auto geometry = dynamic_cast(nifNode); + if (geometry) + { + if (!geometry->shaderprop.empty()) + out.emplace_back(geometry->shaderprop.getPtr()); + if (!geometry->alphaprop.empty()) + out.emplace_back(geometry->alphaprop.getPtr()); + } } // NodeCallback used to have a node always oriented towards the camera. The node can have translation and scale @@ -366,6 +375,11 @@ namespace NifOsg handleProperty(props[i].getPtr(), applyTo, composite, imageManager, boundTextures, animflags); } } + + auto geometry = dynamic_cast(nifNode); + // NiGeometry's NiAlphaProperty doesn't get handled here because it's a drawable property + if (geometry && !geometry->shaderprop.empty()) + handleProperty(geometry->shaderprop.getPtr(), applyTo, composite, imageManager, boundTextures, animflags); } void setupController(const Nif::Controller* ctrl, SceneUtil::Controller* toSetup, int animflags) From 085ea44af52d2780a25fee2ecd1d278c1170c9ec Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sun, 13 Dec 2020 04:19:14 +0300 Subject: [PATCH 35/51] Add BSShaderLightingProperty abstraction --- components/nif/property.cpp | 7 +++++++ components/nif/property.hpp | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/components/nif/property.cpp b/components/nif/property.cpp index 55113d7c8..d5357e123 100644 --- a/components/nif/property.cpp +++ b/components/nif/property.cpp @@ -111,6 +111,13 @@ void BSShaderProperty::read(NIFStream *nif) } } +void BSShaderLightingProperty::read(NIFStream *nif) +{ + BSShaderProperty::read(nif); + if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) + clamp = nif->getUInt(); +} + void NiFogProperty::read(NIFStream *nif) { Property::read(nif); diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 5bc0c52db..813a1888d 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -125,6 +125,12 @@ struct BSShaderProperty : public NiShadeProperty void read(NIFStream *nif) override; }; +struct BSShaderLightingProperty : public BSShaderProperty +{ + unsigned int clamp{0u}; + void read(NIFStream *nif) override; +}; + struct NiDitherProperty : public Property { unsigned short flags; From 53e1e57eef3954b5b61dd44c98aeb3c26932d679 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sun, 13 Dec 2020 16:51:20 +0300 Subject: [PATCH 36/51] Formatting --- components/nifbullet/bulletnifloader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 033cfcab6..d72cef194 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -310,8 +310,8 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n // It must be ignored completely. // (occurs in tr_ex_imp_wall_arch_04.nif) if(!node->hasBounds && (node->recType == Nif::RC_NiTriShape - || node->recType == Nif::RC_NiTriStrips - || node->recType == Nif::RC_BSLODTriShape)) + || node->recType == Nif::RC_NiTriStrips + || node->recType == Nif::RC_BSLODTriShape)) { handleNiTriShape(node, flags, getWorldTransform(node), isAnimated, avoid); } From b39437dfb63156fa32a93515d0404e01efdf70e2 Mon Sep 17 00:00:00 2001 From: fredzio Date: Mon, 14 Dec 2020 22:23:01 +0100 Subject: [PATCH 37/51] Don't allow projectiles to stand still when they hit an ally. When an NPC fire a projectile, it should affect only its targeted actor. To this end, after a hit is detected the target is checked against the list of AI targets and reactivated if necessary. Problem occurs when the hit occurs as a result of a friendly actor going into the projectile (detected in ClosestNotMeConvexResultCallback): while the projectile is inside the friend's collision box, it is deactivated, just to be immediately reactivated. Effectively, the projectile does nothing until the actor moves out. Add a check inside the ClosestNotMeConvexResultCallback before declaring a hit. Since the necessary data is not safely accessible from the async thread, maintain a copy inside the Projectile class. --- .../closestnotmeconvexresultcallback.cpp | 7 +-- apps/openmw/mwphysics/physicssystem.cpp | 7 +++ apps/openmw/mwphysics/projectile.cpp | 48 +++++++++++++++++-- apps/openmw/mwphysics/projectile.hpp | 10 +++- apps/openmw/mwworld/projectilemanager.cpp | 36 +++----------- apps/openmw/mwworld/projectilemanager.hpp | 2 - 6 files changed, 68 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp index 8f7242276..3ec09d00d 100644 --- a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp +++ b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp @@ -30,12 +30,9 @@ namespace MWPhysics return btScalar(1); auto* targetHolder = static_cast(mMe->getUserPointer()); const MWWorld::Ptr target = targetHolder->getPtr(); - // do nothing if we hit the caster. Sometimes the launching origin is inside of caster collision shape - if (projectileHolder->getCaster() != target) - { + if (projectileHolder->isValidTarget(target)) projectileHolder->hit(target, convexResult.m_hitPointLocal, convexResult.m_hitNormalLocal); - return btScalar(1); - } + return btScalar(1); } btVector3 hitNormalWorld; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 8106a78ea..151801e67 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -537,6 +537,13 @@ namespace MWPhysics if (actor->getStandingOnPtr() == old) actor->setStandingOnPtr(updated); } + + for (auto& [_, projectile] : mProjectiles) + { + if (projectile->getCaster() == old) + projectile->setCaster(updated); + } + } Actor *PhysicsSystem::getActor(const MWWorld::Ptr &ptr) diff --git a/apps/openmw/mwphysics/projectile.cpp b/apps/openmw/mwphysics/projectile.cpp index 5f5bc778b..a8aaeb72a 100644 --- a/apps/openmw/mwphysics/projectile.cpp +++ b/apps/openmw/mwphysics/projectile.cpp @@ -55,7 +55,7 @@ Projectile::~Projectile() void Projectile::commitPositionChange() { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mMutex); if (mTransformUpdatePending) { mCollisionObject->setWorldTransform(mLocalTransform); @@ -65,7 +65,7 @@ void Projectile::commitPositionChange() void Projectile::setPosition(const osg::Vec3f &position) { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mMutex); mLocalTransform.setOrigin(Misc::Convert::toBullet(position)); mTransformUpdatePending = true; } @@ -74,7 +74,7 @@ void Projectile::hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal) { if (!mActive.load(std::memory_order_acquire)) return; - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mMutex); mHitTarget = target; mHitPosition = pos; mHitNormal = normal; @@ -86,4 +86,46 @@ void Projectile::activate() assert(!mActive); mActive.store(true, std::memory_order_release); } + +MWWorld::Ptr Projectile::getCaster() const +{ + std::scoped_lock lock(mMutex); + return mCaster; +} + +void Projectile::setCaster(MWWorld::Ptr caster) +{ + std::scoped_lock lock(mMutex); + mCaster = caster; +} + +void Projectile::setValidTargets(const std::vector& targets) +{ + std::scoped_lock lock(mMutex); + mValidTargets = targets; +} + +bool Projectile::isValidTarget(const MWWorld::Ptr& target) const +{ + std::scoped_lock lock(mMutex); + if (mCaster == target) + return false; + + if (!mValidTargets.empty()) + { + bool validTarget = false; + for (const auto& targetActor : mValidTargets) + { + if (targetActor == target) + { + validTarget = true; + break; + } + } + + return validTarget; + } + return true; +} + } diff --git a/apps/openmw/mwphysics/projectile.hpp b/apps/openmw/mwphysics/projectile.hpp index ed0fdce15..5ae963b9a 100644 --- a/apps/openmw/mwphysics/projectile.hpp +++ b/apps/openmw/mwphysics/projectile.hpp @@ -62,7 +62,8 @@ namespace MWPhysics return mHitTarget; } - MWWorld::Ptr getCaster() const { return mCaster; } + MWWorld::Ptr getCaster() const; + void setCaster(MWWorld::Ptr caster); osg::Vec3f getHitPos() const { @@ -73,6 +74,9 @@ namespace MWPhysics void hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal); void activate(); + void setValidTargets(const std::vector& targets); + bool isValidTarget(const MWWorld::Ptr& target) const; + private: std::unique_ptr mShape; @@ -87,7 +91,9 @@ namespace MWPhysics btVector3 mHitPosition; btVector3 mHitNormal; - mutable std::mutex mPositionMutex; + std::vector mValidTargets; + + mutable std::mutex mMutex; osg::Vec3f mPosition; diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index dba4289a2..08c9e4662 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -387,7 +387,7 @@ namespace MWWorld if (magicBoltState.mToDelete) continue; - const auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId); + auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId); if (!projectile->isActive()) continue; // If the actor caster is gone, the magic bolt needs to be removed from the scene during the next frame. @@ -423,6 +423,7 @@ namespace MWWorld std::vector targetActors; if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer()) caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors); + projectile->setValidTargets(targetActors); // Check for impact // TODO: use a proper btRigidBody / btGhostObject? @@ -469,7 +470,7 @@ namespace MWWorld if (projectileState.mToDelete) continue; - const auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId); + auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId); if (!projectile->isActive()) continue; // gravity constant - must be way lower than the gravity affecting actors, since we're not @@ -499,6 +500,7 @@ namespace MWWorld std::vector targetActors; if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer()) caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors); + projectile->setValidTargets(targetActors); // Check for impact // TODO: use a proper btRigidBody / btGhostObject? @@ -547,7 +549,7 @@ namespace MWWorld const auto pos = projectile->getHitPos(); MWWorld::Ptr caster = projectileState.getCaster(); assert(target != caster); - if (!isValidTarget(caster, target)) + if (!projectile->isValidTarget(target)) { projectile->activate(); continue; @@ -583,7 +585,7 @@ namespace MWWorld const auto pos = projectile->getHitPos(); MWWorld::Ptr caster = magicBoltState.getCaster(); assert(target != caster); - if (!isValidTarget(caster, target)) + if (!projectile->isValidTarget(target)) { projectile->activate(); continue; @@ -607,32 +609,6 @@ namespace MWWorld mMagicBolts.end()); } - bool ProjectileManager::isValidTarget(const MWWorld::Ptr& caster, const MWWorld::Ptr& target) - { - // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. - std::vector targetActors; - if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer()) - { - caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors); - if (!targetActors.empty()) - { - bool validTarget = false; - for (MWWorld::Ptr& targetActor : targetActors) - { - if (targetActor == target) - { - validTarget = true; - break; - } - } - - return validTarget; - } - } - - return true; - } - void ProjectileManager::cleanupProjectile(ProjectileManager::ProjectileState& state) { mParent->removeChild(state.mNode); diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index d589e985e..2cd570847 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -132,8 +132,6 @@ namespace MWWorld void moveProjectiles(float dt); void moveMagicBolts(float dt); - bool isValidTarget(const MWWorld::Ptr& caster, const MWWorld::Ptr& target); - void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture = ""); void update (State& state, float duration); From 3195716a2c7cae7d34c5da5c360cf4c068aa9ba1 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Tue, 15 Dec 2020 13:49:25 +0200 Subject: [PATCH 38/51] Don't force loop textkey --- components/resource/keyframemanager.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/components/resource/keyframemanager.cpp b/components/resource/keyframemanager.cpp index 41b10bd65..0ffe59712 100644 --- a/components/resource/keyframemanager.cpp +++ b/components/resource/keyframemanager.cpp @@ -40,8 +40,6 @@ namespace Resource std::string animationName = animation->getName(); std::string start = animationName + std::string(": start"); std::string stop = animationName + std::string(": stop"); - std::string loopstart = animationName + std::string(": loop start"); - std::string loopstop = animationName + std::string(": loop stop"); const osgAnimation::ChannelList& channels = animation->getChannels(); for (const auto& channel: channels) @@ -60,8 +58,6 @@ namespace Resource // Keywords can be stuff like Loop, Equip, Unequip, Block, InventoryHandtoHand, InventoryWeaponOneHand, PickProbe, Slash, Thrust, Chop... even "Slash Small Follow" mTarget.mTextKeys.emplace(startTime, std::move(start)); mTarget.mTextKeys.emplace(stopTime, std::move(stop)); - mTarget.mTextKeys.emplace(startTime, std::move(loopstart)); - mTarget.mTextKeys.emplace(stopTime, std::move(loopstop)); SceneUtil::EmulatedAnimation emulatedAnimation; emulatedAnimation.mStartTime = startTime; From 6c1f6169c0e191dd2a6c6414f484e031470a0e96 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Tue, 15 Dec 2020 13:50:19 +0200 Subject: [PATCH 39/51] Fix root movement glitch --- components/sceneutil/osgacontroller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sceneutil/osgacontroller.cpp b/components/sceneutil/osgacontroller.cpp index d77b8d666..f0eb7da6f 100644 --- a/components/sceneutil/osgacontroller.cpp +++ b/components/sceneutil/osgacontroller.cpp @@ -117,7 +117,7 @@ namespace SceneUtil //Find the correct animation based on time for (const EmulatedAnimation& emulatedAnimation : mEmulatedAnimations) { - if (time > emulatedAnimation.mStartTime && time < emulatedAnimation.mStopTime) + if (time >= emulatedAnimation.mStartTime && time <= emulatedAnimation.mStopTime) { newTime = time - emulatedAnimation.mStartTime; animationName = emulatedAnimation.mName; From 8b2bf12e8fa55327ac9896c741ef366993a87570 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Tue, 15 Dec 2020 13:51:49 +0200 Subject: [PATCH 40/51] Use bip01 for root bone name --- apps/openmw/mwrender/animation.cpp | 2 -- components/resource/keyframemanager.cpp | 2 +- components/sceneutil/osgacontroller.cpp | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8c9f5f493..f8ff3780d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -782,8 +782,6 @@ namespace MWRender NodeMap::const_iterator found = nodeMap.find("bip01"); if (found == nodeMap.end()) found = nodeMap.find("root bone"); - if (found == nodeMap.end()) - found = nodeMap.find("root"); if (found != nodeMap.end()) mAccumRoot = found->second; diff --git a/components/resource/keyframemanager.cpp b/components/resource/keyframemanager.cpp index 0ffe59712..d739392e8 100644 --- a/components/resource/keyframemanager.cpp +++ b/components/resource/keyframemanager.cpp @@ -21,7 +21,7 @@ namespace Resource void RetrieveAnimationsVisitor::apply(osg::Node& node) { - if (node.libraryName() == std::string("osgAnimation") && node.className() == std::string("Bone") && node.getName() == std::string("root")) + if (node.libraryName() == std::string("osgAnimation") && node.className() == std::string("Bone") && node.getName() == std::string("bip01")) { osg::ref_ptr callback = new SceneUtil::OsgAnimationController(); diff --git a/components/sceneutil/osgacontroller.cpp b/components/sceneutil/osgacontroller.cpp index f0eb7da6f..37aa525af 100644 --- a/components/sceneutil/osgacontroller.cpp +++ b/components/sceneutil/osgacontroller.cpp @@ -83,7 +83,7 @@ namespace SceneUtil { osgAnimation::UpdateMatrixTransform* umt = dynamic_cast(cb); if (umt) - if (node.getName() != "root") link(umt); + if (node.getName() != "bip01") link(umt); cb = cb->getNestedCallback(); } @@ -133,7 +133,7 @@ namespace SceneUtil for (const auto& channel: channels) { - if (channel->getTargetName() != "root" || channel->getName() != "transform") continue; + if (channel->getTargetName() != "bip01" || channel->getName() != "transform") continue; if ( osgAnimation::MatrixLinearSampler* templateSampler = dynamic_cast (channel->getSampler()) ) { From 259d52280088c50f6399f55549f7db8b91d98277 Mon Sep 17 00:00:00 2001 From: fredzio Date: Tue, 15 Dec 2020 21:34:58 +0100 Subject: [PATCH 41/51] When doing a ray cast to the next projectile position, ignore collisions that occurs with the projectile own collision object. Otherwise a magic bolt can explode "spontaneously". --- apps/openmw/mwphysics/closestnotmerayresultcallback.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp b/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp index 422ca78bd..f9dc3bdd5 100644 --- a/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp +++ b/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp @@ -24,6 +24,10 @@ namespace MWPhysics { if (rayResult.m_collisionObject == mMe) return 1.f; + + if (mProjectile && rayResult.m_collisionObject == mProjectile->getCollisionObject()) + return 1.f; + if (!mTargets.empty()) { if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end())) From 1e6156e04ae35dbe04dca932ff61a3dc61c9c9a4 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Wed, 16 Dec 2020 01:06:05 +0300 Subject: [PATCH 42/51] Turn all NIF records into structs --- components/nif/base.hpp | 9 +++----- components/nif/controlled.hpp | 21 ++++++----------- components/nif/controller.hpp | 31 +++++++++---------------- components/nif/data.hpp | 42 ++++++++++++---------------------- components/nif/extra.hpp | 9 +++----- components/nif/node.hpp | 3 +-- components/nif/property.hpp | 12 ++++------ components/nif/recordptr.hpp | 34 +++++++++++++-------------- components/nifosg/particle.hpp | 8 +++---- 9 files changed, 65 insertions(+), 104 deletions(-) diff --git a/components/nif/base.hpp b/components/nif/base.hpp index 711272abb..022e5224a 100644 --- a/components/nif/base.hpp +++ b/components/nif/base.hpp @@ -11,9 +11,8 @@ namespace Nif { // An extra data record. All the extra data connected to an object form a linked list. -class Extra : public Record +struct Extra : public Record { -public: std::string name; ExtraPtr next; // Next extra data record in the list @@ -31,9 +30,8 @@ public: void post(NIFFile *nif) override { next.post(nif); } }; -class Controller : public Record +struct Controller : public Record { -public: ControllerPtr next; int flags; float frequency, phase; @@ -45,9 +43,8 @@ public: }; /// Has name, extra-data and controller -class Named : public Record +struct Named : public Record { -public: std::string name; ExtraPtr extra; ExtraList extralist; diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index ac48e5049..0d93381ea 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -29,9 +29,8 @@ namespace Nif { -class NiSourceTexture : public Named +struct NiSourceTexture : public Named { -public: // Is this an external (references a separate texture file) or // internal (data is inside the nif itself) texture? bool external; @@ -93,27 +92,24 @@ struct NiParticleModifier : public Record void post(NIFFile *nif) override; }; -class NiParticleGrowFade : public NiParticleModifier +struct NiParticleGrowFade : public NiParticleModifier { -public: float growTime; float fadeTime; void read(NIFStream *nif) override; }; -class NiParticleColorModifier : public NiParticleModifier +struct NiParticleColorModifier : public NiParticleModifier { -public: NiColorDataPtr data; void read(NIFStream *nif) override; void post(NIFFile *nif) override; }; -class NiGravity : public NiParticleModifier +struct NiGravity : public NiParticleModifier { -public: float mForce; /* 0 - Wind (fixed direction) * 1 - Point (fixed origin) @@ -133,27 +129,24 @@ struct NiParticleCollider : public NiParticleModifier }; // NiPinaColada -class NiPlanarCollider : public NiParticleCollider +struct NiPlanarCollider : public NiParticleCollider { -public: void read(NIFStream *nif) override; osg::Vec3f mPlaneNormal; float mPlaneDistance; }; -class NiSphericalCollider : public NiParticleCollider +struct NiSphericalCollider : public NiParticleCollider { -public: float mRadius; osg::Vec3f mCenter; void read(NIFStream *nif) override; }; -class NiParticleRotation : public NiParticleModifier +struct NiParticleRotation : public NiParticleModifier { -public: void read(NIFStream *nif) override; }; diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 4016188ce..503710fe9 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -29,9 +29,8 @@ namespace Nif { -class NiParticleSystemController : public Controller +struct NiParticleSystemController : public Controller { -public: struct Particle { osg::Vec3f velocity; float lifetime; @@ -80,9 +79,8 @@ public: }; using NiBSPArrayController = NiParticleSystemController; -class NiMaterialColorController : public Controller +struct NiMaterialColorController : public Controller { -public: NiPoint3InterpolatorPtr interpolator; NiPosDataPtr data; unsigned int targetColor; @@ -91,9 +89,8 @@ public: void post(NIFFile *nif) override; }; -class NiPathController : public Controller +struct NiPathController : public Controller { -public: NiPosDataPtr posData; NiFloatDataPtr floatData; @@ -115,9 +112,8 @@ public: void post(NIFFile *nif) override; }; -class NiLookAtController : public Controller +struct NiLookAtController : public Controller { -public: NodePtr target; unsigned short lookAtFlags{0}; @@ -125,9 +121,8 @@ public: void post(NIFFile *nif) override; }; -class NiUVController : public Controller +struct NiUVController : public Controller { -public: NiUVDataPtr data; unsigned int uvSet; @@ -135,9 +130,8 @@ public: void post(NIFFile *nif) override; }; -class NiKeyframeController : public Controller +struct NiKeyframeController : public Controller { -public: NiKeyframeDataPtr data; NiTransformInterpolatorPtr interpolator; @@ -154,12 +148,11 @@ struct NiFloatInterpController : public Controller void post(NIFFile *nif) override; }; -class NiAlphaController : public NiFloatInterpController { }; -class NiRollController : public NiFloatInterpController { }; +struct NiAlphaController : public NiFloatInterpController { }; +struct NiRollController : public NiFloatInterpController { }; -class NiGeomMorpherController : public Controller +struct NiGeomMorpherController : public Controller { -public: NiMorphDataPtr data; NiFloatInterpolatorList interpolators; @@ -167,18 +160,16 @@ public: void post(NIFFile *nif) override; }; -class NiVisController : public Controller +struct NiVisController : public Controller { -public: NiVisDataPtr data; void read(NIFStream *nif) override; void post(NIFFile *nif) override; }; -class NiFlipController : public Controller +struct NiFlipController : public Controller { -public: NiFloatInterpolatorPtr mInterpolator; int mTexSlot; // NiTexturingProperty::TextureType float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 908313f50..66a391afc 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -32,9 +32,8 @@ namespace Nif { // Common ancestor for several data classes -class NiGeometryData : public Record +struct NiGeometryData : public Record { -public: std::vector vertices, normals, tangents, bitangents; std::vector colors; std::vector< std::vector > uvlist; @@ -44,18 +43,16 @@ public: void read(NIFStream *nif) override; }; -class NiTriShapeData : public NiGeometryData +struct NiTriShapeData : public NiGeometryData { -public: // Triangles, three vertex indices per triangle std::vector triangles; void read(NIFStream *nif) override; }; -class NiTriStripsData : public NiGeometryData +struct NiTriStripsData : public NiGeometryData { -public: // Triangle strips, series of vertex indices. std::vector> strips; @@ -70,9 +67,8 @@ struct NiLinesData : public NiGeometryData void read(NIFStream *nif) override; }; -class NiParticlesData : public NiGeometryData +struct NiParticlesData : public NiGeometryData { -public: int numParticles{0}; int activeCount; @@ -84,39 +80,34 @@ public: void read(NIFStream *nif) override; }; -class NiRotatingParticlesData : public NiParticlesData +struct NiRotatingParticlesData : public NiParticlesData { -public: void read(NIFStream *nif) override; }; -class NiPosData : public Record +struct NiPosData : public Record { -public: Vector3KeyMapPtr mKeyList; void read(NIFStream *nif) override; }; -class NiUVData : public Record +struct NiUVData : public Record { -public: FloatKeyMapPtr mKeyList[4]; void read(NIFStream *nif) override; }; -class NiFloatData : public Record +struct NiFloatData : public Record { -public: FloatKeyMapPtr mKeyList; void read(NIFStream *nif) override; }; -class NiPixelData : public Record +struct NiPixelData : public Record { -public: enum Format { NIPXFMT_RGB8, @@ -150,17 +141,15 @@ public: void post(NIFFile *nif) override; }; -class NiColorData : public Record +struct NiColorData : public Record { -public: Vector4KeyMapPtr mKeyMap; void read(NIFStream *nif) override; }; -class NiVisData : public Record +struct NiVisData : public Record { -public: struct VisData { float time; bool isSet; @@ -170,9 +159,8 @@ public: void read(NIFStream *nif) override; }; -class NiSkinInstance : public Record +struct NiSkinInstance : public Record { -public: NiSkinDataPtr data; NiSkinPartitionPtr partitions; NodePtr root; @@ -182,9 +170,8 @@ public: void post(NIFFile *nif) override; }; -class NiSkinData : public Record +struct NiSkinData : public Record { -public: struct VertWeight { unsigned short vertex; @@ -251,9 +238,8 @@ struct NiKeyframeData : public Record void read(NIFStream *nif) override; }; -class NiPalette : public Record +struct NiPalette : public Record { -public: // 32-bit RGBA colors that correspond to 8-bit indices std::vector colors; diff --git a/components/nif/extra.hpp b/components/nif/extra.hpp index 5d7aa0c3b..6d345a18e 100644 --- a/components/nif/extra.hpp +++ b/components/nif/extra.hpp @@ -29,15 +29,13 @@ namespace Nif { -class NiVertWeightsExtraData : public Extra +struct NiVertWeightsExtraData : public Extra { -public: void read(NIFStream *nif) override; }; -class NiTextKeyExtraData : public Extra +struct NiTextKeyExtraData : public Extra { -public: struct TextKey { float time; @@ -48,9 +46,8 @@ public: void read(NIFStream *nif) override; }; -class NiStringExtraData : public Extra +struct NiStringExtraData : public Extra { -public: /* Two known meanings: "MRK" - marker, only visible in the editor, not rendered in-game "NCO" - no collision diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 08f066e36..1d082b8f9 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -131,9 +131,8 @@ struct NiBoundingVolume parent node (unless it's the root), and transformation (location and rotation) relative to it's parent. */ -class Node : public Named +struct Node : public Named { -public: // Node flags. Interpretation depends somewhat on the type of node. unsigned int flags; Transformation trafo; diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 813a1888d..eccb442f7 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -29,11 +29,10 @@ namespace Nif { -class Property : public Named { }; +struct Property : public Named { }; -class NiTexturingProperty : public Property +struct NiTexturingProperty : public Property { -public: unsigned short flags{0u}; // A sub-texture @@ -96,9 +95,8 @@ public: void post(NIFFile *nif) override; }; -class NiFogProperty : public Property +struct NiFogProperty : public Property { -public: unsigned short mFlags; float mFogDepth; osg::Vec3f mColour; @@ -307,8 +305,8 @@ struct S_StencilProperty void read(NIFStream *nif); }; -class NiAlphaProperty : public StructPropT { }; -class NiVertexColorProperty : public StructPropT { }; +struct NiAlphaProperty : public StructPropT { }; +struct NiVertexColorProperty : public StructPropT { }; struct NiStencilProperty : public Property { S_StencilProperty data; diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 4792895e2..b30d99fbe 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -120,24 +120,24 @@ public: }; -class Node; -class Extra; -class Property; -class NiUVData; -class NiPosData; -class NiVisData; -class Controller; -class Named; -class NiSkinData; -class NiFloatData; +struct Node; +struct Extra; +struct Property; +struct NiUVData; +struct NiPosData; +struct NiVisData; +struct Controller; +struct Named; +struct NiSkinData; +struct NiFloatData; struct NiMorphData; -class NiPixelData; -class NiColorData; +struct NiPixelData; +struct NiColorData; struct NiKeyframeData; -class NiTriStripsData; -class NiSkinInstance; -class NiSourceTexture; -class NiPalette; +struct NiTriStripsData; +struct NiSkinInstance; +struct NiSourceTexture; +struct NiPalette; struct NiParticleModifier; struct NiBoolData; struct NiSkinPartition; @@ -147,7 +147,7 @@ struct NiTransformInterpolator; struct BSShaderTextureSet; struct NiGeometryData; struct BSShaderProperty; -class NiAlphaProperty; +struct NiAlphaProperty; using NodePtr = RecordPtrT; using ExtraPtr = RecordPtrT; diff --git a/components/nifosg/particle.hpp b/components/nifosg/particle.hpp index dd89f4501..b0a46f0ca 100644 --- a/components/nifosg/particle.hpp +++ b/components/nifosg/particle.hpp @@ -14,10 +14,10 @@ namespace Nif { - class NiGravity; - class NiPlanarCollider; - class NiSphericalCollider; - class NiColorData; + struct NiGravity; + struct NiPlanarCollider; + struct NiSphericalCollider; + struct NiColorData; } namespace NifOsg From 9f81dcbd1afbd1904254e3c7a8f27a6bc22be8a8 Mon Sep 17 00:00:00 2001 From: psi29a Date: Wed, 16 Dec 2020 13:21:00 +0000 Subject: [PATCH 43/51] Per request via PM: "I would like to request that my name is removed from the teams page as my contributions has all been replaced by better solutions years ago." by vorenon, Manuel Edelmann --- AUTHORS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/AUTHORS.md b/AUTHORS.md index e8134a681..e6ff67293 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -123,7 +123,6 @@ Programmers Lordrea Łukasz Gołębiewski (lukago) Lukasz Gromanowski (lgro) - Manuel Edelmann (vorenon) Marc Bouvier (CramitDeFrog) Marcin Hulist (Gohan) Mark Siewert (mark76) From 640420eaaa61b38307b16aa6e5b14180f26bc4b8 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Wed, 16 Dec 2020 21:46:52 +0100 Subject: [PATCH 44/51] No longer delete and recreate CopyFramebufferToTextureCallback, remove oneshot functionality. Instead just remove the callback after the traversals. Use addInitialDrawCallback instead of setInitialDrawCallback to avoid messing with existing callbacks. --- apps/openmw/mwgui/loadingscreen.cpp | 17 +++++++++-------- apps/openmw/mwgui/loadingscreen.hpp | 2 ++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index cd0384bb0..2e2de578e 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -137,15 +137,11 @@ namespace MWGui public: CopyFramebufferToTextureCallback(osg::Texture2D* texture) : mTexture(texture) - , oneshot(true) { } void operator () (osg::RenderInfo& renderInfo) const override { - if (!oneshot) - return; - oneshot = false; int w = renderInfo.getCurrentCamera()->getViewport()->width(); int h = renderInfo.getCurrentCamera()->getViewport()->height(); mTexture->copyTexImage2D(*renderInfo.getState(), 0, 0, w, h); @@ -153,7 +149,6 @@ namespace MWGui private: osg::ref_ptr mTexture; - mutable bool oneshot; }; class DontComputeBoundCallback : public osg::Node::ComputeBoundingSphereCallback @@ -322,9 +317,12 @@ namespace MWGui mGuiTexture.reset(new osgMyGUI::OSGTexture(mTexture)); } - // Notice that the next time this is called, the current CopyFramebufferToTextureCallback will be deleted - // so there's no memory leak as at most one object of type CopyFramebufferToTextureCallback is allocated at a time. - mViewer->getCamera()->setInitialDrawCallback(new CopyFramebufferToTextureCallback(mTexture)); + if (!mCopyFramebufferToTextureCallback) + { + mCopyFramebufferToTextureCallback = new CopyFramebufferToTextureCallback(mTexture); + } + + mViewer->getCamera()->addInitialDrawCallback(mCopyFramebufferToTextureCallback); mBackgroundImage->setBackgroundImage(""); mBackgroundImage->setVisible(false); @@ -367,6 +365,9 @@ namespace MWGui mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); + if(mCopyFramebufferToTextureCallback) + mViewer->getCamera()->removeInitialDrawCallback(mCopyFramebufferToTextureCallback); + mLastRenderTime = mTimer.time_m(); } diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 2577827aa..ac911ab60 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -28,6 +28,7 @@ namespace Resource namespace MWGui { class BackgroundImage; + class CopyFramebufferToTextureCallback; class LoadingScreen : public WindowBase, public Loading::Listener { @@ -84,6 +85,7 @@ namespace MWGui std::vector mSplashScreens; osg::ref_ptr mTexture; + osg::ref_ptr mCopyFramebufferToTextureCallback; std::unique_ptr mGuiTexture; void changeWallpaper(); From bc961a13f54842aa1dd75a459fabfe98ccce2cad Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Wed, 16 Dec 2020 22:03:16 +0100 Subject: [PATCH 45/51] avoid redundant calls to removeInitialDrawCallback --- apps/openmw/mwgui/loadingscreen.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 2e2de578e..20586ef4a 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -365,8 +365,11 @@ namespace MWGui mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); - if(mCopyFramebufferToTextureCallback) + if (mCopyFramebufferToTextureCallback) + { mViewer->getCamera()->removeInitialDrawCallback(mCopyFramebufferToTextureCallback); + mCopyFramebufferToTextureCallback = nullptr; + } mLastRenderTime = mTimer.time_m(); } From 46ec40fa9291c8cf763f21906c6197cd2cf11a69 Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Fri, 18 Dec 2020 00:20:40 +0300 Subject: [PATCH 46/51] Make sure NIFLoader avoids working further with empty geometry --- components/nifosg/nifloader.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 5f68b1229..902f15fb3 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1245,9 +1245,12 @@ namespace NifOsg void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { assert(isTypeGeometry(nifNode->recType)); - osg::ref_ptr drawable; osg::ref_ptr geom (new osg::Geometry); handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags); + // If the record had no valid geometry data in it, early-out + if (geom->empty()) + return; + osg::ref_ptr drawable; for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) @@ -1292,6 +1295,8 @@ namespace NifOsg assert(isTypeGeometry(nifNode->recType)); osg::ref_ptr geometry (new osg::Geometry); handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags); + if (geometry->empty()) + return; osg::ref_ptr rig(new SceneUtil::RigGeometry); rig->setSourceGeometry(geometry); rig->setName(nifNode->name); From 96a87b582c41f2c83a373a019a776a5ec16f1b6e Mon Sep 17 00:00:00 2001 From: psi29a Date: Thu, 17 Dec 2020 22:51:04 +0000 Subject: [PATCH 47/51] Merge branch 'loadingScreen_initialdrawcallback' into 'master' Fix for !472 for older versions of OSG See merge request OpenMW/openmw!474 (cherry picked from commit 35e25d79b9a6c76807084be5ca2584c4fd9b9c35) 4447dd41 osg versions 06f4d63b Preserve callback in older osg version --- apps/openmw/mwgui/loadingscreen.cpp | 14 ++++++++++++++ apps/openmw/mwgui/loadingscreen.hpp | 2 ++ 2 files changed, 16 insertions(+) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 20586ef4a..9ab5e32a3 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -43,6 +44,7 @@ namespace MWGui , mNestedLoadingCount(0) , mProgress(0) , mShowWallpaper(true) + , mOldCallback(nullptr) { mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); @@ -322,7 +324,13 @@ namespace MWGui mCopyFramebufferToTextureCallback = new CopyFramebufferToTextureCallback(mTexture); } +#if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 10) mViewer->getCamera()->addInitialDrawCallback(mCopyFramebufferToTextureCallback); +#else + // TODO: Remove once we officially end support for OSG versions pre 3.5.10 + mOldCallback = mViewer->getCamera()->getInitialDrawCallback(); + mViewer->getCamera()->setInitialDrawCallback(mCopyFramebufferToTextureCallback); +#endif mBackgroundImage->setBackgroundImage(""); mBackgroundImage->setVisible(false); @@ -367,7 +375,13 @@ namespace MWGui if (mCopyFramebufferToTextureCallback) { + +#if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 10) mViewer->getCamera()->removeInitialDrawCallback(mCopyFramebufferToTextureCallback); +#else + // TODO: Remove once we officially end support for OSG versions pre 3.5.10 + mViewer->getCamera()->setInitialDrawCallback(mOldCallback); +#endif mCopyFramebufferToTextureCallback = nullptr; } diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index ac911ab60..d58899eae 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -3,6 +3,7 @@ #include +#include #include #include @@ -86,6 +87,7 @@ namespace MWGui osg::ref_ptr mTexture; osg::ref_ptr mCopyFramebufferToTextureCallback; + osg::ref_ptr mOldCallback; std::unique_ptr mGuiTexture; void changeWallpaper(); From 7bae6691b63878e76777ec497a1fe14bb00eb48c Mon Sep 17 00:00:00 2001 From: fredzio Date: Fri, 18 Dec 2020 08:18:07 +0100 Subject: [PATCH 48/51] Introduce World::moveObjectBy() function to translate an object relatively to its current position. Use it in relevant MWScripts opcode (move and moveworld). Remove the fragile detection of scripted translation from PhysicsTaskScheduler. No user visible change, just a more robust mechanism. --- apps/openmw/mwbase/world.hpp | 3 +++ apps/openmw/mwphysics/actor.cpp | 17 ++++++++++++----- apps/openmw/mwphysics/actor.hpp | 5 ++++- apps/openmw/mwphysics/mtphysics.cpp | 13 +------------ .../mwscript/transformationextensions.cpp | 19 ++++++------------- apps/openmw/mwworld/worldimp.cpp | 12 ++++++++++++ apps/openmw/mwworld/worldimp.hpp | 3 +++ 7 files changed, 41 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d4f1d2f8a..619bbbdf5 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -286,6 +286,9 @@ namespace MWBase virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0; ///< @return an updated Ptr + virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec) = 0; + ///< @return an updated Ptr + virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; virtual void rotateObject(const MWWorld::Ptr& ptr, float x, float y, float z, diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index b1f219e33..e7fe93b35 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -120,6 +120,8 @@ int Actor::getCollisionMask() const void Actor::updatePositionUnsafe() { + if (!mWorldPositionChanged && mWorldPosition != mPtr.getRefData().getPosition().asVec3()) + mWorldPositionChanged = true; mWorldPosition = mPtr.getRefData().getPosition().asVec3(); } @@ -153,6 +155,7 @@ void Actor::updateCollisionObjectPositionUnsafe() mLocalTransform.setOrigin(Misc::Convert::toBullet(newPosition)); mLocalTransform.setRotation(Misc::Convert::toBullet(mRotation)); mCollisionObject->setWorldTransform(mLocalTransform); + mWorldPositionChanged = false; } void Actor::updateCollisionObjectPosition() @@ -167,18 +170,20 @@ osg::Vec3f Actor::getCollisionObjectPosition() const return Misc::Convert::toOsg(mLocalTransform.getOrigin()); } -void Actor::setPosition(const osg::Vec3f& position) +bool Actor::setPosition(const osg::Vec3f& position) { std::scoped_lock lock(mPositionMutex); - mPreviousPosition = mPosition; - mPosition = position; + bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged; + mPreviousPosition = mPosition + mPositionOffset; + mPosition = position + mPositionOffset; + mPositionOffset = osg::Vec3f(); + return hasChanged; } void Actor::adjustPosition(const osg::Vec3f& offset) { std::scoped_lock lock(mPositionMutex); - mPosition += offset; - mPreviousPosition += offset; + mPositionOffset += offset; } void Actor::resetPosition() @@ -189,6 +194,8 @@ void Actor::resetPosition() mPosition = mWorldPosition; mSimulationPosition = mWorldPosition; updateCollisionObjectPositionUnsafe(); + mStandingOnPtr = nullptr; + mWorldPositionChanged = false; } osg::Vec3f Actor::getPosition() const diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 07a9bebd2..7c8ea0b73 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -90,8 +90,9 @@ namespace MWPhysics /** * Store the current position into mPreviousPosition, then move to this position. + * Returns true if the new position is different. */ - void setPosition(const osg::Vec3f& position); + bool setPosition(const osg::Vec3f& position); void resetPosition(); void adjustPosition(const osg::Vec3f& offset); @@ -177,6 +178,8 @@ namespace MWPhysics osg::Vec3f mSimulationPosition; osg::Vec3f mPosition; osg::Vec3f mPreviousPosition; + osg::Vec3f mPositionOffset; + bool mWorldPositionChanged; btTransform mLocalTransform; mutable std::mutex mPositionMutex; diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index a78a30788..95c61edc7 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -100,15 +100,6 @@ namespace osg::Vec3f interpolateMovements(MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt) { const float interpolationFactor = timeAccum / physicsDt; - - // account for force change of actor's position in the main thread - const auto correction = actorData.mActorRaw->getWorldPosition() - actorData.mOrigin; - if (correction.length() != 0) - { - actorData.mActorRaw->adjustPosition(correction); - actorData.mPosition = actorData.mActorRaw->getPosition(); - } - return actorData.mPosition * interpolationFactor + actorData.mActorRaw->getPreviousPosition() * (1.f - interpolationFactor); } @@ -511,9 +502,7 @@ namespace MWPhysics { if(const auto actor = actorData.mActor.lock()) { - bool positionChanged = actorData.mPosition != actorData.mActorRaw->getPosition(); - actorData.mActorRaw->setPosition(actorData.mPosition); - if (positionChanged) + if (actor->setPosition(actorData.mPosition)) { actor->updateCollisionObjectPosition(); mCollisionWorld->updateSingleAabb(actor->getCollisionObject()); diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index a908940ed..103c6629d 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -32,11 +32,7 @@ namespace MWScript std::vector actors; MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors); for (auto& actor : actors) - { - osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); - actorPos += diff; - MWBase::Environment::get().getWorld()->moveObject(actor, actorPos.x(), actorPos.y(), actorPos.z()); - } + MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff); } template @@ -727,14 +723,12 @@ namespace MWScript return; osg::Vec3f diff = ptr.getRefData().getBaseNode()->getAttitude() * posChange; - osg::Vec3f worldPos(ptr.getRefData().getPosition().asVec3()); - worldPos += diff; // We should move actors, standing on moving object, too. // This approach can be used to create elevators. moveStandingActors(ptr, diff); dynamic_cast(runtime.getContext()).updatePtr(ptr, - MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x(), worldPos.y(), worldPos.z())); + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff)); } }; @@ -755,15 +749,14 @@ namespace MWScript Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); runtime.pop(); - const float *objPos = ptr.getRefData().getPosition().pos; osg::Vec3f diff; if (axis == "x") - diff.x() += movement; + diff.x() = movement; else if (axis == "y") - diff.y() += movement; + diff.y() = movement; else if (axis == "z") - diff.z() += movement; + diff.z() = movement; else return; @@ -771,7 +764,7 @@ namespace MWScript // This approach can be used to create elevators. moveStandingActors(ptr, diff); dynamic_cast(runtime.getContext()).updatePtr(ptr, - MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z())); + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff)); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3756ea04b..3086fed83 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1251,6 +1251,18 @@ namespace MWWorld return moveObjectImp(ptr, x, y, z, true, moveToActive); } + MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec) + { + auto* actor = mPhysics->getActor(ptr); + if (actor) + { + actor->adjustPosition(vec); + return ptr; + } + osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec; + return moveObject(ptr, newpos.x(), newpos.y(), newpos.z()); + } + void World::scaleObject (const Ptr& ptr, float scale) { if (mPhysics->getActor(ptr)) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1fc5ebc20..75717b16b 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -380,6 +380,9 @@ namespace MWWorld MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override; ///< @return an updated Ptr + MWWorld::Ptr moveObjectBy(const Ptr& ptr, osg::Vec3f vec) override; + ///< @return an updated Ptr + void scaleObject (const Ptr& ptr, float scale) override; /// World rotates object, uses radians From ea8f98b33960f1d8b3e05900362f9294b93b1686 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Fri, 18 Dec 2020 08:48:39 +0000 Subject: [PATCH 49/51] Wait for initialDrawCallback to finish before removing it --- apps/openmw/mwgui/loadingscreen.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 9ab5e32a3..5f0e9f33a 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -1,6 +1,7 @@ #include "loadingscreen.hpp" #include +#include #include @@ -139,6 +140,7 @@ namespace MWGui public: CopyFramebufferToTextureCallback(osg::Texture2D* texture) : mTexture(texture) + , mOneshot(true) { } @@ -147,9 +149,25 @@ namespace MWGui int w = renderInfo.getCurrentCamera()->getViewport()->width(); int h = renderInfo.getCurrentCamera()->getViewport()->height(); mTexture->copyTexImage2D(*renderInfo.getState(), 0, 0, w, h); + + { + std::unique_lock lock(mMutex); + mOneshot = false; + } + mSignal.notify_all(); + } + + void wait() + { + std::unique_lock lock(mMutex); + while (mOneshot) + mSignal.wait(lock); } private: + mutable bool mOneshot; + mutable std::mutex mMutex; + mutable std::condition_variable mSignal; osg::ref_ptr mTexture; }; @@ -375,7 +393,7 @@ namespace MWGui if (mCopyFramebufferToTextureCallback) { - + mCopyFramebufferToTextureCallback->wait(); #if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 10) mViewer->getCamera()->removeInitialDrawCallback(mCopyFramebufferToTextureCallback); #else From 4e7c9b66960258bfb7e51d71ea79130ca3758581 Mon Sep 17 00:00:00 2001 From: fredzio Date: Fri, 18 Dec 2020 09:22:02 +0100 Subject: [PATCH 50/51] Embed physics simulation results inside of actor class. This gives finer control over reseting positions (switch off tcl is no longer glitchy) and solve most of the erroneous usage of stale World::Ptr indicated by: "Error in frame: moveTo: object is not in this cell" --- apps/openmw/mwphysics/actor.cpp | 7 ++- apps/openmw/mwphysics/actor.hpp | 1 + apps/openmw/mwphysics/mtphysics.cpp | 84 ++++++++++--------------- apps/openmw/mwphysics/mtphysics.hpp | 9 ++- apps/openmw/mwphysics/physicssystem.cpp | 18 +++++- apps/openmw/mwphysics/physicssystem.hpp | 6 +- apps/openmw/mwworld/worldimp.cpp | 23 ++++--- 7 files changed, 73 insertions(+), 75 deletions(-) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index e7fe93b35..10fd463fb 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -76,6 +76,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic updateScale(); resetPosition(); addCollisionMask(getCollisionMask()); + updateCollisionObjectPosition(); } Actor::~Actor() @@ -139,7 +140,9 @@ osg::Vec3f Actor::getWorldPosition() const void Actor::setSimulationPosition(const osg::Vec3f& position) { - mSimulationPosition = position; + if (!mResetSimulation) + mSimulationPosition = position; + mResetSimulation = false; } osg::Vec3f Actor::getSimulationPosition() const @@ -193,9 +196,9 @@ void Actor::resetPosition() mPreviousPosition = mWorldPosition; mPosition = mWorldPosition; mSimulationPosition = mWorldPosition; - updateCollisionObjectPositionUnsafe(); mStandingOnPtr = nullptr; mWorldPositionChanged = false; + mResetSimulation = true; } osg::Vec3f Actor::getPosition() const diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 7c8ea0b73..b26b52d14 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -180,6 +180,7 @@ namespace MWPhysics osg::Vec3f mPreviousPosition; osg::Vec3f mPositionOffset; bool mWorldPositionChanged; + bool mResetSimulation; btTransform mLocalTransform; mutable std::mutex mPositionMutex; diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index 95c61edc7..60a7259f8 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -204,31 +204,31 @@ namespace MWPhysics thread.join(); } - const PtrPositionList& PhysicsTaskScheduler::moveActors(int numSteps, float timeAccum, std::vector&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) + const std::vector& PhysicsTaskScheduler::moveActors(int numSteps, float timeAccum, std::vector&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) { // This function run in the main thread. // While the mSimulationMutex is held, background physics threads can't run. std::unique_lock lock(mSimulationMutex); - for (auto& data : actorsData) - data.updatePosition(); + mMovedActors.clear(); // start by finishing previous background computation if (mNumThreads != 0) { for (auto& data : mActorsFrameData) { - // Ignore actors that were deleted while the background thread was running - if (!data.mActor.lock()) - continue; - - updateMechanics(data); - if (mAdvanceSimulation) - data.mActorRaw->setStandingOnPtr(data.mStandingOn); + // Only return actors that are still part of the scene + if (std::any_of(actorsData.begin(), actorsData.end(), [&data](const auto& newFrameData) { return data.mActorRaw->getPtr() == newFrameData.mActorRaw->getPtr(); })) + { + updateMechanics(data); - if (mMovementResults.find(data.mPtr) != mMovementResults.end()) - data.mActorRaw->setSimulationPosition(mMovementResults[data.mPtr]); + // these variables are accessed directly from the main thread, update them here to prevent accessing "too new" values + if (mAdvanceSimulation) + data.mActorRaw->setStandingOnPtr(data.mStandingOn); + data.mActorRaw->setSimulationPosition(interpolateMovements(data, mTimeAccum, mPhysicsDt)); + mMovedActors.emplace_back(data.mActorRaw->getPtr()); + } } if (mFrameNumber == frameNumber - 1) @@ -243,6 +243,8 @@ namespace MWPhysics } // init + for (auto& data : actorsData) + data.updatePosition(); mRemainingSteps = numSteps; mTimeAccum = timeAccum; mActorsFrameData = std::move(actorsData); @@ -257,52 +259,28 @@ namespace MWPhysics if (mNumThreads == 0) { - mMovementResults.clear(); syncComputation(); - - for (auto& data : mActorsFrameData) - { - if (mAdvanceSimulation) - data.mActorRaw->setStandingOnPtr(data.mStandingOn); - if (mMovementResults.find(data.mPtr) != mMovementResults.end()) - data.mActorRaw->setSimulationPosition(mMovementResults[data.mPtr]); - } - return mMovementResults; - } - - // Remove actors that were deleted while the background thread was running - for (auto& data : mActorsFrameData) - { - if (!data.mActor.lock()) - mMovementResults.erase(data.mPtr); + return mMovedActors; } - std::swap(mMovementResults, mPreviousMovementResults); - - // mMovementResults is shared between all workers instance - // pre-allocate all nodes so that we don't need synchronization - mMovementResults.clear(); - for (const auto& m : mActorsFrameData) - mMovementResults[m.mPtr] = m.mPosition; lock.unlock(); mHasJob.notify_all(); - return mPreviousMovementResults; + return mMovedActors; } - const PtrPositionList& PhysicsTaskScheduler::resetSimulation(const ActorMap& actors) + const std::vector& PhysicsTaskScheduler::resetSimulation(const ActorMap& actors) { std::unique_lock lock(mSimulationMutex); - mMovementResults.clear(); - mPreviousMovementResults.clear(); + mMovedActors.clear(); mActorsFrameData.clear(); - for (const auto& [_, actor] : actors) { actor->resetPosition(); - actor->setStandingOnPtr(nullptr); - mMovementResults[actor->getPtr()] = actor->getWorldPosition(); + actor->setSimulationPosition(actor->getWorldPosition()); // resetPosition skip next simulation, now we need to "consume" it + actor->updateCollisionObjectPosition(); + mMovedActors.emplace_back(actor->getPtr()); } - return mMovementResults; + return mMovedActors; } void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const @@ -370,17 +348,17 @@ namespace MWPhysics mCollisionWorld->removeCollisionObject(collisionObject); } - void PhysicsTaskScheduler::updateSingleAabb(std::weak_ptr ptr) + void PhysicsTaskScheduler::updateSingleAabb(std::weak_ptr ptr, bool immediate) { - if (mDeferAabbUpdate) + if (!mDeferAabbUpdate || immediate) { - std::unique_lock lock(mUpdateAabbMutex); - mUpdateAabb.insert(std::move(ptr)); + std::unique_lock lock(mCollisionWorldMutex); + updatePtrAabb(ptr); } else { - std::unique_lock lock(mCollisionWorldMutex); - updatePtrAabb(ptr); + std::unique_lock lock(mUpdateAabbMutex); + mUpdateAabb.insert(std::move(ptr)); } } @@ -484,7 +462,6 @@ namespace MWPhysics { auto& actorData = mActorsFrameData[job]; handleFall(actorData, mAdvanceSimulation); - mMovementResults[actorData.mPtr] = interpolateMovements(actorData, mTimeAccum, mPhysicsDt); } } @@ -539,8 +516,11 @@ namespace MWPhysics for (auto& actorData : mActorsFrameData) { handleFall(actorData, mAdvanceSimulation); - mMovementResults[actorData.mPtr] = interpolateMovements(actorData, mTimeAccum, mPhysicsDt); + actorData.mActorRaw->setSimulationPosition(interpolateMovements(actorData, mTimeAccum, mPhysicsDt)); updateMechanics(actorData); + mMovedActors.emplace_back(actorData.mActorRaw->getPtr()); + if (mAdvanceSimulation) + actorData.mActorRaw->setStandingOnPtr(actorData.mStandingOn); } } } diff --git a/apps/openmw/mwphysics/mtphysics.hpp b/apps/openmw/mwphysics/mtphysics.hpp index 3a761829b..90e1007b8 100644 --- a/apps/openmw/mwphysics/mtphysics.hpp +++ b/apps/openmw/mwphysics/mtphysics.hpp @@ -32,9 +32,9 @@ namespace MWPhysics /// @param timeAccum accumulated time from previous run to interpolate movements /// @param actorsData per actor data needed to compute new positions /// @return new position of each actor - const PtrPositionList& moveActors(int numSteps, float timeAccum, std::vector&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); + const std::vector& moveActors(int numSteps, float timeAccum, std::vector&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); - const PtrPositionList& resetSimulation(const ActorMap& actors); + const std::vector& resetSimulation(const ActorMap& actors); // Thread safe wrappers void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const; @@ -46,7 +46,7 @@ namespace MWPhysics void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask); void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask); void removeCollisionObject(btCollisionObject* collisionObject); - void updateSingleAabb(std::weak_ptr ptr); + void updateSingleAabb(std::weak_ptr ptr, bool immediate=false); bool getLineOfSight(const std::weak_ptr& actor1, const std::weak_ptr& actor2); private: @@ -60,8 +60,7 @@ namespace MWPhysics std::unique_ptr mWorldFrameData; std::vector mActorsFrameData; - PtrPositionList mMovementResults; - PtrPositionList mPreviousMovementResults; + std::vector mMovedActors; const float mPhysicsDt; float mTimeAccum; std::shared_ptr mCollisionWorld; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 151801e67..8b9cb0a79 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -437,7 +437,7 @@ namespace MWPhysics ActorMap::iterator found = mActors.find(ptr); if (found == mActors.end()) return ptr.getRefData().getPosition().asVec3(); - found->second->resetPosition(); + resetPosition(ptr); return MovementSolver::traceDown(ptr, position, found->second.get(), mCollisionWorld.get(), maxHeight); } @@ -647,6 +647,17 @@ namespace MWPhysics } } + void PhysicsSystem::resetPosition(const MWWorld::ConstPtr &ptr) + { + ActorMap::iterator foundActor = mActors.find(ptr); + if (foundActor != mActors.end()) + { + foundActor->second->resetPosition(); + mTaskScheduler->updateSingleAabb(foundActor->second, true); + return; + } + } + void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh) { osg::ref_ptr shape = mShapeManager->getShape(mesh); @@ -683,6 +694,8 @@ namespace MWPhysics if (found != mActors.end()) { bool cmode = found->second->getCollisionMode(); + if (cmode) + resetPosition(found->first); cmode = !cmode; found->second->enableCollisionMode(cmode); // NB: Collision body isn't disabled for vanilla TCL compatibility @@ -711,7 +724,7 @@ namespace MWPhysics mMovementQueue.clear(); } - const PtrPositionList& PhysicsSystem::applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) + const std::vector& PhysicsSystem::applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) { mTimeAccum += dt; @@ -930,7 +943,6 @@ namespace MWPhysics void ActorFrameData::updatePosition() { mActorRaw->updatePosition(); - mOrigin = mActorRaw->getSimulationPosition(); mPosition = mActorRaw->getPosition(); if (mMoveToWaterSurface) { diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index b33d829a4..8c7674991 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -50,8 +50,6 @@ class btVector3; namespace MWPhysics { - using PtrPositionList = std::map; - class HeightField; class Object; class Actor; @@ -99,7 +97,6 @@ namespace MWPhysics float mOldHeight; float mFallHeight; osg::Vec3f mMovement; - osg::Vec3f mOrigin; osg::Vec3f mPosition; ESM::Position mRefpos; }; @@ -147,6 +144,7 @@ namespace MWPhysics void updateScale (const MWWorld::Ptr& ptr); void updateRotation (const MWWorld::Ptr& ptr); void updatePosition (const MWWorld::Ptr& ptr); + void resetPosition(const MWWorld::ConstPtr &ptr); void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject); @@ -210,7 +208,7 @@ namespace MWPhysics void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); /// Apply all queued movements, then clear the list. - const PtrPositionList& applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); + const std::vector& applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); /// Clear the queued movements list without applying. void clearQueuedMovement(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3086fed83..c2ad6e22f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1356,12 +1356,7 @@ namespace MWWorld } moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z()); - if (ptr.getClass().isActor()) - { - MWPhysics::Actor* actor = mPhysics->getActor(ptr); - if (actor) - actor->resetPosition(); - } + mPhysics->resetPosition(ptr); } void World::fixPosition() @@ -1512,16 +1507,26 @@ namespace MWWorld mProjectileManager->processHits(); mDiscardMovements = false; - for(const auto& [actor, position]: results) + for(const auto& actor : results) { // Handle player last, in case a cell transition occurs if(actor != getPlayerPtr()) + { + auto* physactor = mPhysics->getActor(actor); + assert(physactor); + const auto position = physactor->getSimulationPosition(); moveObjectImp(actor, position.x(), position.y(), position.z(), false); + } } - const auto player = results.find(getPlayerPtr()); + const auto player = std::find(results.begin(), results.end(), getPlayerPtr()); if (player != results.end()) - moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false); + { + auto* physactor = mPhysics->getActor(*player); + assert(physactor); + const auto position = physactor->getSimulationPosition(); + moveObjectImp(*player, position.x(), position.y(), position.z(), false); + } } void World::updateNavigator() From eec2ba3623f0ad74e3f6747c9dcb959d28321edd Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Fri, 18 Dec 2020 18:15:14 +0000 Subject: [PATCH 51/51] Loading screen initialdrawcallback 4th time's the charm --- apps/openmw/mwgui/loadingscreen.cpp | 34 ++++++++++++++++++++++++----- apps/openmw/mwgui/loadingscreen.hpp | 1 + 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 5f0e9f33a..7ddc8c550 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -46,6 +46,7 @@ namespace MWGui , mProgress(0) , mShowWallpaper(true) , mOldCallback(nullptr) + , mHasCallback(false) { mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); @@ -139,13 +140,19 @@ namespace MWGui { public: CopyFramebufferToTextureCallback(osg::Texture2D* texture) - : mTexture(texture) - , mOneshot(true) + : mOneshot(true) + , mTexture(texture) { } void operator () (osg::RenderInfo& renderInfo) const override { + { + std::unique_lock lock(mMutex); + mOneshot = false; + } + mSignal.notify_all(); + int w = renderInfo.getCurrentCamera()->getViewport()->width(); int h = renderInfo.getCurrentCamera()->getViewport()->height(); mTexture->copyTexImage2D(*renderInfo.getState(), 0, 0, w, h); @@ -164,6 +171,18 @@ namespace MWGui mSignal.wait(lock); } + void waitUntilInvoked() + { + std::unique_lock lock(mMutex); + while (mOneshot) + mSignal.wait(lock); + } + + void reset() + { + mOneshot = true; + } + private: mutable bool mOneshot; mutable std::mutex mMutex; @@ -349,6 +368,8 @@ namespace MWGui mOldCallback = mViewer->getCamera()->getInitialDrawCallback(); mViewer->getCamera()->setInitialDrawCallback(mCopyFramebufferToTextureCallback); #endif + mCopyFramebufferToTextureCallback->reset(); + mHasCallback = true; mBackgroundImage->setBackgroundImage(""); mBackgroundImage->setVisible(false); @@ -391,16 +412,19 @@ namespace MWGui mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); - if (mCopyFramebufferToTextureCallback) + if (mHasCallback) { - mCopyFramebufferToTextureCallback->wait(); + mCopyFramebufferToTextureCallback->waitUntilInvoked(); + + // Note that we are removing the callback before the draw thread has returned from it. + // This is OK as we are retaining the ref_ptr. #if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 10) mViewer->getCamera()->removeInitialDrawCallback(mCopyFramebufferToTextureCallback); #else // TODO: Remove once we officially end support for OSG versions pre 3.5.10 mViewer->getCamera()->setInitialDrawCallback(mOldCallback); #endif - mCopyFramebufferToTextureCallback = nullptr; + mHasCallback = false; } mLastRenderTime = mTimer.time_m(); diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index d58899eae..5d86ed389 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -88,6 +88,7 @@ namespace MWGui osg::ref_ptr mTexture; osg::ref_ptr mCopyFramebufferToTextureCallback; osg::ref_ptr mOldCallback; + bool mHasCallback; std::unique_ptr mGuiTexture; void changeWallpaper();