From 94df2114c13024e592cf94907eb79a1f00900226 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 7 Mar 2020 10:31:00 +0400 Subject: [PATCH 001/227] Store fog of war as a PNG image instead of TGA (bug #5108) --- CHANGELOG.md | 1 + apps/openmw/mwgui/mapwindow.cpp | 1 + apps/openmw/mwrender/localmap.cpp | 10 +++--- components/esm/fogstate.cpp | 54 +++++++++++++++++++++++++++++++ components/esm/savedgame.cpp | 2 +- 5 files changed, 61 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bfd756b5..df17289bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -154,6 +154,7 @@ Bug #5104: Black Dart's enchantment doesn't trigger at low Enchant levels Bug #5105: NPCs start combat with werewolves from any distance Bug #5106: Still can jump even when encumbered + Bug #5108: Savegame bloating due to inefficient fog textures format Bug #5110: ModRegion with a redundant numerical argument breaks script execution Bug #5112: Insufficient magicka for current spell not reflected on HUD icon Bug #5113: Unknown alchemy question mark not centered diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index bc65ee021..372e0f64d 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -209,6 +209,7 @@ namespace MWGui MyGUI::IntCoord(mx*mMapWidgetSize, my*mMapWidgetSize, mMapWidgetSize, mMapWidgetSize), MyGUI::Align::Top | MyGUI::Align::Left); fog->setDepth(Local_FogLayer); + fog->setColour(MyGUI::Colour(0, 0, 0)); map->setNeedMouseFocus(false); fog->setNeedMouseFocus(false); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 7bd202e7e..81d925e33 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -692,12 +692,10 @@ void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm) return; } - // TODO: deprecate tga and use raw data instead - - osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); + osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!readerwriter) { - Log(Debug::Error) << "Error: Unable to load fog, can't find a tga ReaderWriter" ; + Log(Debug::Error) << "Error: Unable to load fog, can't find a png ReaderWriter" ; return; } @@ -726,10 +724,10 @@ void LocalMap::MapSegment::saveFogOfWar(ESM::FogTexture &fog) const std::ostringstream ostream; - osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); + osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!readerwriter) { - Log(Debug::Error) << "Error: Unable to write fog, can't find a tga ReaderWriter"; + Log(Debug::Error) << "Error: Unable to write fog, can't find a png ReaderWriter"; return; } diff --git a/components/esm/fogstate.cpp b/components/esm/fogstate.cpp index 18235066d..444da7ac9 100644 --- a/components/esm/fogstate.cpp +++ b/components/esm/fogstate.cpp @@ -3,10 +3,60 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include + +#include +#include + +#include "savedgame.hpp" + +void convertFogOfWar(std::vector& imageData) +{ + if (imageData.empty()) + { + return; + } + + osgDB::ReaderWriter* tgaReader = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); + if (!tgaReader) + { + Log(Debug::Error) << "Error: Unable to load fog, can't find a tga ReaderWriter"; + return; + } + + Files::IMemStream in(&imageData[0], imageData.size()); + + osgDB::ReaderWriter::ReadResult result = tgaReader->readImage(in); + if (!result.success()) + { + Log(Debug::Error) << "Error: Failed to read fog: " << result.message() << " code " << result.status(); + return; + } + + osgDB::ReaderWriter* pngWriter = osgDB::Registry::instance()->getReaderWriterForExtension("png"); + if (!pngWriter) + { + Log(Debug::Error) << "Error: Unable to write fog, can't find a png ReaderWriter"; + return; + } + + std::ostringstream ostream; + osgDB::ReaderWriter::WriteResult png = pngWriter->writeImage(*result.getImage(), ostream); + if (!png.success()) + { + Log(Debug::Error) << "Error: Unable to write fog: " << png.message() << " code " << png.status(); + return; + } + + std::string str = ostream.str(); + imageData = std::vector(str.begin(), str.end()); +} + void ESM::FogState::load (ESMReader &esm) { esm.getHNOT(mBounds, "BOUN"); esm.getHNOT(mNorthMarkerAngle, "ANGL"); + int dataFormat = esm.getFormat(); while (esm.isNextSub("FTEX")) { esm.getSubHeader(); @@ -18,6 +68,10 @@ void ESM::FogState::load (ESMReader &esm) size_t imageSize = esm.getSubSize()-sizeof(int)*2; tex.mImageData.resize(imageSize); esm.getExact(&tex.mImageData[0], imageSize); + + if (dataFormat < 6) + convertFogOfWar(tex.mImageData); + mFogTextures.push_back(tex); } } diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index ea9fef4fb..633f7c9fd 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 5; +int ESM::SavedGame::sCurrentFormat = 6; void ESM::SavedGame::load (ESMReader &esm) { From ae65b0228aa7bafcc08d900d549fd02d92ba1af3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 4 Mar 2020 15:14:22 +0400 Subject: [PATCH 002/227] Do not write custom data for disposed actors --- apps/openmw/mwclass/creature.cpp | 6 ++++++ apps/openmw/mwclass/npc.cpp | 6 ++++++ apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 9d5bd14e4..e379ddf41 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -813,6 +813,12 @@ namespace MWClass return; } + if (ptr.getRefData().getCount() <= 0) + { + state.mHasCustomState = false; + return; + } + const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); customData.mContainerStore->writeState (state2.mInventory); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4f1a996e7..e264dbbb1 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1331,6 +1331,12 @@ namespace MWClass return; } + if (ptr.getRefData().getCount() <= 0) + { + state.mHasCustomState = false; + return; + } + const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); customData.mInventoryStore.writeState (state2.mInventory); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 695abe105..0f9664d9c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1197,7 +1197,7 @@ namespace MWMechanics if (!Misc::StringUtils::ciEqual(item.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId)) { - if (victim.isEmpty() || (victim.getClass().isActor() && !victim.getClass().getCreatureStats(victim).isDead())) + if (victim.isEmpty() || (victim.getClass().isActor() && victim.getRefData().getCount() > 0 && !victim.getClass().getCreatureStats(victim).isDead())) mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count; } if (alarm) From 12044a607bcad2f9d394dbb43b899635b8e29aed Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 10 Apr 2020 15:45:37 +0100 Subject: [PATCH 003/227] Only alpha-test shadows when necessary Previously we always discarded shadow map fragments if the alpha channel of the output would have been low, but there were some (modded) assets that have non-one alpha but have testing or blending disabled so end up opaque anyway. This lets the shadows of those objects match. --- components/sceneutil/mwshadowtechnique.cpp | 10 +++++ components/sceneutil/mwshadowtechnique.hpp | 1 + components/shader/shadermanager.cpp | 10 +++++ components/shader/shadermanager.hpp | 6 +++ components/shader/shadervisitor.cpp | 47 +++++++++++++++++++++- components/shader/shadervisitor.hpp | 3 ++ files/shaders/shadowcasting_fragment.glsl | 7 ++++ files/shaders/shadowcasting_vertex.glsl | 8 ++-- 8 files changed, 87 insertions(+), 5 deletions(-) diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index f31d2faef..cb3a1b278 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -872,6 +872,15 @@ void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & sh _castingProgram->addShader(shaderManager.getShader("shadowcasting_vertex.glsl", Shader::ShaderManager::DefineMap(), osg::Shader::VERTEX)); _castingProgram->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", Shader::ShaderManager::DefineMap(), osg::Shader::FRAGMENT)); + + _shadowMapAlphaTestDisableUniform = shaderManager.getShadowMapAlphaTestDisableUniform(); + _shadowMapAlphaTestDisableUniform->setName("alphaTestShadows"); + _shadowMapAlphaTestDisableUniform->setType(osg::Uniform::BOOL); + _shadowMapAlphaTestDisableUniform->set(false); + + shaderManager.getShadowMapAlphaTestEnableUniform()->setName("alphaTestShadows"); + shaderManager.getShadowMapAlphaTestEnableUniform()->setType(osg::Uniform::BOOL); + shaderManager.getShadowMapAlphaTestEnableUniform()->set(true); } MWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData(osgUtil::CullVisitor* /*cv*/) @@ -1570,6 +1579,7 @@ void MWShadowTechnique::createShaders() // The casting program uses a sampler, so to avoid undefined behaviour, we must bind a dummy texture in case no other is supplied _shadowCastingStateSet->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON); _shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false)); + _shadowCastingStateSet->addUniform(_shadowMapAlphaTestDisableUniform); _shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON); diff --git a/components/sceneutil/mwshadowtechnique.hpp b/components/sceneutil/mwshadowtechnique.hpp index 165613f3c..85e548b4b 100644 --- a/components/sceneutil/mwshadowtechnique.hpp +++ b/components/sceneutil/mwshadowtechnique.hpp @@ -286,6 +286,7 @@ namespace SceneUtil { osg::ref_ptr _debugHud; osg::ref_ptr _castingProgram; + osg::ref_ptr _shadowMapAlphaTestDisableUniform; }; } diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 0a7345b97..92848de86 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -370,4 +370,14 @@ namespace Shader program.second->releaseGLObjects(state); } + const osg::ref_ptr ShaderManager::getShadowMapAlphaTestEnableUniform() + { + return mShadowMapAlphaTestEnableUniform; + } + + const osg::ref_ptr ShaderManager::getShadowMapAlphaTestDisableUniform() + { + return mShadowMapAlphaTestDisableUniform; + } + } diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index 05775edb6..4ea979c60 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -44,6 +44,9 @@ namespace Shader void releaseGLObjects(osg::State* state); + const osg::ref_ptr getShadowMapAlphaTestEnableUniform(); + const osg::ref_ptr getShadowMapAlphaTestDisableUniform(); + private: std::string mPath; @@ -61,6 +64,9 @@ namespace Shader ProgramMap mPrograms; OpenThreads::Mutex mMutex; + + const osg::ref_ptr mShadowMapAlphaTestEnableUniform = new osg::Uniform(); + const osg::ref_ptr mShadowMapAlphaTestDisableUniform = new osg::Uniform(); }; } diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 7fb5d53f5..b2a6d6f63 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -1,8 +1,10 @@ #include "shadervisitor.hpp" -#include -#include +#include +#include #include +#include +#include #include @@ -23,6 +25,8 @@ namespace Shader : mShaderRequired(false) , mColorMode(0) , mMaterialOverridden(false) + , mAlphaFuncOverridden(false) + , mBlendFuncOverridden(false) , mNormalHeight(false) , mTexStageRequiringTangents(-1) , mNode(nullptr) @@ -229,15 +233,21 @@ namespace Shader { if (!writableStateSet) writableStateSet = getWritableStateSet(node); + // We probably shouldn't construct a new version of this each time as StateSets only use pointer comparison by default. + // Also it should probably belong to the shader manager writableStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true)); } } + bool alphaSettingsChanged = false; + bool alphaTestShadows = false; + const osg::StateSet::AttributeList& attributes = stateset->getAttributeList(); for (osg::StateSet::AttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it) { if (it->first.first == osg::StateAttribute::MATERIAL) { + // This should probably be moved out of ShaderRequirements and be applied directly now it's a uniform instead of a define if (!mRequirements.back().mMaterialOverridden || it->second.second & osg::StateAttribute::PROTECTED) { if (it->second.second & osg::StateAttribute::OVERRIDE) @@ -269,6 +279,39 @@ namespace Shader mRequirements.back().mColorMode = colorMode; } } + else if (it->first.first == osg::StateAttribute::ALPHAFUNC) + { + if (!mRequirements.back().mAlphaFuncOverridden || it->second.second & osg::StateAttribute::PROTECTED) + { + if (it->second.second & osg::StateAttribute::OVERRIDE) + mRequirements.back().mAlphaFuncOverridden = true; + + const osg::AlphaFunc* test = static_cast(it->second.first.get()); + if (test->getFunction() == osg::AlphaFunc::GREATER || test->getFunction() == osg::AlphaFunc::GEQUAL) + alphaTestShadows = true; + alphaSettingsChanged = true; + } + } + else if (it->first.first == osg::StateAttribute::BLENDFUNC) + { + if (!mRequirements.back().mBlendFuncOverridden || it->second.second & osg::StateAttribute::PROTECTED) + { + if (it->second.second & osg::StateAttribute::OVERRIDE) + mRequirements.back().mBlendFuncOverridden = true; + + const osg::BlendFunc* blend = static_cast(it->second.first.get()); + if (blend->getSource() == osg::BlendFunc::SRC_ALPHA || blend->getSource() == osg::BlendFunc::SRC_COLOR) + alphaTestShadows = true; + alphaSettingsChanged = true; + } + } + } + // we don't need to check for glEnable/glDisable of blending and testing as we always set it at the same time + if (alphaSettingsChanged) + { + if (!writableStateSet) + writableStateSet = getWritableStateSet(node); + writableStateSet->addUniform(alphaTestShadows ? mShaderManager.getShadowMapAlphaTestEnableUniform() : mShaderManager.getShadowMapAlphaTestDisableUniform()); } } diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index ac0ecc699..311f6213f 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -75,6 +75,9 @@ namespace Shader int mColorMode; bool mMaterialOverridden; + bool mAlphaFuncOverridden; + bool mBlendFuncOverridden; + bool mNormalHeight; // true if normal map has height info in alpha channel // -1 == no tangents required diff --git a/files/shaders/shadowcasting_fragment.glsl b/files/shaders/shadowcasting_fragment.glsl index 336bfe4a4..47323cc6a 100644 --- a/files/shaders/shadowcasting_fragment.glsl +++ b/files/shaders/shadowcasting_fragment.glsl @@ -6,10 +6,17 @@ varying vec2 diffuseMapUV; varying float alphaPassthrough; uniform bool useDiffuseMapForShadowAlpha; +uniform bool alphaTestShadows; void main() { gl_FragData[0].rgb = vec3(1.0); + if (!alphaTestShadows) + { + gl_FragData[0].a = 1.0; + return; + } + if (useDiffuseMapForShadowAlpha) gl_FragData[0].a = texture2D(diffuseMap, diffuseMapUV).a * alphaPassthrough; else diff --git a/files/shaders/shadowcasting_vertex.glsl b/files/shaders/shadowcasting_vertex.glsl index d578e97b7..e19b587e5 100644 --- a/files/shaders/shadowcasting_vertex.glsl +++ b/files/shaders/shadowcasting_vertex.glsl @@ -6,6 +6,7 @@ varying float alphaPassthrough; uniform int colorMode; uniform bool useDiffuseMapForShadowAlpha; +uniform bool alphaTestShadows; void main(void) { @@ -14,12 +15,13 @@ void main(void) vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; - if (useDiffuseMapForShadowAlpha) + if (alphaTestShadows && useDiffuseMapForShadowAlpha) diffuseMapUV = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy; else diffuseMapUV = vec2(0.0); // Avoid undefined behaviour if running on hardware predating the concept of dynamically uniform expressions - - if (colorMode == 2) + if (!alphaTestShadows) + alphaPassthrough = 1.0; + else if (colorMode == 2) alphaPassthrough = gl_Color.a; else // This is uniform, so if it's too low, we might be able to put the position/clip vertex outside the view frustum and skip the fragment shader and rasteriser From 9698c21b367d059cfdab07ada7660fd5f6ce663b Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 17 Apr 2020 13:50:54 +0200 Subject: [PATCH 004/227] build bsa and esm tools --- CI/before_script.msvc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 847435ac5..77ac1cc33 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -759,8 +759,8 @@ echo cd $DEPS_INSTALL/.. echo echo "Setting up OpenMW build..." -add_cmake_opts -DBUILD_BSATOOL=no \ - -DBUILD_ESMTOOL=no \ +add_cmake_opts -DBUILD_BSATOOL=yes \ + -DBUILD_ESMTOOL=yes \ -DBUILD_MYGUI_PLUGIN=no \ -DOPENMW_MP_BUILD=on if [ ! -z $CI ]; then From 0d10293f24a6142fe6868d95bf6cc7b6ec3d8cb0 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 17 Apr 2020 15:10:05 +0200 Subject: [PATCH 005/227] everything is implied yes --- CI/before_script.msvc.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 77ac1cc33..2427f2be1 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -759,9 +759,7 @@ echo cd $DEPS_INSTALL/.. echo echo "Setting up OpenMW build..." -add_cmake_opts -DBUILD_BSATOOL=yes \ - -DBUILD_ESMTOOL=yes \ - -DBUILD_MYGUI_PLUGIN=no \ +add_cmake_opts -DBUILD_MYGUI_PLUGIN=no \ -DOPENMW_MP_BUILD=on if [ ! -z $CI ]; then case $STEP in From 2b54e6216b4b8773d3b7481684540849ac5a5e82 Mon Sep 17 00:00:00 2001 From: p4r4digm Date: Sun, 19 Apr 2020 16:34:00 -0700 Subject: [PATCH 006/227] Added setting to change the directory screenshots are stored in --- apps/openmw/engine.cpp | 4 ++-- components/files/configurationmanager.cpp | 13 +++++++++++++ components/files/configurationmanager.hpp | 1 + files/settings-default.cfg | 3 +++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index ec8c1e305..5b0bd491f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -657,7 +657,6 @@ private: }; // Initialise and enter main loop. - void OMW::Engine::go() { assert (!mContentFiles.empty()); @@ -686,7 +685,8 @@ void OMW::Engine::go() mViewer->setUseConfigureAffinity(false); #endif - mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(), + mScreenCaptureOperation = new WriteScreenshotToFileOperation( + mCfgMgr.getScreenshotPath(Settings::Manager::getString("screenshot path", "General")).string(), Settings::Manager::getString("screenshot format", "General")); mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation); diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 3bc6e1772..231401c08 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -196,4 +196,17 @@ const boost::filesystem::path& ConfigurationManager::getLogPath() const return mLogPath; } +const boost::filesystem::path ConfigurationManager::getScreenshotPath(std::string const& screenshotSettings) const +{ + boost::filesystem::path ssPath = screenshotSettings; + if (ssPath.is_relative()) { + ssPath = mFixedPath.getUserDataPath() / ssPath; + } + boost::system::error_code dirErr; + if (!boost::filesystem::create_directories(ssPath, dirErr) && !boost::filesystem::is_directory(ssPath)) { + ssPath = mFixedPath.getUserDataPath(); + } + return ssPath; +} + } /* namespace Cfg */ diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index df131e671..1c6123b84 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -41,6 +41,7 @@ struct ConfigurationManager const boost::filesystem::path& getCachePath() const; const boost::filesystem::path& getLogPath() const; + const boost::filesystem::path getScreenshotPath(std::string const& screenshotSetting) const; private: typedef Files::FixedPath<> FixedPathType; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 06950e50d..08a520ca0 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -284,6 +284,9 @@ anisotropy = 4 # File format for screenshots. (jpg, png, tga, and possibly more). screenshot format = png +# Directory to store screenshots in. Supports relative and absolute paths. Relative paths will be to the user data folder. +screenshot path =./ + # Texture magnification filter type. (nearest or linear). texture mag filter = linear From 0741fe5b800278b11e2a34bb878116a38c21eee1 Mon Sep 17 00:00:00 2001 From: p4r4digm Date: Mon, 20 Apr 2020 09:22:50 -0700 Subject: [PATCH 007/227] removed path configuration and made screenshots just save in a folder --- apps/openmw/engine.cpp | 2 +- components/files/configurationmanager.cpp | 20 ++++++++++---------- components/files/configurationmanager.hpp | 3 ++- files/settings-default.cfg | 3 --- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5b0bd491f..3d609259f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -686,7 +686,7 @@ void OMW::Engine::go() #endif mScreenCaptureOperation = new WriteScreenshotToFileOperation( - mCfgMgr.getScreenshotPath(Settings::Manager::getString("screenshot path", "General")).string(), + mCfgMgr.getScreenshotPath().string(), Settings::Manager::getString("screenshot format", "General")); mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation); diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 231401c08..0ba2d1519 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -32,6 +32,14 @@ ConfigurationManager::ConfigurationManager(bool silent) boost::filesystem::create_directories(mFixedPath.getUserDataPath()); mLogPath = mFixedPath.getUserConfigPath(); + + mScreenshotPath = mFixedPath.getUserDataPath() / "screenshots"; + + // probably not necessary but validate the creation of the screenshots directory and fallback to the original behavior if it fails + boost::system::error_code dirErr; + if (!boost::filesystem::create_directories(mScreenshotPath, dirErr) && !boost::filesystem::is_directory(mScreenshotPath)) { + mScreenshotPath = mFixedPath.getUserDataPath(); + } } ConfigurationManager::~ConfigurationManager() @@ -196,17 +204,9 @@ const boost::filesystem::path& ConfigurationManager::getLogPath() const return mLogPath; } -const boost::filesystem::path ConfigurationManager::getScreenshotPath(std::string const& screenshotSettings) const +const boost::filesystem::path& ConfigurationManager::getScreenshotPath() const { - boost::filesystem::path ssPath = screenshotSettings; - if (ssPath.is_relative()) { - ssPath = mFixedPath.getUserDataPath() / ssPath; - } - boost::system::error_code dirErr; - if (!boost::filesystem::create_directories(ssPath, dirErr) && !boost::filesystem::is_directory(ssPath)) { - ssPath = mFixedPath.getUserDataPath(); - } - return ssPath; + return mScreenshotPath; } } /* namespace Cfg */ diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 1c6123b84..446abd4dc 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -41,7 +41,7 @@ struct ConfigurationManager const boost::filesystem::path& getCachePath() const; const boost::filesystem::path& getLogPath() const; - const boost::filesystem::path getScreenshotPath(std::string const& screenshotSetting) const; + const boost::filesystem::path& getScreenshotPath() const; private: typedef Files::FixedPath<> FixedPathType; @@ -58,6 +58,7 @@ struct ConfigurationManager FixedPathType mFixedPath; boost::filesystem::path mLogPath; + boost::filesystem::path mScreenshotPath; TokensMappingContainer mTokensMapping; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 08a520ca0..06950e50d 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -284,9 +284,6 @@ anisotropy = 4 # File format for screenshots. (jpg, png, tga, and possibly more). screenshot format = png -# Directory to store screenshots in. Supports relative and absolute paths. Relative paths will be to the user data folder. -screenshot path =./ - # Texture magnification filter type. (nearest or linear). texture mag filter = linear From 53b9b411591b2bc22e78ba2cc15fa422449d89a1 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 21 Apr 2020 18:18:55 +0100 Subject: [PATCH 008/227] Rely on existing alpha test for non-blended shadow casting --- components/shader/shadervisitor.cpp | 17 ++--------------- components/shader/shadervisitor.hpp | 1 - files/shaders/shadowcasting_fragment.glsl | 10 ++-------- files/shaders/shadowcasting_vertex.glsl | 10 ++++------ 4 files changed, 8 insertions(+), 30 deletions(-) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index b2a6d6f63..639a7ecca 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -25,7 +25,6 @@ namespace Shader : mShaderRequired(false) , mColorMode(0) , mMaterialOverridden(false) - , mAlphaFuncOverridden(false) , mBlendFuncOverridden(false) , mNormalHeight(false) , mTexStageRequiringTangents(-1) @@ -279,19 +278,6 @@ namespace Shader mRequirements.back().mColorMode = colorMode; } } - else if (it->first.first == osg::StateAttribute::ALPHAFUNC) - { - if (!mRequirements.back().mAlphaFuncOverridden || it->second.second & osg::StateAttribute::PROTECTED) - { - if (it->second.second & osg::StateAttribute::OVERRIDE) - mRequirements.back().mAlphaFuncOverridden = true; - - const osg::AlphaFunc* test = static_cast(it->second.first.get()); - if (test->getFunction() == osg::AlphaFunc::GREATER || test->getFunction() == osg::AlphaFunc::GEQUAL) - alphaTestShadows = true; - alphaSettingsChanged = true; - } - } else if (it->first.first == osg::StateAttribute::BLENDFUNC) { if (!mRequirements.back().mBlendFuncOverridden || it->second.second & osg::StateAttribute::PROTECTED) @@ -305,8 +291,9 @@ namespace Shader alphaSettingsChanged = true; } } + // Eventually, move alpha testing to discard in shader adn remove deprecated state here } - // we don't need to check for glEnable/glDisable of blending and testing as we always set it at the same time + // we don't need to check for glEnable/glDisable of blending as we always set it at the same time if (alphaSettingsChanged) { if (!writableStateSet) diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index 311f6213f..8e35f1d9c 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -75,7 +75,6 @@ namespace Shader int mColorMode; bool mMaterialOverridden; - bool mAlphaFuncOverridden; bool mBlendFuncOverridden; bool mNormalHeight; // true if normal map has height info in alpha channel diff --git a/files/shaders/shadowcasting_fragment.glsl b/files/shaders/shadowcasting_fragment.glsl index 47323cc6a..a5410d008 100644 --- a/files/shaders/shadowcasting_fragment.glsl +++ b/files/shaders/shadowcasting_fragment.glsl @@ -11,18 +11,12 @@ uniform bool alphaTestShadows; void main() { gl_FragData[0].rgb = vec3(1.0); - if (!alphaTestShadows) - { - gl_FragData[0].a = 1.0; - return; - } - if (useDiffuseMapForShadowAlpha) gl_FragData[0].a = texture2D(diffuseMap, diffuseMapUV).a * alphaPassthrough; else gl_FragData[0].a = alphaPassthrough; - // Prevent translucent things casting shadow (including the player using an invisibility effect) - if (gl_FragData[0].a <= 0.5) + // Prevent translucent things casting shadow (including the player using an invisibility effect). For now, rely on the deprecated FF test for non-blended stuff. + if (alphaTestShadows && gl_FragData[0].a <= 0.5) discard; } diff --git a/files/shaders/shadowcasting_vertex.glsl b/files/shaders/shadowcasting_vertex.glsl index e19b587e5..e36f21a4d 100644 --- a/files/shaders/shadowcasting_vertex.glsl +++ b/files/shaders/shadowcasting_vertex.glsl @@ -5,8 +5,8 @@ varying vec2 diffuseMapUV; varying float alphaPassthrough; uniform int colorMode; -uniform bool useDiffuseMapForShadowAlpha; -uniform bool alphaTestShadows; +uniform bool useDiffuseMapForShadowAlpha = true; +uniform bool alphaTestShadows = true; void main(void) { @@ -15,13 +15,11 @@ void main(void) vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; - if (alphaTestShadows && useDiffuseMapForShadowAlpha) + if (useDiffuseMapForShadowAlpha) diffuseMapUV = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy; else diffuseMapUV = vec2(0.0); // Avoid undefined behaviour if running on hardware predating the concept of dynamically uniform expressions - if (!alphaTestShadows) - alphaPassthrough = 1.0; - else if (colorMode == 2) + if (colorMode == 2) alphaPassthrough = gl_Color.a; else // This is uniform, so if it's too low, we might be able to put the position/clip vertex outside the view frustum and skip the fragment shader and rasteriser From ac256f05ff0f9aae575b175ba4e78ccbf6f8f292 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 22 Apr 2020 19:20:48 +0100 Subject: [PATCH 009/227] Close graphics context while it still exists --- extern/osgQt/GraphicsWindowQt.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/extern/osgQt/GraphicsWindowQt.cpp b/extern/osgQt/GraphicsWindowQt.cpp index af963c04b..aa9b4bbdb 100644 --- a/extern/osgQt/GraphicsWindowQt.cpp +++ b/extern/osgQt/GraphicsWindowQt.cpp @@ -119,6 +119,13 @@ bool GLWidget::event( QEvent* event ) enqueueDeferredEvent(QEvent::ParentChange); return true; } +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) + else if (event->type() == QEvent::PlatformSurface && static_cast(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) + { + if (_gw) + _gw->close(); + } +#endif // perform regular event handling return QGLWidget::event( event ); From 688e804548861d3b12009ab414da05e58438aae7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Apr 2020 13:21:49 +0300 Subject: [PATCH 010/227] Fix simple water with radial fog enabled --- components/sceneutil/waterutil.cpp | 3 +++ files/shaders/objects_fragment.glsl | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/components/sceneutil/waterutil.cpp b/components/sceneutil/waterutil.cpp index 562b0ee73..b98a19ae8 100644 --- a/components/sceneutil/waterutil.cpp +++ b/components/sceneutil/waterutil.cpp @@ -74,6 +74,9 @@ namespace SceneUtil stateset->setRenderBinDetails(renderBin, "RenderBin"); + // Let the shader know we're dealing with simple water here. + stateset->addUniform(new osg::Uniform("simpleWater", true)); + return stateset; } } diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 31e929a90..81884feac 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -49,6 +49,8 @@ uniform vec2 envMapLumaBias; uniform mat2 bumpMapMatrix; #endif +uniform bool simpleWater = false; + varying float euclideanDepth; varying float linearDepth; @@ -180,7 +182,11 @@ void main() gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; #if @radialFog - float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); + float depth = euclideanDepth; + // For the less detailed mesh of simple water we need to recalculate depth on per-pixel basis + if (simpleWater) + depth = length(passViewPos); + float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); #else float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); #endif From 066f0a744fa181c3d0d80335a4d118a0e408f59c Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 25 Apr 2020 00:26:42 +0200 Subject: [PATCH 011/227] Add env variable to enable/disable crash catcher --- components/crashcatcher/crashcatcher.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index 006ab2d88..99df2cfd8 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -555,6 +555,9 @@ static bool is_debugger_present() void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath) { + if (const auto env = std::getenv("OPENMW_DISABLE_CRASH_CATCHER")) + if (std::atol(env) != 0) + return; if ((argc == 2 && strcmp(argv[1], crash_switch) == 0) || !is_debugger_present()) { int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT }; From 5535a7fb44956a7644d45b3ced4bf4ac186b04a5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 4 Apr 2020 16:27:00 +0300 Subject: [PATCH 012/227] Move getSummonedCreature() to summoning --- apps/openmw/mwmechanics/spellcasting.cpp | 34 ---------------------- apps/openmw/mwmechanics/spellcasting.hpp | 2 -- apps/openmw/mwmechanics/summoning.cpp | 37 +++++++++++++++++++++--- apps/openmw/mwmechanics/summoning.hpp | 5 ++-- apps/openmw/mwworld/worldimp.cpp | 1 + 5 files changed, 37 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index ed8972f05..3ded41e91 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -1312,40 +1312,6 @@ namespace MWMechanics return true; } - std::string getSummonedCreature(int effectId) - { - static const std::map summonMap - { - {ESM::MagicEffect::SummonAncestralGhost, "sMagicAncestralGhostID"}, - {ESM::MagicEffect::SummonBonelord, "sMagicBonelordID"}, - {ESM::MagicEffect::SummonBonewalker, "sMagicLeastBonewalkerID"}, - {ESM::MagicEffect::SummonCenturionSphere, "sMagicCenturionSphereID"}, - {ESM::MagicEffect::SummonClannfear, "sMagicClannfearID"}, - {ESM::MagicEffect::SummonDaedroth, "sMagicDaedrothID"}, - {ESM::MagicEffect::SummonDremora, "sMagicDremoraID"}, - {ESM::MagicEffect::SummonFabricant, "sMagicFabricantID"}, - {ESM::MagicEffect::SummonFlameAtronach, "sMagicFlameAtronachID"}, - {ESM::MagicEffect::SummonFrostAtronach, "sMagicFrostAtronachID"}, - {ESM::MagicEffect::SummonGoldenSaint, "sMagicGoldenSaintID"}, - {ESM::MagicEffect::SummonGreaterBonewalker, "sMagicGreaterBonewalkerID"}, - {ESM::MagicEffect::SummonHunger, "sMagicHungerID"}, - {ESM::MagicEffect::SummonScamp, "sMagicScampID"}, - {ESM::MagicEffect::SummonSkeletalMinion, "sMagicSkeletalMinionID"}, - {ESM::MagicEffect::SummonStormAtronach, "sMagicStormAtronachID"}, - {ESM::MagicEffect::SummonWingedTwilight, "sMagicWingedTwilightID"}, - {ESM::MagicEffect::SummonWolf, "sMagicCreature01ID"}, - {ESM::MagicEffect::SummonBear, "sMagicCreature02ID"}, - {ESM::MagicEffect::SummonBonewolf, "sMagicCreature03ID"}, - {ESM::MagicEffect::SummonCreature04, "sMagicCreature04ID"}, - {ESM::MagicEffect::SummonCreature05, "sMagicCreature05ID"} - }; - - auto it = summonMap.find(effectId); - if (it != summonMap.end()) - return MWBase::Environment::get().getWorld()->getStore().get().find(it->second)->mValue.getString(); - return std::string(); - } - void ApplyLoopingParticlesVisitor::visit (MWMechanics::EffectKey key, const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/, float /*magnitude*/, float /*remainingTime*/, float /*totalTime*/) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 85e732e03..bca93c734 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -75,8 +75,6 @@ namespace MWMechanics /// @return Was the effect a tickable effect with a magnitude? bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const MWMechanics::EffectKey& effectKey, float magnitude); - std::string getSummonedCreature(int effectId); - class CastSpell { private: diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index 86d0faa9d..d4973925b 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -20,13 +20,42 @@ namespace MWMechanics { - UpdateSummonedCreatures::UpdateSummonedCreatures(const MWWorld::Ptr &actor) - : mActor(actor) + std::string getSummonedCreature(int effectId) { - + static const std::map summonMap + { + {ESM::MagicEffect::SummonAncestralGhost, "sMagicAncestralGhostID"}, + {ESM::MagicEffect::SummonBonelord, "sMagicBonelordID"}, + {ESM::MagicEffect::SummonBonewalker, "sMagicLeastBonewalkerID"}, + {ESM::MagicEffect::SummonCenturionSphere, "sMagicCenturionSphereID"}, + {ESM::MagicEffect::SummonClannfear, "sMagicClannfearID"}, + {ESM::MagicEffect::SummonDaedroth, "sMagicDaedrothID"}, + {ESM::MagicEffect::SummonDremora, "sMagicDremoraID"}, + {ESM::MagicEffect::SummonFabricant, "sMagicFabricantID"}, + {ESM::MagicEffect::SummonFlameAtronach, "sMagicFlameAtronachID"}, + {ESM::MagicEffect::SummonFrostAtronach, "sMagicFrostAtronachID"}, + {ESM::MagicEffect::SummonGoldenSaint, "sMagicGoldenSaintID"}, + {ESM::MagicEffect::SummonGreaterBonewalker, "sMagicGreaterBonewalkerID"}, + {ESM::MagicEffect::SummonHunger, "sMagicHungerID"}, + {ESM::MagicEffect::SummonScamp, "sMagicScampID"}, + {ESM::MagicEffect::SummonSkeletalMinion, "sMagicSkeletalMinionID"}, + {ESM::MagicEffect::SummonStormAtronach, "sMagicStormAtronachID"}, + {ESM::MagicEffect::SummonWingedTwilight, "sMagicWingedTwilightID"}, + {ESM::MagicEffect::SummonWolf, "sMagicCreature01ID"}, + {ESM::MagicEffect::SummonBear, "sMagicCreature02ID"}, + {ESM::MagicEffect::SummonBonewolf, "sMagicCreature03ID"}, + {ESM::MagicEffect::SummonCreature04, "sMagicCreature04ID"}, + {ESM::MagicEffect::SummonCreature05, "sMagicCreature05ID"} + }; + + auto it = summonMap.find(effectId); + if (it != summonMap.end()) + return MWBase::Environment::get().getWorld()->getStore().get().find(it->second)->mValue.getString(); + return std::string(); } - UpdateSummonedCreatures::~UpdateSummonedCreatures() + UpdateSummonedCreatures::UpdateSummonedCreatures(const MWWorld::Ptr &actor) + : mActor(actor) { } diff --git a/apps/openmw/mwmechanics/summoning.hpp b/apps/openmw/mwmechanics/summoning.hpp index 9329dcb83..e638155a9 100644 --- a/apps/openmw/mwmechanics/summoning.hpp +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -9,13 +9,14 @@ namespace MWMechanics { - class CreatureStats; + std::string getSummonedCreature(int effectId); + struct UpdateSummonedCreatures : public EffectSourceVisitor { UpdateSummonedCreatures(const MWWorld::Ptr& actor); - virtual ~UpdateSummonedCreatures(); + virtual ~UpdateSummonedCreatures() = default; virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, const std::string& sourceId, int casterActorId, diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ce06d887c..37123099a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -41,6 +41,7 @@ #include "../mwmechanics/levelledlist.hpp" #include "../mwmechanics/combat.hpp" #include "../mwmechanics/aiavoiddoor.hpp" //Used to tell actors to avoid doors +#include "../mwmechanics/summoning.hpp" #include "../mwrender/animation.hpp" #include "../mwrender/npcanimation.hpp" From 5973285446017312e331b943ca5ddcc61469780e Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 4 Apr 2020 17:45:03 +0300 Subject: [PATCH 013/227] Move isSummoningEffect to summoning --- apps/openmw/mwmechanics/spellcasting.cpp | 10 +--------- apps/openmw/mwmechanics/spellcasting.hpp | 2 -- apps/openmw/mwmechanics/spellpriority.cpp | 1 + apps/openmw/mwmechanics/summoning.cpp | 8 +++++++- apps/openmw/mwmechanics/summoning.hpp | 2 ++ 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 3ded41e91..d7c3bcdb1 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -27,6 +27,7 @@ #include "actorutil.hpp" #include "aifollow.hpp" #include "weapontype.hpp" +#include "summoning.hpp" namespace MWMechanics { @@ -1104,15 +1105,6 @@ namespace MWMechanics return static_cast((result < 1) ? 1 : result); } - bool isSummoningEffect(int effectId) - { - return ((effectId >= ESM::MagicEffect::SummonScamp - && effectId <= ESM::MagicEffect::SummonStormAtronach) - || effectId == ESM::MagicEffect::SummonCenturionSphere - || (effectId >= ESM::MagicEffect::SummonFabricant - && effectId <= ESM::MagicEffect::SummonCreature05)); - } - bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) { if (ptr.getClass().hasInventoryStore(ptr)) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index bca93c734..804b4bca9 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -27,8 +27,6 @@ namespace MWMechanics float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect = nullptr); - bool isSummoningEffect(int effectId); - /** * @param spell spell to cast * @param actor calculate spell success chance for this actor (depends on actor's skills) diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 7b5c38592..f90e59971 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -18,6 +18,7 @@ #include "spellcasting.hpp" #include "weapontype.hpp" #include "combat.hpp" +#include "summoning.hpp" namespace { diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index d4973925b..03fd0d681 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -13,13 +13,19 @@ #include "../mwrender/animation.hpp" -#include "spellcasting.hpp" #include "creaturestats.hpp" #include "aifollow.hpp" namespace MWMechanics { + bool isSummoningEffect(int effectId) + { + return ((effectId >= ESM::MagicEffect::SummonScamp && effectId <= ESM::MagicEffect::SummonStormAtronach) + || (effectId == ESM::MagicEffect::SummonCenturionSphere) + || (effectId >= ESM::MagicEffect::SummonFabricant && effectId <= ESM::MagicEffect::SummonCreature05)); + } + std::string getSummonedCreature(int effectId) { static const std::map summonMap diff --git a/apps/openmw/mwmechanics/summoning.hpp b/apps/openmw/mwmechanics/summoning.hpp index e638155a9..f24413120 100644 --- a/apps/openmw/mwmechanics/summoning.hpp +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -11,6 +11,8 @@ namespace MWMechanics { class CreatureStats; + bool isSummoningEffect(int effectId); + std::string getSummonedCreature(int effectId); struct UpdateSummonedCreatures : public EffectSourceVisitor From db13984db00e9096fdc48817757856ee2100c3a4 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 4 Apr 2020 18:28:53 +0300 Subject: [PATCH 014/227] Separate spell resistance --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwmechanics/combat.cpp | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 78 +----------------- apps/openmw/mwmechanics/spellcasting.hpp | 22 +---- apps/openmw/mwmechanics/spellpriority.cpp | 1 + apps/openmw/mwmechanics/spellresistance.cpp | 90 +++++++++++++++++++++ apps/openmw/mwmechanics/spellresistance.hpp | 37 +++++++++ apps/openmw/mwworld/inventorystore.cpp | 1 + 8 files changed, 134 insertions(+), 98 deletions(-) create mode 100644 apps/openmw/mwmechanics/spellresistance.cpp create mode 100644 apps/openmw/mwmechanics/spellresistance.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 57262f964..3395b5fc8 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -83,7 +83,7 @@ add_openmw_dir (mwclass add_openmw_dir (mwmechanics mechanicsmanagerimp stat creaturestats magiceffects movement actorutil drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe - aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellsuccess spellcasting + aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype ) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 9f5446c11..9698892e4 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -18,6 +18,7 @@ #include "npcstats.hpp" #include "movement.hpp" #include "spellcasting.hpp" +#include "spellresistance.hpp" #include "difficultyscaling.hpp" #include "actorutil.hpp" #include "pathfinding.hpp" diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d7c3bcdb1..d5ddd9a55 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -28,6 +28,7 @@ #include "aifollow.hpp" #include "weapontype.hpp" #include "summoning.hpp" +#include "spellresistance.hpp" namespace MWMechanics { @@ -168,83 +169,6 @@ namespace MWMechanics return spell && spellIncreasesSkill(spell); } - float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects) - { - short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); - short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); - - float resistance = 0; - if (resistanceEffect != -1) - resistance += actorEffects->get(resistanceEffect).getMagnitude(); - if (weaknessEffect != -1) - resistance -= actorEffects->get(weaknessEffect).getMagnitude(); - - if (effectId == ESM::MagicEffect::FireDamage) - resistance += actorEffects->get(ESM::MagicEffect::FireShield).getMagnitude(); - if (effectId == ESM::MagicEffect::ShockDamage) - resistance += actorEffects->get(ESM::MagicEffect::LightningShield).getMagnitude(); - if (effectId == ESM::MagicEffect::FrostDamage) - resistance += actorEffects->get(ESM::MagicEffect::FrostShield).getMagnitude(); - - return resistance; - } - - float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, - const ESM::Spell* spell, const MagicEffects* effects) - { - const ESM::MagicEffect *magicEffect = - MWBase::Environment::get().getWorld()->getStore().get().find ( - effectId); - - const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); - const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects(); - if (effects) - magicEffects = effects; - - // Effects with no resistance attribute belonging to them can not be resisted - if (ESM::MagicEffect::getResistanceEffect(effectId) == -1) - return 0.f; - - float resistance = getEffectResistanceAttribute(effectId, magicEffects); - - int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); - float x = (willpower + 0.1f * luck) * stats.getFatigueTerm(); - - // This makes spells that are easy to cast harder to resist and vice versa - float castChance = 100.f; - if (spell != nullptr && !caster.isEmpty() && caster.getClass().isActor()) - { - castChance = getSpellSuccessChance(spell, caster, nullptr, false, false); // Uncapped casting chance - } - if (castChance > 0) - x *= 50 / castChance; - - float roll = Misc::Rng::rollClosedProbability() * 100; - if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) - roll -= resistance; - - if (x <= roll) - x = 0; - else - { - if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) - x = 100; - else - x = roll / std::min(x, 100.f); - } - - x = std::min(x + resistance, 100.f); - return x; - } - - float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, - const ESM::Spell* spell, const MagicEffects* effects) - { - float resistance = getEffectResistance(effectId, actor, caster, spell, effects); - return 1 - resistance / 100.f; - } - /// Check if the given effect can be applied to the target. If \a castByPlayer, emits a message box on failure. bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer) { diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 804b4bca9..91cf37272 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -1,5 +1,5 @@ -#ifndef MWMECHANICS_SPELLSUCCESS_H -#define MWMECHANICS_SPELLSUCCESS_H +#ifndef MWMECHANICS_SPELLCASTING_H +#define MWMECHANICS_SPELLCASTING_H #include #include @@ -46,24 +46,6 @@ namespace MWMechanics bool spellIncreasesSkill(const ESM::Spell* spell); bool spellIncreasesSkill(const std::string& spellId); - /// Get the resistance attribute against an effect for a given actor. This will add together - /// ResistX and Weakness to X effects relevant against the given effect. - float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects); - - /// Get the effective resistance against an effect casted by the given actor in the given spell (optional). - /// @return >=100 for fully resisted. can also return negative value for damage amplification. - /// @param effects Override the actor's current magicEffects. Useful if there are effects currently - /// being applied (but not applied yet) that should also be considered. - float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, - const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); - - /// Get an effect multiplier for applying an effect cast by the given actor in the given spell (optional). - /// @return effect multiplier from 0 to 2. (100% net resistance to 100% net weakness) - /// @param effects Override the actor's current magicEffects. Useful if there are effects currently - /// being applied (but not applied yet) that should also be considered. - float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, - const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); - bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer); int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index f90e59971..a529781cb 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -16,6 +16,7 @@ #include "creaturestats.hpp" #include "spellcasting.hpp" +#include "spellresistance.hpp" #include "weapontype.hpp" #include "combat.hpp" #include "summoning.hpp" diff --git a/apps/openmw/mwmechanics/spellresistance.cpp b/apps/openmw/mwmechanics/spellresistance.cpp new file mode 100644 index 000000000..bbb4b56a5 --- /dev/null +++ b/apps/openmw/mwmechanics/spellresistance.cpp @@ -0,0 +1,90 @@ +#include "spellresistance.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +#include "creaturestats.hpp" +#include "spellcasting.hpp" + +namespace MWMechanics +{ + + float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell, const MagicEffects* effects) + { + float resistance = getEffectResistance(effectId, actor, caster, spell, effects); + return 1 - resistance / 100.f; + } + + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell, const MagicEffects* effects) + { + // Effects with no resistance attribute belonging to them can not be resisted + if (ESM::MagicEffect::getResistanceEffect(effectId) == -1) + return 0.f; + + const auto magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectId); + + const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects(); + if (effects) + magicEffects = effects; + + float resistance = getEffectResistanceAttribute(effectId, magicEffects); + + int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); + float x = (willpower + 0.1f * luck) * stats.getFatigueTerm(); + + // This makes spells that are easy to cast harder to resist and vice versa + float castChance = 100.f; + if (spell != nullptr && !caster.isEmpty() && caster.getClass().isActor()) + castChance = getSpellSuccessChance(spell, caster, nullptr, false, false); // Uncapped casting chance + if (castChance > 0) + x *= 50 / castChance; + + float roll = Misc::Rng::rollClosedProbability() * 100; + if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) + roll -= resistance; + + if (x <= roll) + x = 0; + else + { + if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) + x = 100; + else + x = roll / std::min(x, 100.f); + } + + x = std::min(x + resistance, 100.f); + return x; + } + + float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects) + { + short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); + short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); + + float resistance = 0; + if (resistanceEffect != -1) + resistance += actorEffects->get(resistanceEffect).getMagnitude(); + if (weaknessEffect != -1) + resistance -= actorEffects->get(weaknessEffect).getMagnitude(); + + if (effectId == ESM::MagicEffect::FireDamage) + resistance += actorEffects->get(ESM::MagicEffect::FireShield).getMagnitude(); + if (effectId == ESM::MagicEffect::ShockDamage) + resistance += actorEffects->get(ESM::MagicEffect::LightningShield).getMagnitude(); + if (effectId == ESM::MagicEffect::FrostDamage) + resistance += actorEffects->get(ESM::MagicEffect::FrostShield).getMagnitude(); + + return resistance; + } + +} diff --git a/apps/openmw/mwmechanics/spellresistance.hpp b/apps/openmw/mwmechanics/spellresistance.hpp new file mode 100644 index 000000000..8e74c2260 --- /dev/null +++ b/apps/openmw/mwmechanics/spellresistance.hpp @@ -0,0 +1,37 @@ +#ifndef MWMECHANICS_SPELLRESISTANCE_H +#define MWMECHANICS_SPELLRESISTANCE_H + +namespace ESM +{ + struct Spell; +} + +namespace MWWorld +{ + class Ptr; +} + +namespace MWMechanics +{ + class MagicEffects; + + /// Get an effect multiplier for applying an effect cast by the given actor in the given spell (optional). + /// @return effect multiplier from 0 to 2. (100% net resistance to 100% net weakness) + /// @param effects Override the actor's current magicEffects. Useful if there are effects currently + /// being applied (but not applied yet) that should also be considered. + float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); + + /// Get the effective resistance against an effect casted by the given actor in the given spell (optional). + /// @return >=100 for fully resisted. can also return negative value for damage amplification. + /// @param effects Override the actor's current magicEffects. Useful if there are effects currently + /// being applied (but not applied yet) that should also be considered. + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); + + /// Get the resistance attribute against an effect for a given actor. This will add together + /// ResistX and Weakness to X effects relevant against the given effect. + float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects); +} + +#endif diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 1c8062011..1de97d068 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -14,6 +14,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spellresistance.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/weapontype.hpp" From 8d22e075e67cc5304260f19e7072997e48b4a05c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Apr 2020 20:46:51 +0300 Subject: [PATCH 015/227] Separate functions that don't belong to CastSpell class --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/quickkeysmenu.cpp | 2 +- apps/openmw/mwgui/spellcreationdialog.cpp | 2 +- apps/openmw/mwgui/spellmodel.cpp | 2 +- apps/openmw/mwgui/spellwindow.cpp | 2 +- apps/openmw/mwgui/tooltips.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 1 + apps/openmw/mwmechanics/aicombataction.cpp | 1 - apps/openmw/mwmechanics/autocalcspell.cpp | 2 +- apps/openmw/mwmechanics/enchanting.cpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 4 +- apps/openmw/mwmechanics/spellcasting.cpp | 409 +----------------- apps/openmw/mwmechanics/spellcasting.hpp | 51 --- apps/openmw/mwmechanics/spellpriority.cpp | 2 +- apps/openmw/mwmechanics/spellresistance.cpp | 2 +- apps/openmw/mwmechanics/spellutil.cpp | 208 +++++++++ apps/openmw/mwmechanics/spellutil.hpp | 50 +++ apps/openmw/mwmechanics/tickableeffects.cpp | 218 ++++++++++ apps/openmw/mwmechanics/tickableeffects.hpp | 19 + apps/openmw/mwmechanics/weaponpriority.cpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 2 +- apps/openmw/mwworld/player.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 32 +- 23 files changed, 544 insertions(+), 475 deletions(-) create mode 100644 apps/openmw/mwmechanics/spellutil.cpp create mode 100644 apps/openmw/mwmechanics/spellutil.hpp create mode 100644 apps/openmw/mwmechanics/tickableeffects.cpp create mode 100644 apps/openmw/mwmechanics/tickableeffects.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 3395b5fc8..bee3641f8 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -85,7 +85,7 @@ add_openmw_dir (mwmechanics drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning - character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype + character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype spellutil tickableeffects ) add_openmw_dir (mwstate diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 39278f0fa..8449e6a5b 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -19,7 +19,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spellutil.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 23f24e321..a567d114b 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -15,10 +15,10 @@ #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" +#include "../mwmechanics/spellutil.hpp" #include "tooltips.hpp" #include "class.hpp" diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index cbe664ab1..1dedfa10b 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -7,7 +7,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spellutil.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 41be4f3a8..7776b376a 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -18,7 +18,7 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" -#include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spellutil.hpp" #include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index e3250e5fe..c0db57b1b 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -17,7 +17,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spellutil.hpp" #include "../mwmechanics/actorutil.hpp" #include "mapwindow.hpp" diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 726b2a31f..ba3dc1725 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -40,6 +40,7 @@ #include "summoning.hpp" #include "combat.hpp" #include "actorutil.hpp" +#include "tickableeffects.hpp" namespace { diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 9f698b630..c26454aab 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -14,7 +14,6 @@ #include "../mwworld/cellstore.hpp" #include "npcstats.hpp" -#include "spellcasting.hpp" #include "combat.hpp" #include "weaponpriority.hpp" #include "spellpriority.hpp" diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index f55bebfc9..6d3090918 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -1,5 +1,4 @@ #include "autocalcspell.hpp" -#include "spellcasting.hpp" #include @@ -8,6 +7,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "spellutil.hpp" namespace MWMechanics { diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index fdf25b7c6..c71516090 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -13,7 +13,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "creaturestats.hpp" -#include "spellcasting.hpp" +#include "spellutil.hpp" #include "actorutil.hpp" #include "weapontype.hpp" diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 695abe105..25b33c486 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -21,7 +21,7 @@ #include "aicombat.hpp" #include "aipursue.hpp" -#include "spellcasting.hpp" +#include "spellutil.hpp" #include "autocalcspell.hpp" #include "npcstats.hpp" #include "actorutil.hpp" @@ -376,7 +376,7 @@ namespace MWMechanics { const std::string& spell = winMgr->getSelectedSpell(); if (!spell.empty()) - winMgr->setSelectedSpell(spell, int(MWMechanics::getSpellSuccessChance(spell, mWatched))); + winMgr->setSelectedSpell(spell, int(getSpellSuccessChance(spell, mWatched))); else winMgr->unsetSelectedSpell(); } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d5ddd9a55..bd61e7798 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -1,8 +1,5 @@ #include "spellcasting.hpp" -#include -#include - #include #include #include @@ -29,185 +26,11 @@ #include "weapontype.hpp" #include "summoning.hpp" #include "spellresistance.hpp" +#include "spellutil.hpp" +#include "tickableeffects.hpp" namespace MWMechanics { - ESM::Skill::SkillEnum spellSchoolToSkill(int school) - { - static const std::array schoolSkillArray - { - ESM::Skill::Alteration, ESM::Skill::Conjuration, ESM::Skill::Destruction, - ESM::Skill::Illusion, ESM::Skill::Mysticism, ESM::Skill::Restoration - }; - return schoolSkillArray.at(school); - } - - float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect) - { - if (!magicEffect) - magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); - bool hasMagnitude = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude); - bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); - int minMagn = hasMagnitude ? effect.mMagnMin : 1; - int maxMagn = hasMagnitude ? effect.mMagnMax : 1; - int duration = hasDuration ? effect.mDuration : 1; - static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() - .get().find("fEffectCostMult")->mValue.getFloat(); - - float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn)); - x *= 0.1 * magicEffect->mData.mBaseCost; - x *= 1 + duration; - x += 0.05 * std::max(1, effect.mArea) * magicEffect->mData.mBaseCost; - - return x * fEffectCostMult; - } - - float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool) - { - // Morrowind for some reason uses a formula slightly different from magicka cost calculation - float y = std::numeric_limits::max(); - float lowestSkill = 0; - - for (const ESM::ENAMstruct& effect : spell->mEffects.mList) - { - float x = static_cast(effect.mDuration); - const auto magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); - - if (!(magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce)) - x = std::max(1.f, x); - - x *= 0.1f * magicEffect->mData.mBaseCost; - x *= 0.5f * (effect.mMagnMin + effect.mMagnMax); - x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost; - if (effect.mRange == ESM::RT_Target) - x *= 1.5f; - static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fEffectCostMult")->mValue.getFloat(); - x *= fEffectCostMult; - - float s = 2.0f * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool)); - if (s - x < y) - { - y = s - x; - if (effectiveSchool) - *effectiveSchool = magicEffect->mData.mSchool; - lowestSkill = s; - } - } - - CreatureStats& stats = actor.getClass().getCreatureStats(actor); - - int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - - float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck); - - return castChance; - } - - float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap, bool checkMagicka) - { - bool godmode = actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); - - CreatureStats& stats = actor.getClass().getCreatureStats(actor); - - float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude(); - - float castChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool) + castBonus; - castChance *= stats.getFatigueTerm(); - - if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude()&& !godmode) - return 0; - - if (spell->mData.mType == ESM::Spell::ST_Power) - return stats.getSpells().canUsePower(spell) ? 100 : 0; - - if (spell->mData.mType != ESM::Spell::ST_Spell) - return 100; - - if (checkMagicka && stats.getMagicka().getCurrent() < spell->mData.mCost && !godmode) - return 0; - - if (spell->mData.mFlags & ESM::Spell::F_Always) - return 100; - - if (godmode) - return 100; - - return std::max(0.f, cap ? std::min(100.f, castChance) : castChance); - } - - float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap, bool checkMagicka) - { - if (const auto spell = MWBase::Environment::get().getWorld()->getStore().get().search(spellId)) - return getSpellSuccessChance(spell, actor, effectiveSchool, cap, checkMagicka); - return 0.f; - } - - int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor) - { - int school = 0; - getSpellSuccessChance(spellId, actor, &school); - return school; - } - - int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor) - { - int school = 0; - getSpellSuccessChance(spell, actor, &school); - return school; - } - - bool spellIncreasesSkill(const ESM::Spell *spell) - { - return spell->mData.mType == ESM::Spell::ST_Spell && !(spell->mData.mFlags & ESM::Spell::F_Always); - } - - bool spellIncreasesSkill(const std::string &spellId) - { - const auto spell = MWBase::Environment::get().getWorld()->getStore().get().search(spellId); - return spell && spellIncreasesSkill(spell); - } - - /// Check if the given effect can be applied to the target. If \a castByPlayer, emits a message box on failure. - bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer) - { - switch (effectId) - { - case ESM::MagicEffect::Levitate: - if (!MWBase::Environment::get().getWorld()->isLevitationEnabled()) - { - if (castByPlayer) - MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}"); - return false; - } - break; - case ESM::MagicEffect::Soultrap: - if (!target.getClass().isNpc() // no messagebox for NPCs - && (target.getTypeName() == typeid(ESM::Creature).name() && target.get()->mBase->mData.mSoul == 0)) - { - if (castByPlayer) - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInvalidTarget}"); - return true; // must still apply to get visual effect and have target regard it as attack - } - break; - case ESM::MagicEffect::WaterWalking: - if (target.getClass().isPureWaterCreature(target) && MWBase::Environment::get().getWorld()->isSwimming(target)) - return false; - - MWBase::World *world = MWBase::Environment::get().getWorld(); - - if (!world->isWaterWalkingCastableOnTarget(target)) - { - if (castByPlayer && caster == target) - MWBase::Environment::get().getWindowManager()->messageBox ("#{sMagicInvalidEffect}"); - return false; - } - break; - } - return true; - } - class GetAbsorptionProbability : public MWMechanics::EffectSourceVisitor { public: @@ -1016,232 +839,4 @@ namespace MWMechanics { return !mManualSpell && MWMechanics::spellIncreasesSkill(mId); } - - int getEffectiveEnchantmentCastCost(float castCost, const MWWorld::Ptr &actor) - { - /* - * Each point of enchant skill above/under 10 subtracts/adds - * one percent of enchantment cost while minimum is 1. - */ - int eSkill = actor.getClass().getSkill(actor, ESM::Skill::Enchant); - const float result = castCost - (castCost / 100) * (eSkill - 10); - - return static_cast((result < 1) ? 1 : result); - } - - bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) - { - if (ptr.getClass().hasInventoryStore(ptr)) - { - MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); - MWWorld::ContainerStoreIterator item = inv.getSlot(slot); - - if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor || item.getType() == MWWorld::ContainerStore::Type_Weapon)) - { - if (!item->getClass().hasItemHealth(*item)) - return false; - int charge = item->getClass().getItemHealth(*item); - - if (charge == 0) - return false; - - // Store remainder of disintegrate amount (automatically subtracted if > 1) - item->getCellRef().applyChargeRemainderToBeSubtracted(disintegrate - std::floor(disintegrate)); - - charge = item->getClass().getItemHealth(*item); - charge -= std::min(static_cast(disintegrate), charge); - item->getCellRef().setCharge(charge); - - if (charge == 0) - { - // Will unequip the broken item and try to find a replacement - if (ptr != getPlayer()) - inv.autoEquip(ptr); - else - inv.unequipItem(*item, ptr); - } - - return true; - } - } - return false; - } - - void adjustDynamicStat(CreatureStats& creatureStats, int index, float magnitude, bool allowDecreaseBelowZero = false) - { - DynamicStat stat = creatureStats.getDynamic(index); - stat.setCurrent(stat.getCurrent() + magnitude, allowDecreaseBelowZero); - creatureStats.setDynamic(index, stat); - } - - bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey &effectKey, float magnitude) - { - if (magnitude == 0.f) - return false; - - bool receivedMagicDamage = false; - - switch (effectKey.mId) - { - case ESM::MagicEffect::DamageAttribute: - { - AttributeValue attr = creatureStats.getAttribute(effectKey.mArg); - attr.damage(magnitude); - creatureStats.setAttribute(effectKey.mArg, attr); - break; - } - case ESM::MagicEffect::RestoreAttribute: - { - AttributeValue attr = creatureStats.getAttribute(effectKey.mArg); - attr.restore(magnitude); - creatureStats.setAttribute(effectKey.mArg, attr); - break; - } - case ESM::MagicEffect::RestoreHealth: - case ESM::MagicEffect::RestoreMagicka: - case ESM::MagicEffect::RestoreFatigue: - adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::RestoreHealth, magnitude); - break; - case ESM::MagicEffect::DamageHealth: - receivedMagicDamage = true; - adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude); - break; - - case ESM::MagicEffect::DamageMagicka: - case ESM::MagicEffect::DamageFatigue: - { - int index = effectKey.mId-ESM::MagicEffect::DamageHealth; - static const bool uncappedDamageFatigue = Settings::Manager::getBool("uncapped damage fatigue", "Game"); - adjustDynamicStat(creatureStats, index, -magnitude, index == 2 && uncappedDamageFatigue); - break; - } - case ESM::MagicEffect::AbsorbHealth: - if (magnitude > 0.f) - receivedMagicDamage = true; - adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude); - - break; - - case ESM::MagicEffect::AbsorbMagicka: - case ESM::MagicEffect::AbsorbFatigue: - adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude); - break; - - case ESM::MagicEffect::DisintegrateArmor: - { - // According to UESP - int priorities[] = { - MWWorld::InventoryStore::Slot_CarriedLeft, - MWWorld::InventoryStore::Slot_Cuirass, - MWWorld::InventoryStore::Slot_LeftPauldron, - MWWorld::InventoryStore::Slot_RightPauldron, - MWWorld::InventoryStore::Slot_LeftGauntlet, - MWWorld::InventoryStore::Slot_RightGauntlet, - MWWorld::InventoryStore::Slot_Helmet, - MWWorld::InventoryStore::Slot_Greaves, - MWWorld::InventoryStore::Slot_Boots - }; - - for (unsigned int i=0; iisExterior()) - break; - float time = MWBase::Environment::get().getWorld()->getTimeStamp().getHour(); - float timeDiff = std::min(7.f, std::max(0.f, std::abs(time - 13))); - float damageScale = 1.f - timeDiff / 7.f; - // When cloudy, the sun damage effect is halved - static float fMagicSunBlockedMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fMagicSunBlockedMult")->mValue.getFloat(); - - int weather = MWBase::Environment::get().getWorld()->getCurrentWeather(); - if (weather > 1) - damageScale *= fMagicSunBlockedMult; - - adjustDynamicStat(creatureStats, 0, -magnitude * damageScale); - if (magnitude * damageScale > 0.f) - receivedMagicDamage = true; - - break; - } - - case ESM::MagicEffect::FireDamage: - case ESM::MagicEffect::ShockDamage: - case ESM::MagicEffect::FrostDamage: - case ESM::MagicEffect::Poison: - { - adjustDynamicStat(creatureStats, 0, -magnitude); - receivedMagicDamage = true; - break; - } - - case ESM::MagicEffect::DamageSkill: - case ESM::MagicEffect::RestoreSkill: - { - if (!actor.getClass().isNpc()) - break; - NpcStats &npcStats = actor.getClass().getNpcStats(actor); - SkillValue& skill = npcStats.getSkill(effectKey.mArg); - if (effectKey.mId == ESM::MagicEffect::RestoreSkill) - skill.restore(magnitude); - else - skill.damage(magnitude); - break; - } - - case ESM::MagicEffect::CurePoison: - actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); - break; - case ESM::MagicEffect::CureParalyzation: - actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze); - break; - case ESM::MagicEffect::CureCommonDisease: - actor.getClass().getCreatureStats(actor).getSpells().purgeCommonDisease(); - break; - case ESM::MagicEffect::CureBlightDisease: - actor.getClass().getCreatureStats(actor).getSpells().purgeBlightDisease(); - break; - case ESM::MagicEffect::CureCorprusDisease: - actor.getClass().getCreatureStats(actor).getSpells().purgeCorprusDisease(); - break; - case ESM::MagicEffect::RemoveCurse: - actor.getClass().getCreatureStats(actor).getSpells().purgeCurses(); - break; - default: - return false; - } - - if (receivedMagicDamage && actor == getPlayer()) - MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); - return true; - } - - void ApplyLoopingParticlesVisitor::visit (MWMechanics::EffectKey key, - const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/, - float /*magnitude*/, float /*remainingTime*/, float /*totalTime*/) - { - const auto magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(key.mId); - if ((magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) == 0) - return; - const ESM::Static* castStatic; - if (!magicEffect->mHit.empty()) - castStatic = MWBase::Environment::get().getWorld()->getStore().get().find (magicEffect->mHit); - else - castStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_DefaultHit"); - MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mActor); - if (anim && !castStatic->mModel.empty()) - anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, /*loop*/true, "", magicEffect->mParticle); - } } diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 91cf37272..3fcec8f4a 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -2,13 +2,10 @@ #define MWMECHANICS_SPELLCASTING_H #include -#include #include #include "../mwworld/ptr.hpp" -#include "magiceffects.hpp" - namespace ESM { struct Spell; @@ -23,38 +20,6 @@ namespace MWMechanics class MagicEffects; class CreatureStats; - ESM::Skill::SkillEnum spellSchoolToSkill(int school); - - float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect = nullptr); - - /** - * @param spell spell to cast - * @param actor calculate spell success chance for this actor (depends on actor's skills) - * @param effectiveSchool the spell's effective school (relevant for skill progress) will be written here - * @param cap cap the result to 100%? - * @param checkMagicka check magicka? - * @note actor can be an NPC or a creature - * @return success chance from 0 to 100 (in percent), if cap=false then chance above 100 may be returned. - */ - float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=true); - float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=true); - - int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor); - int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); - - /// Get whether or not the given spell contributes to skill progress. - bool spellIncreasesSkill(const ESM::Spell* spell); - bool spellIncreasesSkill(const std::string& spellId); - - bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer); - - int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor); - float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool); - - /// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed - /// @return Was the effect a tickable effect with a magnitude? - bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const MWMechanics::EffectKey& effectKey, float magnitude); - class CastSpell { private: @@ -105,22 +70,6 @@ namespace MWMechanics /// @return was the target suitable for the effect? bool applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); }; - - class ApplyLoopingParticlesVisitor : public EffectSourceVisitor - { - private: - MWWorld::Ptr mActor; - - public: - ApplyLoopingParticlesVisitor(const MWWorld::Ptr& actor) - : mActor(actor) - { - } - - virtual void visit (MWMechanics::EffectKey key, - const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/, - float /*magnitude*/, float /*remainingTime*/ = -1, float /*totalTime*/ = -1); - }; } #endif diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index a529781cb..9428beafc 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -15,11 +15,11 @@ #include "../mwworld/cellstore.hpp" #include "creaturestats.hpp" -#include "spellcasting.hpp" #include "spellresistance.hpp" #include "weapontype.hpp" #include "combat.hpp" #include "summoning.hpp" +#include "spellutil.hpp" namespace { diff --git a/apps/openmw/mwmechanics/spellresistance.cpp b/apps/openmw/mwmechanics/spellresistance.cpp index bbb4b56a5..4868a7a25 100644 --- a/apps/openmw/mwmechanics/spellresistance.cpp +++ b/apps/openmw/mwmechanics/spellresistance.cpp @@ -9,7 +9,7 @@ #include "../mwworld/esmstore.hpp" #include "creaturestats.hpp" -#include "spellcasting.hpp" +#include "spellutil.hpp" namespace MWMechanics { diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp new file mode 100644 index 000000000..cce07f9e3 --- /dev/null +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -0,0 +1,208 @@ +#include "spellutil.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +#include "actorutil.hpp" +#include "creaturestats.hpp" + +namespace MWMechanics +{ + ESM::Skill::SkillEnum spellSchoolToSkill(int school) + { + static const std::array schoolSkillArray + { + ESM::Skill::Alteration, ESM::Skill::Conjuration, ESM::Skill::Destruction, + ESM::Skill::Illusion, ESM::Skill::Mysticism, ESM::Skill::Restoration + }; + return schoolSkillArray.at(school); + } + + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect) + { + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + if (!magicEffect) + magicEffect = store.get().find(effect.mEffectID); + bool hasMagnitude = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude); + bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); + int minMagn = hasMagnitude ? effect.mMagnMin : 1; + int maxMagn = hasMagnitude ? effect.mMagnMax : 1; + int duration = hasDuration ? effect.mDuration : 1; + static const float fEffectCostMult = store.get().find("fEffectCostMult")->mValue.getFloat(); + + float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn)); + x *= 0.1 * magicEffect->mData.mBaseCost; + x *= 1 + duration; + x += 0.05 * std::max(1, effect.mArea) * magicEffect->mData.mBaseCost; + + return x * fEffectCostMult; + } + + int getEffectiveEnchantmentCastCost(float castCost, const MWWorld::Ptr &actor) + { + /* + * Each point of enchant skill above/under 10 subtracts/adds + * one percent of enchantment cost while minimum is 1. + */ + int eSkill = actor.getClass().getSkill(actor, ESM::Skill::Enchant); + const float result = castCost - (castCost / 100) * (eSkill - 10); + + return static_cast((result < 1) ? 1 : result); + } + + float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool) + { + // Morrowind for some reason uses a formula slightly different from magicka cost calculation + float y = std::numeric_limits::max(); + float lowestSkill = 0; + + for (const ESM::ENAMstruct& effect : spell->mEffects.mList) + { + float x = static_cast(effect.mDuration); + const auto magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); + + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce)) + x = std::max(1.f, x); + + x *= 0.1f * magicEffect->mData.mBaseCost; + x *= 0.5f * (effect.mMagnMin + effect.mMagnMax); + x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost; + if (effect.mRange == ESM::RT_Target) + x *= 1.5f; + static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find( + "fEffectCostMult")->mValue.getFloat(); + x *= fEffectCostMult; + + float s = 2.0f * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool)); + if (s - x < y) + { + y = s - x; + if (effectiveSchool) + *effectiveSchool = magicEffect->mData.mSchool; + lowestSkill = s; + } + } + + CreatureStats& stats = actor.getClass().getCreatureStats(actor); + + int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + + float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck); + + return castChance; + } + + float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap, bool checkMagicka) + { + bool godmode = actor == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); + + CreatureStats& stats = actor.getClass().getCreatureStats(actor); + + if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() && !godmode) + return 0; + + if (spell->mData.mType == ESM::Spell::ST_Power) + return stats.getSpells().canUsePower(spell) ? 100 : 0; + + if (godmode) + return 100; + + if (spell->mData.mType != ESM::Spell::ST_Spell) + return 100; + + if (checkMagicka && stats.getMagicka().getCurrent() < spell->mData.mCost) + return 0; + + if (spell->mData.mFlags & ESM::Spell::F_Always) + return 100; + + float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude(); + float castChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool) + castBonus; + castChance *= stats.getFatigueTerm(); + + return std::max(0.f, cap ? std::min(100.f, castChance) : castChance); + } + + float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap, bool checkMagicka) + { + if (const auto spell = MWBase::Environment::get().getWorld()->getStore().get().search(spellId)) + return getSpellSuccessChance(spell, actor, effectiveSchool, cap, checkMagicka); + return 0.f; + } + + int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor) + { + int school = 0; + getSpellSuccessChance(spellId, actor, &school); + return school; + } + + int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor) + { + int school = 0; + getSpellSuccessChance(spell, actor, &school); + return school; + } + + bool spellIncreasesSkill(const ESM::Spell *spell) + { + return spell->mData.mType == ESM::Spell::ST_Spell && !(spell->mData.mFlags & ESM::Spell::F_Always); + } + + bool spellIncreasesSkill(const std::string &spellId) + { + const auto spell = MWBase::Environment::get().getWorld()->getStore().get().search(spellId); + return spell && spellIncreasesSkill(spell); + } + + bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer) + { + switch (effectId) + { + case ESM::MagicEffect::Levitate: + { + if (!MWBase::Environment::get().getWorld()->isLevitationEnabled()) + { + if (castByPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}"); + return false; + } + break; + } + case ESM::MagicEffect::Soultrap: + { + if (!target.getClass().isNpc() // no messagebox for NPCs + && (target.getTypeName() == typeid(ESM::Creature).name() && target.get()->mBase->mData.mSoul == 0)) + { + if (castByPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInvalidTarget}"); + return true; // must still apply to get visual effect and have target regard it as attack + } + break; + } + case ESM::MagicEffect::WaterWalking: + { + if (target.getClass().isPureWaterCreature(target) && MWBase::Environment::get().getWorld()->isSwimming(target)) + return false; + + MWBase::World *world = MWBase::Environment::get().getWorld(); + + if (!world->isWaterWalkingCastableOnTarget(target)) + { + if (castByPlayer && caster == target) + MWBase::Environment::get().getWindowManager()->messageBox ("#{sMagicInvalidEffect}"); + return false; + } + break; + } + } + return true; + } +} diff --git a/apps/openmw/mwmechanics/spellutil.hpp b/apps/openmw/mwmechanics/spellutil.hpp new file mode 100644 index 000000000..865a9126e --- /dev/null +++ b/apps/openmw/mwmechanics/spellutil.hpp @@ -0,0 +1,50 @@ +#ifndef MWMECHANICS_SPELLUTIL_H +#define MWMECHANICS_SPELLUTIL_H + +#include + +namespace ESM +{ + struct ENAMstruct; + struct MagicEffect; + struct Spell; +} + +namespace MWWorld +{ + class Ptr; +} + +namespace MWMechanics +{ + ESM::Skill::SkillEnum spellSchoolToSkill(int school); + + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect = nullptr); + + int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor); + + /** + * @param spell spell to cast + * @param actor calculate spell success chance for this actor (depends on actor's skills) + * @param effectiveSchool the spell's effective school (relevant for skill progress) will be written here + * @param cap cap the result to 100%? + * @param checkMagicka check magicka? + * @note actor can be an NPC or a creature + * @return success chance from 0 to 100 (in percent), if cap=false then chance above 100 may be returned. + */ + float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool); + float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=true); + float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=true); + + int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor); + int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); + + /// Get whether or not the given spell contributes to skill progress. + bool spellIncreasesSkill(const ESM::Spell* spell); + bool spellIncreasesSkill(const std::string& spellId); + + /// Check if the given effect can be applied to the target. If \a castByPlayer, emits a message box on failure. + bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer); +} + +#endif diff --git a/apps/openmw/mwmechanics/tickableeffects.cpp b/apps/openmw/mwmechanics/tickableeffects.cpp new file mode 100644 index 000000000..2e48375c9 --- /dev/null +++ b/apps/openmw/mwmechanics/tickableeffects.cpp @@ -0,0 +1,218 @@ +#include "tickableeffects.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/cellstore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/inventorystore.hpp" + +#include "actorutil.hpp" +#include "npcstats.hpp" + +namespace MWMechanics +{ + void adjustDynamicStat(CreatureStats& creatureStats, int index, float magnitude, bool allowDecreaseBelowZero = false) + { + DynamicStat stat = creatureStats.getDynamic(index); + stat.setCurrent(stat.getCurrent() + magnitude, allowDecreaseBelowZero); + creatureStats.setDynamic(index, stat); + } + + bool disintegrateSlot (const MWWorld::Ptr& ptr, int slot, float disintegrate) + { + if (!ptr.getClass().hasInventoryStore(ptr)) + return false; + + MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); + MWWorld::ContainerStoreIterator item = inv.getSlot(slot); + + if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor || item.getType() == MWWorld::ContainerStore::Type_Weapon)) + { + if (!item->getClass().hasItemHealth(*item)) + return false; + int charge = item->getClass().getItemHealth(*item); + if (charge == 0) + return false; + + // Store remainder of disintegrate amount (automatically subtracted if > 1) + item->getCellRef().applyChargeRemainderToBeSubtracted(disintegrate - std::floor(disintegrate)); + + charge = item->getClass().getItemHealth(*item); + charge -= std::min(static_cast(disintegrate), charge); + item->getCellRef().setCharge(charge); + + if (charge == 0) + { + // Will unequip the broken item and try to find a replacement + if (ptr != getPlayer()) + inv.autoEquip(ptr); + else + inv.unequipItem(*item, ptr); + } + + return true; + } + + return false; + } + + bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey &effectKey, float magnitude) + { + if (magnitude == 0.f) + return false; + + bool receivedMagicDamage = false; + + switch (effectKey.mId) + { + case ESM::MagicEffect::DamageAttribute: + { + AttributeValue attr = creatureStats.getAttribute(effectKey.mArg); + attr.damage(magnitude); + creatureStats.setAttribute(effectKey.mArg, attr); + break; + } + case ESM::MagicEffect::RestoreAttribute: + { + AttributeValue attr = creatureStats.getAttribute(effectKey.mArg); + attr.restore(magnitude); + creatureStats.setAttribute(effectKey.mArg, attr); + break; + } + case ESM::MagicEffect::RestoreHealth: + case ESM::MagicEffect::RestoreMagicka: + case ESM::MagicEffect::RestoreFatigue: + adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::RestoreHealth, magnitude); + break; + case ESM::MagicEffect::DamageHealth: + receivedMagicDamage = true; + adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude); + break; + + case ESM::MagicEffect::DamageMagicka: + case ESM::MagicEffect::DamageFatigue: + { + int index = effectKey.mId-ESM::MagicEffect::DamageHealth; + static const bool uncappedDamageFatigue = Settings::Manager::getBool("uncapped damage fatigue", "Game"); + adjustDynamicStat(creatureStats, index, -magnitude, index == 2 && uncappedDamageFatigue); + break; + } + case ESM::MagicEffect::AbsorbHealth: + if (magnitude > 0.f) + receivedMagicDamage = true; + adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude); + + break; + + case ESM::MagicEffect::AbsorbMagicka: + case ESM::MagicEffect::AbsorbFatigue: + adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude); + break; + + case ESM::MagicEffect::DisintegrateArmor: + { + // According to UESP + int priorities[] = { + MWWorld::InventoryStore::Slot_CarriedLeft, + MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_LeftPauldron, + MWWorld::InventoryStore::Slot_RightPauldron, + MWWorld::InventoryStore::Slot_LeftGauntlet, + MWWorld::InventoryStore::Slot_RightGauntlet, + MWWorld::InventoryStore::Slot_Helmet, + MWWorld::InventoryStore::Slot_Greaves, + MWWorld::InventoryStore::Slot_Boots + }; + + for (unsigned int i=0; iisExterior()) + break; + float time = MWBase::Environment::get().getWorld()->getTimeStamp().getHour(); + float timeDiff = std::min(7.f, std::max(0.f, std::abs(time - 13))); + float damageScale = 1.f - timeDiff / 7.f; + // When cloudy, the sun damage effect is halved + static float fMagicSunBlockedMult = MWBase::Environment::get().getWorld()->getStore().get().find( + "fMagicSunBlockedMult")->mValue.getFloat(); + + int weather = MWBase::Environment::get().getWorld()->getCurrentWeather(); + if (weather > 1) + damageScale *= fMagicSunBlockedMult; + + adjustDynamicStat(creatureStats, 0, -magnitude * damageScale); + if (magnitude * damageScale > 0.f) + receivedMagicDamage = true; + + break; + } + + case ESM::MagicEffect::FireDamage: + case ESM::MagicEffect::ShockDamage: + case ESM::MagicEffect::FrostDamage: + case ESM::MagicEffect::Poison: + { + adjustDynamicStat(creatureStats, 0, -magnitude); + receivedMagicDamage = true; + break; + } + + case ESM::MagicEffect::DamageSkill: + case ESM::MagicEffect::RestoreSkill: + { + if (!actor.getClass().isNpc()) + break; + NpcStats &npcStats = actor.getClass().getNpcStats(actor); + SkillValue& skill = npcStats.getSkill(effectKey.mArg); + if (effectKey.mId == ESM::MagicEffect::RestoreSkill) + skill.restore(magnitude); + else + skill.damage(magnitude); + break; + } + + case ESM::MagicEffect::CurePoison: + actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); + break; + case ESM::MagicEffect::CureParalyzation: + actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze); + break; + case ESM::MagicEffect::CureCommonDisease: + actor.getClass().getCreatureStats(actor).getSpells().purgeCommonDisease(); + break; + case ESM::MagicEffect::CureBlightDisease: + actor.getClass().getCreatureStats(actor).getSpells().purgeBlightDisease(); + break; + case ESM::MagicEffect::CureCorprusDisease: + actor.getClass().getCreatureStats(actor).getSpells().purgeCorprusDisease(); + break; + case ESM::MagicEffect::RemoveCurse: + actor.getClass().getCreatureStats(actor).getSpells().purgeCurses(); + break; + default: + return false; + } + + if (receivedMagicDamage && actor == getPlayer()) + MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); + return true; + } +} diff --git a/apps/openmw/mwmechanics/tickableeffects.hpp b/apps/openmw/mwmechanics/tickableeffects.hpp new file mode 100644 index 000000000..c4abed6a3 --- /dev/null +++ b/apps/openmw/mwmechanics/tickableeffects.hpp @@ -0,0 +1,19 @@ +#ifndef MWMECHANICS_TICKABLEEFFECTS_H +#define MWMECHANICS_TICKABLEEFFECTS_H + +namespace MWWorld +{ + class Ptr; +} + +namespace MWMechanics +{ + class CreatureStats; + struct EffectKey; + + /// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed + /// @return Was the effect a tickable effect with a magnitude? + bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey& effectKey, float magnitude); +} + +#endif diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 2e6501225..13ce30927 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -13,7 +13,7 @@ #include "combat.hpp" #include "aicombataction.hpp" #include "spellpriority.hpp" -#include "spellcasting.hpp" +#include "spellutil.hpp" #include "weapontype.hpp" namespace MWMechanics diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 1de97d068..d4358532c 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -13,8 +13,8 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellresistance.hpp" +#include "../mwmechanics/spellutil.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/weapontype.hpp" diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 8c5f52655..11444c8eb 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -20,7 +20,7 @@ #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spellutil.hpp" #include "class.hpp" #include "ptr.hpp" diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 37123099a..a623a5d52 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3161,12 +3161,42 @@ namespace MWWorld mProjectileManager->launchMagicBolt(spellId, caster, fallbackDirection); } + class ApplyLoopingParticlesVisitor : public MWMechanics::EffectSourceVisitor + { + private: + MWWorld::Ptr mActor; + + public: + ApplyLoopingParticlesVisitor(const MWWorld::Ptr& actor) + : mActor(actor) + { + } + + virtual void visit (MWMechanics::EffectKey key, + const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/, + float /*magnitude*/, float /*remainingTime*/ = -1, float /*totalTime*/ = -1) + { + const ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + const auto magicEffect = store.get().find(key.mId); + if ((magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) == 0) + return; + const ESM::Static* castStatic; + if (!magicEffect->mHit.empty()) + castStatic = store.get().find (magicEffect->mHit); + else + castStatic = store.get().find ("VFX_DefaultHit"); + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mActor); + if (anim && !castStatic->mModel.empty()) + anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, /*loop*/true, "", magicEffect->mParticle); + } + }; + void World::applyLoopingParticles(const MWWorld::Ptr& ptr) { const MWWorld::Class &cls = ptr.getClass(); if (cls.isActor()) { - MWMechanics::ApplyLoopingParticlesVisitor visitor(ptr); + ApplyLoopingParticlesVisitor visitor(ptr); cls.getCreatureStats(ptr).getActiveSpells().visitEffectSources(visitor); cls.getCreatureStats(ptr).getSpells().visitEffectSources(visitor); if (cls.hasInventoryStore(ptr)) From b1d857818d12283b63b2f74bd3025659708b21a6 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Apr 2020 21:37:19 +0300 Subject: [PATCH 016/227] Clean up CastSpell --- apps/openmw/mwmechanics/spellcasting.cpp | 32 ++++++++---------------- apps/openmw/mwmechanics/spellcasting.hpp | 4 --- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index bd61e7798..d78cab84a 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -20,7 +20,7 @@ #include "../mwrender/animation.hpp" -#include "npcstats.hpp" +#include "creaturestats.hpp" #include "actorutil.hpp" #include "aifollow.hpp" #include "weapontype.hpp" @@ -515,16 +515,14 @@ namespace MWMechanics bool CastSpell::cast(const std::string &id) { - if (const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().search (id)) + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + if (const auto spell = store.get().search(id)) return cast(spell); - if (const ESM::Potion *potion = - MWBase::Environment::get().getWorld()->getStore().get().search (id)) + if (const auto potion = store.get().search(id)) return cast(potion); - if (const ESM::Ingredient *ingredient = - MWBase::Environment::get().getWorld()->getStore().get().search (id)) + if (const auto ingredient = store.get().search(id)) return cast(ingredient); throw std::runtime_error("ID type cannot be casted"); @@ -687,10 +685,9 @@ namespace MWMechanics stats.getSpells().usePower(spell); } - if (mCaster == getPlayer() && spellIncreasesSkill()) - mCaster.getClass().skillUsageSucceeded(mCaster, - spellSchoolToSkill(school), 0); - + if (!mManualSpell && mCaster == getPlayer() && spellIncreasesSkill(spell)) + mCaster.getClass().skillUsageSucceeded(mCaster, spellSchoolToSkill(school), 0); + // A non-actor doesn't play its spell cast effects from a character controller, so play them here if (!mCaster.getClass().isActor()) playSpellCastingEffects(spell->mEffects.mList); @@ -718,10 +715,8 @@ namespace MWMechanics effect.mRange = ESM::RT_Self; effect.mArea = 0; - const ESM::MagicEffect *magicEffect = - MWBase::Environment::get().getWorld()->getStore().get().find ( - effect.mEffectID); - + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + const auto magicEffect = store.get().find(effect.mEffectID); const MWMechanics::CreatureStats& creatureStats = mCaster.getClass().getCreatureStats(mCaster); float x = (mCaster.getClass().getSkill(mCaster, ESM::Skill::Alchemy) + @@ -733,7 +728,7 @@ namespace MWMechanics if (roll > x) { // "X has no effect on you" - std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage50")->mValue.getString(); + std::string message = store.get().find("sNotifyMessage50")->mValue.getString(); message = Misc::StringUtils::format(message, ingredient->mName); MWBase::Environment::get().getWindowManager()->messageBox(message); return false; @@ -834,9 +829,4 @@ namespace MWMechanics sndMgr->playSound3D(mCaster, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f); } } - - bool CastSpell::spellIncreasesSkill() - { - return !mManualSpell && MWMechanics::spellIncreasesSkill(mId); - } } diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 3fcec8f4a..45431bbc6 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -17,8 +17,6 @@ namespace ESM namespace MWMechanics { struct EffectKey; - class MagicEffects; - class CreatureStats; class CastSpell { @@ -56,8 +54,6 @@ namespace MWMechanics void playSpellCastingEffects(const std::string &spellid, bool enchantment); - bool spellIncreasesSkill(); - /// Launch a bolt with the given effects. void launchMagicBolt (); From bd1ef4dd6d1f314dab61b86b6a18c5c5f2052079 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 22 Feb 2020 13:03:44 -0800 Subject: [PATCH 017/227] Add detournavigator test for multiple worker threads --- .../detournavigator/navigator.cpp | 67 +++++++++++++++++++ .../detournavigator/asyncnavmeshupdater.cpp | 22 ++++-- .../detournavigator/asyncnavmeshupdater.hpp | 1 + components/detournavigator/navmeshmanager.cpp | 2 +- components/misc/guarded.hpp | 8 +++ 5 files changed, 92 insertions(+), 8 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 51370c8e0..71b531513 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -699,4 +699,71 @@ namespace EXPECT_FLOAT_EQ(distance, 85.260780334472656); } + + TEST_F(DetourNavigatorNavigatorTest, multiple_threads_should_lock_tiles) + { + mSettings.mAsyncNavMeshUpdaterThreads = 2; + mNavigator.reset(new NavigatorImpl(mSettings)); + + const std::array heightfieldData {{ + 0, 0, 0, 0, 0, + 0, -25, -25, -25, -25, + 0, -25, -100, -100, -100, + 0, -25, -100, -100, -100, + 0, -25, -100, -100, -100, + }}; + btHeightfieldTerrainShape heightfieldShape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + heightfieldShape.setLocalScaling(btVector3(128, 128, 1)); + + const std::vector boxShapes(100, btVector3(20, 20, 100)); + + mNavigator->addAgent(mAgentHalfExtents); + + mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); + + for (std::size_t i = 0; i < boxShapes.size(); ++i) + { + const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 10, i * 10, i * 10)); + mNavigator->addObject(ObjectId(&boxShapes[i]), boxShapes[i], transform); + } + + std::this_thread::sleep_for(std::chrono::microseconds(1)); + + for (std::size_t i = 0; i < boxShapes.size(); ++i) + { + const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 10 + 1, i * 10 + 1, i * 10 + 1)); + mNavigator->updateObject(ObjectId(&boxShapes[i]), boxShapes[i], transform); + } + + mNavigator->update(mPlayerPosition); + mNavigator->wait(); + + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + + EXPECT_THAT(mPath, ElementsAre( + Vec3fEq(-215, 215, 1.8782780170440673828125), + Vec3fEq(-199.7968292236328125, 191.09100341796875, -3.54875946044921875), + Vec3fEq(-184.5936431884765625, 167.1819915771484375, -8.97846889495849609375), + Vec3fEq(-169.3904571533203125, 143.2729949951171875, -14.40818119049072265625), + Vec3fEq(-154.1872711181640625, 119.363983154296875, -19.837886810302734375), + Vec3fEq(-138.9840850830078125, 95.4549713134765625, -25.2675952911376953125), + Vec3fEq(-123.78090667724609375, 71.54595947265625, -30.6973056793212890625), + Vec3fEq(-108.57772064208984375, 47.63695526123046875, -36.12701416015625), + Vec3fEq(-93.3745269775390625, 23.72794342041015625, -40.754695892333984375), + Vec3fEq(-78.17134857177734375, -0.18106450140476226806640625, -37.128795623779296875), + Vec3fEq(-62.968158721923828125, -24.0900726318359375, -33.50289154052734375), + Vec3fEq(-47.764972686767578125, -47.99908447265625, -30.797946929931640625), + Vec3fEq(-23.8524494171142578125, -63.196746826171875, -33.97112274169921875), + Vec3fEq(0.0600722394883632659912109375, -78.3944091796875, -37.14543914794921875), + Vec3fEq(23.97259521484375, -93.592071533203125, -40.774089813232421875), + Vec3fEq(47.885120391845703125, -108.78974151611328125, -36.051296234130859375), + Vec3fEq(71.797637939453125, -123.98740386962890625, -30.62355804443359375), + Vec3fEq(95.71016693115234375, -139.18505859375, -25.195819854736328125), + Vec3fEq(119.6226806640625, -154.382720947265625, -19.768085479736328125), + Vec3fEq(143.5352020263671875, -169.5803680419921875, -14.34035015106201171875), + Vec3fEq(167.447723388671875, -184.7780303955078125, -8.912616729736328125), + Vec3fEq(191.3602294921875, -199.9756927490234375, -3.48488140106201171875), + Vec3fEq(215, -215, 1.8782813549041748046875) + )) << mPath; + } } diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index da1ac19a7..1c07384b8 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -102,8 +102,11 @@ namespace DetourNavigator void AsyncNavMeshUpdater::wait() { - std::unique_lock lock(mMutex); - mDone.wait(lock, [&] { return mJobs.empty() && getTotalThreadJobsUnsafe() == 0; }); + { + std::unique_lock lock(mMutex); + mDone.wait(lock, [&] { return mJobs.empty() && getTotalThreadJobsUnsafe() == 0; }); + } + mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); } void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const @@ -122,7 +125,7 @@ namespace DetourNavigator void AsyncNavMeshUpdater::process() throw() { - Log(Debug::Debug) << "Start process navigator jobs"; + Log(Debug::Debug) << "Start process navigator jobs by thread=" << std::this_thread::get_id(); while (!mShouldStop) { try @@ -140,12 +143,13 @@ namespace DetourNavigator Log(Debug::Error) << "AsyncNavMeshUpdater::process exception: " << e.what(); } } - Log(Debug::Debug) << "Stop navigator jobs processing"; + Log(Debug::Debug) << "Stop navigator jobs processing by thread=" << std::this_thread::get_id(); } bool AsyncNavMeshUpdater::processJob(const Job& job) { - Log(Debug::Debug) << "Process job for agent=(" << std::fixed << std::setprecision(2) << job.mAgentHalfExtents << ")"; + Log(Debug::Debug) << "Process job for agent=(" << std::fixed << std::setprecision(2) << job.mAgentHalfExtents << ")" + " by thread=" << std::this_thread::get_id(); const auto start = std::chrono::steady_clock::now(); @@ -176,7 +180,8 @@ namespace DetourNavigator " generation=" << locked->getGeneration() << " revision=" << locked->getNavMeshRevision() << " time=" << std::chrono::duration_cast(finish - start).count() << "ms" << - " total_time=" << std::chrono::duration_cast(finish - firstStart).count() << "ms"; + " total_time=" << std::chrono::duration_cast(finish - firstStart).count() << "ms" + " thread=" << std::this_thread::get_id(); return isSuccess(status); } @@ -201,7 +206,7 @@ namespace DetourNavigator } Log(Debug::Debug) << "Got " << mJobs.size() << " navigator jobs and " - << threadQueue.mJobs.size() << " thread jobs"; + << threadQueue.mJobs.size() << " thread jobs by thread=" << std::this_thread::get_id(); auto job = threadQueue.mJobs.empty() ? getJob(mJobs, mPushed) @@ -329,6 +334,9 @@ namespace DetourNavigator if (agent->second.empty()) locked->erase(agent); + + if (locked->empty()) + mProcessed.notify_all(); } std::size_t AsyncNavMeshUpdater::getTotalThreadJobsUnsafe() const diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index c833d617c..6a3799969 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -86,6 +86,7 @@ namespace DetourNavigator mutable std::mutex mMutex; std::condition_variable mHasJob; std::condition_variable mDone; + std::condition_variable mProcessed; Jobs mJobs; std::map> mPushed; Misc::ScopeGuarded mPlayerTile; diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index b6c25bd93..2424a51e3 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -191,7 +191,7 @@ namespace DetourNavigator mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, tilesToPost); if (changedTiles != mChangedTiles.end()) changedTiles->second.clear(); - Log(Debug::Debug) << "cache update posted for agent=" << agentHalfExtents << + Log(Debug::Debug) << "Cache update posted for agent=" << agentHalfExtents << " playerTile=" << lastPlayerTile->second << " recastMeshManagerRevision=" << lastRevision; } diff --git a/components/misc/guarded.hpp b/components/misc/guarded.hpp index 559476867..55a2c670c 100644 --- a/components/misc/guarded.hpp +++ b/components/misc/guarded.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace Misc { @@ -79,6 +80,13 @@ namespace Misc return Locked(mMutex, mValue); } + template + void wait(std::condition_variable& cv, Predicate&& predicate) + { + std::unique_lock lock(mMutex); + cv.wait(lock, [&] { return predicate(mValue); }); + } + private: std::mutex mMutex; T mValue; From 4c1c30db3350472d024e4391a9f85421985f82de Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 27 Apr 2020 12:03:20 +0300 Subject: [PATCH 018/227] Address akortunov's concerns regarding spell refactoring Separate linked effect handling into linked effects header Separate spell absorption handling into spell absorption header Make armor disintegration loop a range-based for loop --- apps/openmw/CMakeLists.txt | 1 + apps/openmw/mwmechanics/linkedeffects.cpp | 71 +++++++++++++ apps/openmw/mwmechanics/linkedeffects.hpp | 32 ++++++ apps/openmw/mwmechanics/spellabsorption.cpp | 76 ++++++++++++++ apps/openmw/mwmechanics/spellabsorption.hpp | 20 ++++ apps/openmw/mwmechanics/spellcasting.cpp | 109 ++++---------------- apps/openmw/mwmechanics/tickableeffects.cpp | 9 +- 7 files changed, 223 insertions(+), 95 deletions(-) create mode 100644 apps/openmw/mwmechanics/linkedeffects.cpp create mode 100644 apps/openmw/mwmechanics/linkedeffects.hpp create mode 100644 apps/openmw/mwmechanics/spellabsorption.cpp create mode 100644 apps/openmw/mwmechanics/spellabsorption.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index bee3641f8..2b4739ba9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -86,6 +86,7 @@ add_openmw_dir (mwmechanics aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype spellutil tickableeffects + spellabsorption linkedeffects ) add_openmw_dir (mwstate diff --git a/apps/openmw/mwmechanics/linkedeffects.cpp b/apps/openmw/mwmechanics/linkedeffects.cpp new file mode 100644 index 000000000..58a497a59 --- /dev/null +++ b/apps/openmw/mwmechanics/linkedeffects.cpp @@ -0,0 +1,71 @@ +#include "linkedeffects.hpp" + +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwrender/animation.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +#include "creaturestats.hpp" + +namespace MWMechanics +{ + + bool reflectEffect(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, + const MWWorld::Ptr& caster, const MWWorld::Ptr& target, ESM::EffectList& reflectedEffects) + { + if (caster.isEmpty() || caster == target || !target.getClass().isActor()) + return false; + + bool isHarmful = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful; + bool isUnreflectable = magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable; + if (!isHarmful || isUnreflectable) + return false; + + float reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).getMagnitude(); + if (Misc::Rng::roll0to99() >= reflect) + return false; + + const ESM::Static* reflectStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Reflect"); + MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target); + if (animation && !reflectStatic->mModel.empty()) + animation->addEffect("meshes\\" + reflectStatic->mModel, ESM::MagicEffect::Reflect, false, std::string()); + reflectedEffects.mList.emplace_back(effect); + return true; + } + + void absorbStat(const ESM::ENAMstruct& effect, const ESM::ActiveEffect& appliedEffect, + const MWWorld::Ptr& caster, const MWWorld::Ptr& target, bool reflected, const std::string& source) + { + if (caster.isEmpty() || caster == target) + return; + + if (!target.getClass().isActor() || !caster.getClass().isActor()) + return; + + // Make sure callers don't do something weird + if (effect.mEffectID < ESM::MagicEffect::AbsorbAttribute || effect.mEffectID > ESM::MagicEffect::AbsorbSkill) + throw std::runtime_error("invalid absorb stat effect"); + + std::vector absorbEffects; + ActiveSpells::ActiveEffect absorbEffect = appliedEffect; + absorbEffect.mMagnitude *= -1; + absorbEffects.emplace_back(absorbEffect); + + // Morrowind negates reflected Absorb spells so the original caster won't be harmed. + if (reflected && Settings::Manager::getBool("classic reflected absorb spells behavior", "Game")) + { + target.getClass().getCreatureStats(target).getActiveSpells().addSpell(std::string(), true, + absorbEffects, source, caster.getClass().getCreatureStats(caster).getActorId()); + return; + } + + caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell(std::string(), true, + absorbEffects, source, target.getClass().getCreatureStats(target).getActorId()); + } +} diff --git a/apps/openmw/mwmechanics/linkedeffects.hpp b/apps/openmw/mwmechanics/linkedeffects.hpp new file mode 100644 index 000000000..a6dea2a3a --- /dev/null +++ b/apps/openmw/mwmechanics/linkedeffects.hpp @@ -0,0 +1,32 @@ +#ifndef MWMECHANICS_LINKEDEFFECTS_H +#define MWMECHANICS_LINKEDEFFECTS_H + +#include + +namespace ESM +{ + struct ActiveEffect; + struct EffectList; + struct ENAMstruct; + struct MagicEffect; + struct Spell; +} + +namespace MWWorld +{ + class Ptr; +} + +namespace MWMechanics +{ + + // Try to reflect a spell effect. If it's reflected, it's also put into the passed reflected effects list. + bool reflectEffect(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, + const MWWorld::Ptr& caster, const MWWorld::Ptr& target, ESM::EffectList& reflectedEffects); + + // Try to absorb a stat (skill, attribute, etc.) from the target and transfer it to the caster. + void absorbStat(const ESM::ENAMstruct& effect, const ESM::ActiveEffect& appliedEffect, + const MWWorld::Ptr& caster, const MWWorld::Ptr& target, bool reflected, const std::string& source); +} + +#endif diff --git a/apps/openmw/mwmechanics/spellabsorption.cpp b/apps/openmw/mwmechanics/spellabsorption.cpp new file mode 100644 index 000000000..f38fd78e2 --- /dev/null +++ b/apps/openmw/mwmechanics/spellabsorption.cpp @@ -0,0 +1,76 @@ +#include "spellabsorption.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwrender/animation.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/inventorystore.hpp" + +#include "creaturestats.hpp" + +namespace MWMechanics +{ + + class GetAbsorptionProbability : public MWMechanics::EffectSourceVisitor + { + public: + float mProbability{0.f}; + + GetAbsorptionProbability() = default; + + virtual void visit (MWMechanics::EffectKey key, + const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/, + float magnitude, float /*remainingTime*/, float /*totalTime*/) + { + if (key.mId == ESM::MagicEffect::SpellAbsorption) + { + if (mProbability == 0.f) + mProbability = magnitude / 100; + else + { + // If there are different sources of SpellAbsorption effect, multiply failing probability for all effects. + // Real absorption probability will be the (1 - total fail chance) in this case. + float failProbability = 1.f - mProbability; + failProbability *= 1.f - magnitude / 100; + mProbability = 1.f - failProbability; + } + } + } + }; + + bool absorbSpell (const ESM::Spell* spell, const MWWorld::Ptr& caster, const MWWorld::Ptr& target) + { + if (!spell || caster == target || !target.getClass().isActor()) + return false; + + CreatureStats& stats = target.getClass().getCreatureStats(target); + if (stats.getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude() <= 0.f) + return false; + + GetAbsorptionProbability check; + stats.getActiveSpells().visitEffectSources(check); + stats.getSpells().visitEffectSources(check); + if (target.getClass().hasInventoryStore(target)) + target.getClass().getInventoryStore(target).visitEffectSources(check); + + int chance = check.mProbability * 100; + if (Misc::Rng::roll0to99() >= chance) + return false; + + const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find("VFX_Absorb"); + MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target); + if (animation && !absorbStatic->mModel.empty()) + animation->addEffect( "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, std::string()); + // Magicka is increased by the cost of the spell + DynamicStat magicka = stats.getMagicka(); + magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); + stats.setMagicka(magicka); + return true; + } + +} diff --git a/apps/openmw/mwmechanics/spellabsorption.hpp b/apps/openmw/mwmechanics/spellabsorption.hpp new file mode 100644 index 000000000..147090d96 --- /dev/null +++ b/apps/openmw/mwmechanics/spellabsorption.hpp @@ -0,0 +1,20 @@ +#ifndef MWMECHANICS_SPELLABSORPTION_H +#define MWMECHANICS_SPELLABSORPTION_H + +namespace ESM +{ + struct Spell; +} + +namespace MWWorld +{ + class Ptr; +} + +namespace MWMechanics +{ + // Try to absorb a spell based on the magnitude of every Spell Absorption effect source on the target. + bool absorbSpell(const ESM::Spell* spell, const MWWorld::Ptr& caster, const MWWorld::Ptr& target); +} + +#endif diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d78cab84a..4326a43f9 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" @@ -20,44 +19,19 @@ #include "../mwrender/animation.hpp" -#include "creaturestats.hpp" #include "actorutil.hpp" #include "aifollow.hpp" -#include "weapontype.hpp" -#include "summoning.hpp" +#include "creaturestats.hpp" +#include "linkedeffects.hpp" +#include "spellabsorption.hpp" #include "spellresistance.hpp" #include "spellutil.hpp" +#include "summoning.hpp" #include "tickableeffects.hpp" +#include "weapontype.hpp" namespace MWMechanics { - class GetAbsorptionProbability : public MWMechanics::EffectSourceVisitor - { - public: - float mProbability{0.f}; - - GetAbsorptionProbability() = default; - - virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& sourceId, int casterActorId, - float magnitude, float remainingTime = -1, float totalTime = -1) - { - if (key.mId == ESM::MagicEffect::SpellAbsorption) - { - if (mProbability == 0.f) - mProbability = magnitude / 100; - else - { - // If there are different sources of SpellAbsorption effect, multiply failing probability for all effects. - // Real absorption probability will be the (1 - total fail chance) in this case. - float failProbability = 1.f - mProbability; - failProbability *= 1.f - magnitude / 100; - mProbability = 1.f - failProbability; - } - } - } - }; - CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile, const bool manualSpell) : mCaster(caster) , mTarget(target) @@ -172,57 +146,27 @@ namespace MWMechanics && target.getClass().isActor()) MWBase::Environment::get().getWindowManager()->setEnemy(target); - // Try absorbing if it's a spell - // Unlike Reflect, this is done once per spell absorption effect source + // Try absorbing the spell + // FIXME: this should be done only once for the spell bool absorbed = false; - if (spell && caster != target && target.getClass().isActor()) + if (absorbSpell(spell, caster, target)) { - CreatureStats& stats = target.getClass().getCreatureStats(target); - if (stats.getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude() > 0.f) - { - GetAbsorptionProbability check; - stats.getActiveSpells().visitEffectSources(check); - stats.getSpells().visitEffectSources(check); - if (target.getClass().hasInventoryStore(target)) - target.getClass().getInventoryStore(target).visitEffectSources(check); - - int absorb = check.mProbability * 100; - absorbed = (Misc::Rng::roll0to99() < absorb); - if (absorbed) - { - const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); - MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( - "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, ""); - // Magicka is increased by cost of spell - DynamicStat magicka = stats.getMagicka(); - magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); - stats.setMagicka(magicka); - } - } + absorbed = true; + continue; + } + + // Reflect harmful effects + if (!reflected && reflectEffect(*effectIt, magicEffect, caster, target, reflectedEffects)) + { + reflected = true; + continue; } float magnitudeMult = 1; if (target.getClass().isActor()) { - if (absorbed) - continue; - bool isHarmful = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful; - // Reflect harmful effects - if (isHarmful && !reflected && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable)) - { - float reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).getMagnitude(); - bool isReflected = (Misc::Rng::roll0to99() < reflect); - if (isReflected) - { - const ESM::Static* reflectStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Reflect"); - MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( - "meshes\\" + reflectStatic->mModel, ESM::MagicEffect::Reflect, false, ""); - reflectedEffects.mList.push_back(*effectIt); - continue; - } - } // Try resisting magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects); @@ -317,23 +261,8 @@ namespace MWMechanics // For absorb effects, also apply the effect to the caster - but with a negative // magnitude, since we're transferring stats from the target to the caster - if (!caster.isEmpty() && caster != target && caster.getClass().isActor()) - { - if (effectIt->mEffectID >= ESM::MagicEffect::AbsorbAttribute && - effectIt->mEffectID <= ESM::MagicEffect::AbsorbSkill) - { - std::vector absorbEffects; - ActiveSpells::ActiveEffect effect_ = effect; - effect_.mMagnitude *= -1; - absorbEffects.push_back(effect_); - if (reflected && Settings::Manager::getBool("classic reflected absorb spells behavior", "Game")) - target.getClass().getCreatureStats(target).getActiveSpells().addSpell("", true, - absorbEffects, mSourceName, caster.getClass().getCreatureStats(caster).getActorId()); - else - caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, - absorbEffects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); - } - } + if (effectIt->mEffectID >= ESM::MagicEffect::AbsorbAttribute && effectIt->mEffectID <= ESM::MagicEffect::AbsorbSkill) + absorbStat(*effectIt, effect, caster, target, reflected, mSourceName); } } diff --git a/apps/openmw/mwmechanics/tickableeffects.cpp b/apps/openmw/mwmechanics/tickableeffects.cpp index 2e48375c9..31e8c150c 100644 --- a/apps/openmw/mwmechanics/tickableeffects.cpp +++ b/apps/openmw/mwmechanics/tickableeffects.cpp @@ -117,8 +117,8 @@ namespace MWMechanics case ESM::MagicEffect::DisintegrateArmor: { - // According to UESP - int priorities[] = { + static const std::array priorities + { MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_LeftPauldron, @@ -129,10 +129,9 @@ namespace MWMechanics MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Boots }; - - for (unsigned int i=0; i Date: Mon, 27 Apr 2020 12:43:34 +0300 Subject: [PATCH 019/227] Simplify some inflict() logic --- apps/openmw/mwmechanics/linkedeffects.cpp | 3 + apps/openmw/mwmechanics/spellcasting.cpp | 76 +++++++++------------ apps/openmw/mwmechanics/spellresistance.cpp | 3 + 3 files changed, 39 insertions(+), 43 deletions(-) diff --git a/apps/openmw/mwmechanics/linkedeffects.cpp b/apps/openmw/mwmechanics/linkedeffects.cpp index 58a497a59..364358433 100644 --- a/apps/openmw/mwmechanics/linkedeffects.cpp +++ b/apps/openmw/mwmechanics/linkedeffects.cpp @@ -52,6 +52,9 @@ namespace MWMechanics if (effect.mEffectID < ESM::MagicEffect::AbsorbAttribute || effect.mEffectID > ESM::MagicEffect::AbsorbSkill) throw std::runtime_error("invalid absorb stat effect"); + if (appliedEffect.mMagnitude == 0) + return; + std::vector absorbEffects; ActiveSpells::ActiveEffect absorbEffect = appliedEffect; absorbEffect.mMagnitude *= -1; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 4326a43f9..cfea2e7ab 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -113,6 +113,9 @@ namespace MWMechanics // throughout the iteration of this spell's // effects, we display a "can't re-cast" message + // Try absorbing the spell. Some handling must still happen for absorbed effects. + bool absorbed = absorbSpell(spell, caster, target); + for (std::vector::const_iterator effectIt (effects.mList.begin()); !target.isEmpty() && effectIt != effects.mList.end(); ++effectIt) { @@ -140,20 +143,14 @@ namespace MWMechanics && (caster.isEmpty() || !caster.getClass().isActor())) continue; - // If player is healing someone, show the target's HP bar - if (castByPlayer && target != caster - && effectIt->mEffectID == ESM::MagicEffect::RestoreHealth - && target.getClass().isActor()) - MWBase::Environment::get().getWindowManager()->setEnemy(target); + // Notify the target actor they've been hit + bool isHarmful = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful; + if (target.getClass().isActor() && target != caster && !caster.isEmpty() && isHarmful) + target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true); - // Try absorbing the spell - // FIXME: this should be done only once for the spell - bool absorbed = false; - if (absorbSpell(spell, caster, target)) - { - absorbed = true; + // Avoid proceeding further for absorbed spells. + if (absorbed) continue; - } // Reflect harmful effects if (!reflected && reflectEffect(*effectIt, magicEffect, caster, target, reflectedEffects)) @@ -162,37 +159,17 @@ namespace MWMechanics continue; } - float magnitudeMult = 1; - - if (target.getClass().isActor()) + // Try resisting. + float magnitudeMult = getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects); + if (magnitudeMult == 0) { - bool isHarmful = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful; - - // Try resisting - magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects); - if (magnitudeMult == 0) - { - // Fully resisted, show message - if (target == getPlayer()) - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}"); - else if (castByPlayer) - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}"); - } - else if (isHarmful && castByPlayer && target != caster) - { - // If player is attempting to cast a harmful spell and it wasn't fully resisted, show the target's HP bar - MWBase::Environment::get().getWindowManager()->setEnemy(target); - } - - if (target == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState() && isHarmful) - magnitudeMult = 0; - - // Notify the target actor they've been hit - if (target != caster && !caster.isEmpty() && isHarmful) - target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true); + // Fully resisted, show message + if (target == getPlayer()) + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}"); + else if (castByPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}"); } - - if (magnitudeMult > 0 && !absorbed) + else { float magnitude = effectIt->mMagnMin + Misc::Rng::rollDice(effectIt->mMagnMax - effectIt->mMagnMin + 1); magnitude *= magnitudeMult; @@ -219,6 +196,19 @@ namespace MWMechanics effect.mMagnitude = 0; } + // Avoid applying harmful effects to the player in god mode + if (target == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState() && isHarmful) + { + effect.mMagnitude = 0; + } + + bool effectAffectsHealth = isHarmful || effectIt->mEffectID == ESM::MagicEffect::RestoreHealth; + if (castByPlayer && target != caster && effectAffectsHealth) + { + // If player is attempting to cast a harmful spell or is healing someone, show the target's HP bar. + MWBase::Environment::get().getWindowManager()->setEnemy(target); + } + bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); if (hasDuration && effectIt->mDuration == 0) { @@ -228,7 +218,7 @@ namespace MWMechanics // duration 0 means apply full magnitude instantly bool wasDead = target.getClass().getCreatureStats(target).isDead(); - effectTick(target.getClass().getCreatureStats(target), target, EffectKey(*effectIt), magnitude); + effectTick(target.getClass().getCreatureStats(target), target, EffectKey(*effectIt), effect.mMagnitude); bool isDead = target.getClass().getCreatureStats(target).isDead(); if (!wasDead && isDead) @@ -253,7 +243,7 @@ namespace MWMechanics // Command spells should have their effect, including taking the target out of combat, each time the spell successfully affects the target if (((effectIt->mEffectID == ESM::MagicEffect::CommandHumanoid && target.getClass().isNpc()) || (effectIt->mEffectID == ESM::MagicEffect::CommandCreature && target.getTypeName() == typeid(ESM::Creature).name())) - && !caster.isEmpty() && caster.getClass().isActor() && target != getPlayer() && magnitude >= target.getClass().getCreatureStats(target).getLevel()) + && !caster.isEmpty() && caster.getClass().isActor() && target != getPlayer() && effect.mMagnitude >= target.getClass().getCreatureStats(target).getLevel()) { MWMechanics::AiFollow package(caster, true); target.getClass().getCreatureStats(target).getAiSequence().stack(package, target); diff --git a/apps/openmw/mwmechanics/spellresistance.cpp b/apps/openmw/mwmechanics/spellresistance.cpp index 4868a7a25..a187600fb 100644 --- a/apps/openmw/mwmechanics/spellresistance.cpp +++ b/apps/openmw/mwmechanics/spellresistance.cpp @@ -17,6 +17,9 @@ namespace MWMechanics float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell, const MagicEffects* effects) { + if (!actor.getClass().isActor()) + return 1; + float resistance = getEffectResistance(effectId, actor, caster, spell, effects); return 1 - resistance / 100.f; } From 69cd53ef8abd370cf244d8deb305dc7775e100e5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 27 Apr 2020 14:06:50 +0300 Subject: [PATCH 020/227] Fix reflect --- apps/openmw/mwmechanics/spellcasting.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index cfea2e7ab..044a4338e 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -154,10 +154,7 @@ namespace MWMechanics // Reflect harmful effects if (!reflected && reflectEffect(*effectIt, magicEffect, caster, target, reflectedEffects)) - { - reflected = true; continue; - } // Try resisting. float magnitudeMult = getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects); From a3b032bf2bc3f4331f0b35220117978a2609eed3 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Mon, 27 Apr 2020 23:49:48 +0100 Subject: [PATCH 021/227] Fix chameleon shadows --- apps/openmw/mwrender/animation.cpp | 12 ++++++++++-- components/shader/shadervisitor.cpp | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0f7548f05..627d36ac4 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -37,6 +38,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" @@ -502,8 +505,9 @@ namespace MWRender class TransparencyUpdater : public SceneUtil::StateSetUpdater { public: - TransparencyUpdater(const float alpha) + TransparencyUpdater(const float alpha, osg::ref_ptr shadowUniform) : mAlpha(alpha) + , mShadowUniform(shadowUniform) { } @@ -517,6 +521,9 @@ namespace MWRender { osg::BlendFunc* blendfunc (new osg::BlendFunc); stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); + // TODO: don't do this anymore once custom shadow renderbin is handling it + if (mShadowUniform) + stateset->addUniform(mShadowUniform); // FIXME: overriding diffuse/ambient/emissive colors osg::Material* material = new osg::Material; @@ -535,6 +542,7 @@ namespace MWRender private: float mAlpha; + osg::ref_ptr mShadowUniform; }; struct Animation::AnimSource @@ -1744,7 +1752,7 @@ namespace MWRender { if (mTransparencyUpdater == nullptr) { - mTransparencyUpdater = new TransparencyUpdater(alpha); + mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform()); mObjectRoot->addUpdateCallback(mTransparencyUpdater); } else diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 639a7ecca..8165effb1 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -232,7 +232,7 @@ namespace Shader { if (!writableStateSet) writableStateSet = getWritableStateSet(node); - // We probably shouldn't construct a new version of this each time as StateSets only use pointer comparison by default. + // We probably shouldn't construct a new version of this each time as Uniforms use pointer comparison for early-out. // Also it should probably belong to the shader manager writableStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true)); } From c08f9e13af2a9b5319eaab074465e7733bcad50d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 28 Apr 2020 23:53:00 +0300 Subject: [PATCH 022/227] Allow emitters to be attached to nodes after particle systems --- components/nifosg/nifloader.cpp | 45 +++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 7d62d1ef1..15c461bf2 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -219,6 +219,9 @@ namespace NifOsg size_t mFirstRootTextureIndex = -1; bool mFoundFirstRootTexturingProperty = false; + // This is used to queue emitters that weren't attached to their node yet. + std::vector>> mEmitterQueue; + static void loadKf(Nif::NIFFilePtr nif, KeyframeHolder& target) { if(nif->numRoots() < 1) @@ -290,6 +293,9 @@ namespace NifOsg osg::ref_ptr created = handleNode(nifNode, nullptr, imageManager, std::vector(), 0, false, false, false, &textkeys->mTextKeys); + // Attach particle emitters to their nodes which should all be loaded by now. + handleQueuedParticleEmitters(created, nif); + if (nif->getUseSkinning()) { osg::ref_ptr skel = new SceneUtil::Skeleton; @@ -979,6 +985,27 @@ namespace NifOsg return emitter; } + void handleQueuedParticleEmitters(osg::Node* rootNode, Nif::NIFFilePtr nif) + { + for (const auto& emitterPair : mEmitterQueue) + { + size_t recIndex = emitterPair.first; + FindGroupByRecIndex findEmitterNode(recIndex); + rootNode->accept(findEmitterNode); + osg::Group* emitterNode = findEmitterNode.mFound; + if (!emitterNode) + { + nif->warn("Failed to find particle emitter emitter node (node record index " + std::to_string(recIndex) + ")"); + continue; + } + + // Emitter attached to the emitter node. Note one side effect of the emitter using the CullVisitor is that hiding its node + // actually causes the emitter to stop firing. Convenient, because MW behaves this way too! + emitterNode->addChild(emitterPair.second); + } + mEmitterQueue.clear(); + } + void handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, int animflags, osg::Node* rootNode) { osg::ref_ptr partsys (new ParticleSystem); @@ -1028,22 +1055,8 @@ namespace NifOsg emitter->setParticleSystem(partsys); emitter->setReferenceFrame(osgParticle::ParticleProcessor::RELATIVE_RF); - // Note: we assume that the Emitter node is placed *before* the Particle node in the scene graph. - // This seems to be true for all NIF files in the game that I've checked, suggesting that NIFs work similar to OSG with regards to update order. - // If something ever violates this assumption, the worst that could happen is the culling being one frame late, which wouldn't be a disaster. - - FindGroupByRecIndex find (partctrl->emitter->recIndex); - rootNode->accept(find); - if (!find.mFound) - { - Log(Debug::Info) << "can't find emitter node, wrong node order? in " << mFilename; - return; - } - osg::Group* emitterNode = find.mFound; - - // Emitter attached to the emitter node. Note one side effect of the emitter using the CullVisitor is that hiding its node - // actually causes the emitter to stop firing. Convenient, because MW behaves this way too! - emitterNode->addChild(emitter); + // The emitter node may not actually be handled yet, so let's delay attaching the emitter to a later moment. + mEmitterQueue.emplace_back(partctrl->emitter->recIndex, emitter); osg::ref_ptr callback(new ParticleSystemController(partctrl)); setupController(partctrl, callback, animflags); From f516178ec9b42840fc2d626844f402893a0f8cb1 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Apr 2020 11:06:14 +0300 Subject: [PATCH 023/227] Fix particle processor cloning Extend emitter handling comment in NIF loader --- components/nifosg/nifloader.cpp | 4 +++- components/sceneutil/clone.cpp | 12 ++++++++++++ components/sceneutil/clone.hpp | 3 ++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 15c461bf2..f89720dd4 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1056,6 +1056,8 @@ namespace NifOsg emitter->setReferenceFrame(osgParticle::ParticleProcessor::RELATIVE_RF); // The emitter node may not actually be handled yet, so let's delay attaching the emitter to a later moment. + // If the emitter node is placed later than the particle node, it'll have a single frame delay in particle processing. + // But that shouldn't be a game-breaking issue. mEmitterQueue.emplace_back(partctrl->emitter->recIndex, emitter); osg::ref_ptr callback(new ParticleSystemController(partctrl)); @@ -1073,7 +1075,7 @@ namespace NifOsg partsys->update(0.0, nv); } - // affectors must be attached *after* the emitter in the scene graph for correct update order + // affectors should be attached *after* the emitter in the scene graph for correct update order // attach to same node as the ParticleSystem, we need osgParticle Operators to get the correct // localToWorldMatrix for transforming to particle space handleParticlePrograms(partctrl->affectors, partctrl->colliders, parentNode, partsys.get(), rf); diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp index 04fd5d78d..0423d6117 100644 --- a/components/sceneutil/clone.cpp +++ b/components/sceneutil/clone.cpp @@ -69,6 +69,15 @@ namespace SceneUtil osgParticle::ParticleProcessor* CopyOp::operator() (const osgParticle::ParticleProcessor* processor) const { osgParticle::ParticleProcessor* cloned = osg::clone(processor, osg::CopyOp::DEEP_COPY_CALLBACKS); + for (std::map::const_iterator it = mMap3.begin(); it != mMap3.end(); ++it) + { + if (processor->getParticleSystem() == it->first) + { + cloned->setParticleSystem(it->second); + return cloned; + } + } + mMap[cloned] = processor->getParticleSystem(); return cloned; } @@ -93,6 +102,9 @@ namespace SceneUtil updater->addParticleSystem(cloned); } } + // In rare situations a particle processor may be placed after the particle system in the scene graph. + mMap3[partsys] = cloned; + return cloned; } diff --git a/components/sceneutil/clone.hpp b/components/sceneutil/clone.hpp index 8a18eeb20..b0a9d56c3 100644 --- a/components/sceneutil/clone.hpp +++ b/components/sceneutil/clone.hpp @@ -35,10 +35,11 @@ namespace SceneUtil virtual osg::Object* operator ()(const osg::Object* node) const; private: - // maps new ParticleProcessor to their old ParticleSystem pointer + // maps new pointers to their old pointers // a little messy, but I think this should be the most efficient way mutable std::map mMap; mutable std::map mMap2; + mutable std::map mMap3; }; } From 6b874e397b030ef042ae0426551fdceca290dc89 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Apr 2020 12:25:52 +0300 Subject: [PATCH 024/227] Make particle system cloning map names more sensible --- components/sceneutil/clone.cpp | 24 ++++++++++++------------ components/sceneutil/clone.hpp | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp index 0423d6117..0df0f4a5b 100644 --- a/components/sceneutil/clone.cpp +++ b/components/sceneutil/clone.cpp @@ -47,7 +47,7 @@ namespace SceneUtil if (const osgParticle::ParticleSystemUpdater* updater = dynamic_cast(node)) { osgParticle::ParticleSystemUpdater* cloned = new osgParticle::ParticleSystemUpdater(*updater, osg::CopyOp::SHALLOW_COPY); - mMap2[cloned] = updater->getParticleSystem(0); + mUpdaterToOldPs[cloned] = updater->getParticleSystem(0); return cloned; } return osg::CopyOp::operator()(node); @@ -69,16 +69,16 @@ namespace SceneUtil osgParticle::ParticleProcessor* CopyOp::operator() (const osgParticle::ParticleProcessor* processor) const { osgParticle::ParticleProcessor* cloned = osg::clone(processor, osg::CopyOp::DEEP_COPY_CALLBACKS); - for (std::map::const_iterator it = mMap3.begin(); it != mMap3.end(); ++it) + for (const auto& oldPsNewPsPair : mOldPsToNewPs) { - if (processor->getParticleSystem() == it->first) + if (processor->getParticleSystem() == oldPsNewPsPair.first) { - cloned->setParticleSystem(it->second); + cloned->setParticleSystem(oldPsNewPsPair.second); return cloned; } } - mMap[cloned] = processor->getParticleSystem(); + mProcessorToOldPs[cloned] = processor->getParticleSystem(); return cloned; } @@ -86,24 +86,24 @@ namespace SceneUtil { osgParticle::ParticleSystem* cloned = osg::clone(partsys, *this); - for (std::map::const_iterator it = mMap.begin(); it != mMap.end(); ++it) + for (const auto& processorPsPair : mProcessorToOldPs) { - if (it->second == partsys) + if (processorPsPair.second == partsys) { - it->first->setParticleSystem(cloned); + processorPsPair.first->setParticleSystem(cloned); } } - for (std::map::const_iterator it = mMap2.begin(); it != mMap2.end(); ++it) + for (const auto& updaterPsPair : mUpdaterToOldPs) { - if (it->second == partsys) + if (updaterPsPair.second == partsys) { - osgParticle::ParticleSystemUpdater* updater = it->first; + osgParticle::ParticleSystemUpdater* updater = updaterPsPair.first; updater->removeParticleSystem(updater->getParticleSystem(0)); updater->addParticleSystem(cloned); } } // In rare situations a particle processor may be placed after the particle system in the scene graph. - mMap3[partsys] = cloned; + mOldPsToNewPs[partsys] = cloned; return cloned; } diff --git a/components/sceneutil/clone.hpp b/components/sceneutil/clone.hpp index b0a9d56c3..20788799f 100644 --- a/components/sceneutil/clone.hpp +++ b/components/sceneutil/clone.hpp @@ -37,9 +37,9 @@ namespace SceneUtil private: // maps new pointers to their old pointers // a little messy, but I think this should be the most efficient way - mutable std::map mMap; - mutable std::map mMap2; - mutable std::map mMap3; + mutable std::map mProcessorToOldPs; + mutable std::map mUpdaterToOldPs; + mutable std::map mOldPsToNewPs; }; } From 899a6b5aa3d9e21169276cfca6e50bdb0fc29d73 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 29 Apr 2020 13:54:48 +0200 Subject: [PATCH 025/227] Workaround for GCC 5 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61636 --- components/detournavigator/navmeshmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index b6c25bd93..29a297018 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -57,7 +57,7 @@ namespace DetourNavigator const AreaType areaType) { return mRecastMeshManager.updateObject(id, shape, transform, areaType, - [&] (const auto& tile) { addChangedTile(tile, ChangeType::update); }); + [&] (const TilePosition& tile) { addChangedTile(tile, ChangeType::update); }); } bool NavMeshManager::removeObject(const ObjectId id) From 89282d14aa8063f98a089c011d253900594ad774 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Apr 2020 16:20:03 +0300 Subject: [PATCH 026/227] Fix collision switch node mask (again) --- apps/openmw/mwrender/renderingmanager.cpp | 1 + components/nifosg/nifloader.cpp | 14 +++++++++++++- components/nifosg/nifloader.hpp | 6 ++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e631a8fd8..6b41854e0 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -376,6 +376,7 @@ namespace MWRender mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater)); NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor); + NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect); mNearClip = Settings::Manager::getFloat("near clip", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index e77e6b34f..a3972b0e2 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -177,7 +177,7 @@ namespace NifOsg void setEnabled(bool enabled) { - setNodeMask(enabled ? ~0 : 0); + setNodeMask(enabled ? ~0 : Loader::getIntersectionDisabledNodeMask()); } }; @@ -204,6 +204,18 @@ namespace NifOsg return sHiddenNodeMask; } + unsigned int Loader::sIntersectionDisabledNodeMask = ~0; + + void Loader::setIntersectionDisabledNodeMask(unsigned int mask) + { + sIntersectionDisabledNodeMask = mask; + } + + unsigned int Loader::getIntersectionDisabledNodeMask() + { + return sIntersectionDisabledNodeMask; + } + class LoaderImpl { public: diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index 6168bb474..4de9027b8 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -79,8 +79,14 @@ namespace NifOsg static void setHiddenNodeMask(unsigned int mask); static unsigned int getHiddenNodeMask(); + // Set the mask to use for nodes that ignore the crosshair intersection. The default is the default node mask. + // This is used for NiCollisionSwitch nodes with NiCollisionSwitch state set to disabled. + static void setIntersectionDisabledNodeMask(unsigned int mask); + static unsigned int getIntersectionDisabledNodeMask(); + private: static unsigned int sHiddenNodeMask; + static unsigned int sIntersectionDisabledNodeMask; static bool sShowMarkers; }; From 844838c46ac27872133e9492a46786121017a55f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 30 Apr 2020 00:12:39 +0300 Subject: [PATCH 027/227] Revert an invalid attempt to autoequip shields instead of torches --- CHANGELOG.md | 1 - apps/openmw/mwmechanics/actors.cpp | 5 ----- 2 files changed, 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97aa37f23..77b126d85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -217,7 +217,6 @@ Bug #5264: "Damage Fatigue" Magic Effect Can Bring Fatigue below 0 Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted Bug #5278: Console command Show doesn't fall back to global variable after local var not found - Bug #5300: NPCs don't switch from torch to shield when starting combat Bug #5308: World map copying makes save loading much slower Bug #5313: Node properties of identical type are not applied in the correct order Bug #5326: Formatting issues in the settings.cfg diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 726b2a31f..b089d543a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1235,11 +1235,6 @@ namespace MWMechanics if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) inventoryStore.unequipItem(*heldIter, ptr); } - else if (heldIter == inventoryStore.end() || heldIter->getTypeName() == typeid(ESM::Light).name()) - { - // For hostile NPCs, see if they have anything better to equip first - inventoryStore.autoEquip(ptr); - } heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); From 16f6c7b27fb878782b9bc449dce25a396f07a4ac Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Apr 2020 14:14:53 +0300 Subject: [PATCH 028/227] Use the new option to treat TGA files as TGA 1.0 --- components/resource/imagemanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/resource/imagemanager.cpp b/components/resource/imagemanager.cpp index c1d71ee00..41ce999e0 100644 --- a/components/resource/imagemanager.cpp +++ b/components/resource/imagemanager.cpp @@ -47,7 +47,7 @@ namespace Resource ImageManager::ImageManager(const VFS::Manager *vfs) : ResourceManager(vfs) , mWarningImage(createWarningImage()) - , mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba")) + , mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba ignoreTga2Fields")) { } From 63fe02b1ba5a5618afa5ef01b01e87f0c87ae601 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Apr 2020 15:50:50 +0300 Subject: [PATCH 029/227] CollisionSwitch and Switch/LOD node fixes Properly apply transformations to both switch and LOD nodes Allow both NiSwitchNode and NiLODNode to be the root node Properly add CollisionSwitch into the scene graph --- components/nifosg/nifloader.cpp | 38 ++++++++++++++++++++---------- components/sceneutil/serialize.cpp | 1 + 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index a3972b0e2..e49e672d2 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -170,6 +170,17 @@ namespace NifOsg class CollisionSwitch : public osg::MatrixTransform { public: + CollisionSwitch() : osg::MatrixTransform() + { + } + + CollisionSwitch(const CollisionSwitch& copy, const osg::CopyOp& copyop) + : osg::MatrixTransform(copy, copyop) + { + } + + META_Node(NifOsg, CollisionSwitch) + CollisionSwitch(const osg::Matrixf& transformations, bool enabled) : osg::MatrixTransform(transformations) { setEnabled(enabled); @@ -472,17 +483,8 @@ namespace NifOsg osg::ref_ptr node; osg::Object::DataVariance dataVariance = osg::Object::UNSPECIFIED; - // TODO: it is unclear how to handle transformations of LOD nodes and controllers for them. switch (nifNode->recType) { - case Nif::RC_NiLODNode: - { - const Nif::NiLODNode* niLodNode = static_cast(nifNode); - node = handleLodNode(niLodNode); - dataVariance = osg::Object::DYNAMIC; - break; - } - case Nif::RC_NiSwitchNode: case Nif::RC_NiAutoNormalParticles: case Nif::RC_NiRotatingParticles: // Leaf nodes in the NIF hierarchy, so won't be able to dynamically attach children. @@ -663,6 +665,11 @@ namespace NifOsg && !nifNode->controller.empty() && node->getDataVariance() == osg::Object::DYNAMIC) handleNodeControllers(nifNode, static_cast(node.get()), animflags); + // LOD and Switch nodes must be wrapped by a transform (the current node) to support transformations properly + // and we need to attach their children to the osg::LOD/osg::Switch nodes + // but we must return that transform to the caller of handleNode instead of the actual LOD/Switch nodes. + osg::ref_ptr currentNode = node; + if (nifNode->recType == Nif::RC_NiSwitchNode) { const Nif::NiSwitchNode* niSwitchNode = static_cast(nifNode); @@ -673,7 +680,14 @@ namespace NifOsg else if (niSwitchNode->name == Constants::HerbalismLabel && !SceneUtil::hasUserDescription(rootNode, Constants::HerbalismLabel)) rootNode->getOrCreateUserDataContainer()->addDescription(Constants::HerbalismLabel); - node = switchNode; + currentNode = switchNode; + } + else if (nifNode->recType == Nif::RC_NiLODNode) + { + const Nif::NiLODNode* niLodNode = static_cast(nifNode); + osg::ref_ptr lodNode = handleLodNode(niLodNode); + node->addChild(lodNode); + currentNode = lodNode; } const Nif::NiNode *ninode = dynamic_cast(nifNode); @@ -683,14 +697,14 @@ namespace NifOsg for (size_t i = 0; i < effects.length(); ++i) { if (!effects[i].empty()) - handleEffect(effects[i].getPtr(), node, imageManager); + handleEffect(effects[i].getPtr(), currentNode, imageManager); } const Nif::NodeList &children = ninode->children; for(size_t i = 0;i < children.length();++i) { if(!children[i].empty()) - handleNode(children[i].getPtr(), node, imageManager, boundTextures, animflags, skipMeshes, hasMarkers, isAnimated, textKeys, rootNode); + handleNode(children[i].getPtr(), currentNode, imageManager, boundTextures, animflags, skipMeshes, hasMarkers, isAnimated, textKeys, rootNode); } } diff --git a/components/sceneutil/serialize.cpp b/components/sceneutil/serialize.cpp index f84a19876..60f096a72 100644 --- a/components/sceneutil/serialize.cpp +++ b/components/sceneutil/serialize.cpp @@ -131,6 +131,7 @@ void registerSerializers() "NifOsg::StaticBoundingBoxCallback", "NifOsg::GeomMorpherController", "NifOsg::UpdateMorphGeometry", + "NifOsg::CollisionSwitch", "osgMyGUI::Drawable", "osg::DrawCallback", "osgOQ::ClearQueriesCallback", From 957d2a890f769feee80815c2ce0a310033d5645d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Apr 2020 17:14:50 +0300 Subject: [PATCH 030/227] Ignore empty children of osg::LOD and osg::Switch like in OSG --- components/sceneutil/optimizer.cpp | 21 +++++---------------- components/sceneutil/optimizer.hpp | 2 -- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index e8ebed868..487126627 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -735,20 +735,6 @@ bool Optimizer::CombineStaticTransformsVisitor::removeTransforms(osg::Node* node // RemoveEmptyNodes. //////////////////////////////////////////////////////////////////////////// -void Optimizer::RemoveEmptyNodesVisitor::apply(osg::Switch& switchNode) -{ - // We should keep all switch child nodes since they reflect different switch states. - for (unsigned int i=0; i0) @@ -787,8 +773,11 @@ void Optimizer::RemoveEmptyNodesVisitor::removeEmptyNodes() ++pitr) { osg::Group* parent = *pitr; - parent->removeChild(nodeToRemove.get()); - if (parent->getNumChildren()==0 && isOperationPermissibleForObject(parent)) newEmptyGroups.insert(parent); + if (!parent->asSwitch() && !dynamic_cast(parent)) + { + parent->removeChild(nodeToRemove.get()); + if (parent->getNumChildren()==0 && isOperationPermissibleForObject(parent)) newEmptyGroups.insert(parent); + } } } diff --git a/components/sceneutil/optimizer.hpp b/components/sceneutil/optimizer.hpp index 9974e7097..6dd4394d1 100644 --- a/components/sceneutil/optimizer.hpp +++ b/components/sceneutil/optimizer.hpp @@ -321,8 +321,6 @@ class Optimizer BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES) {} virtual void apply(osg::Group& group); - virtual void apply(osg::LOD& lod); - virtual void apply(osg::Switch& switchNode); void removeEmptyNodes(); From 10daadefbea1f62a818c339dc7f3d69a858bc8a6 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 30 Apr 2020 21:57:22 +0200 Subject: [PATCH 031/227] Add missing include --- components/detournavigator/offmeshconnectionsmanager.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/detournavigator/offmeshconnectionsmanager.hpp b/components/detournavigator/offmeshconnectionsmanager.hpp index 30d7976ae..155ce3296 100644 --- a/components/detournavigator/offmeshconnectionsmanager.hpp +++ b/components/detournavigator/offmeshconnectionsmanager.hpp @@ -13,6 +13,7 @@ #include +#include #include #include #include From 87ba0bb0e044e5d8a17eee40d20a7ffb71e53df6 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 1 May 2020 16:22:07 +0300 Subject: [PATCH 032/227] Fix usage of uninitialized weapon type in equipmentChanged() --- apps/openmw/mwrender/npcanimation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index a797a9876..9c27b3bb0 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -1127,7 +1127,7 @@ void NpcAnimation::equipmentChanged() static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game"); if (shieldSheathing) { - int weaptype; + int weaptype = ESM::Weapon::None; MWMechanics::getActiveWeapon(mPtr, &weaptype); showCarriedLeft(updateCarriedLeftVisible(weaptype)); } From 6d8debe009ea0a9cc9ae4253b4a45857eddb4721 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 1 May 2020 17:10:06 +0200 Subject: [PATCH 033/227] Initialize variable without reading itself --- apps/openmw/mwrender/npcanimation.cpp | 14 ++++++++++---- apps/openmw/mwrender/npcanimation.hpp | 3 ++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index a797a9876..629b10ba4 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -266,16 +266,22 @@ void HeadAnimationTime::setBlinkStop(float value) // ---------------------------------------------------- -NpcAnimation::NpcType NpcAnimation::getNpcType() +NpcAnimation::NpcType NpcAnimation::getNpcType() const { const MWWorld::Class &cls = mPtr.getClass(); // Dead vampires should typically stay vampires. if (mNpcType == Type_Vampire && cls.getNpcStats(mPtr).isDead() && !cls.getNpcStats(mPtr).isWerewolf()) return mNpcType; + return getNpcType(mPtr); +} + +NpcAnimation::NpcType NpcAnimation::getNpcType(const MWWorld::Ptr& ptr) +{ + const MWWorld::Class &cls = ptr.getClass(); NpcAnimation::NpcType curType = Type_Normal; - if (cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0) + if (cls.getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0) curType = Type_Vampire; - if (cls.getNpcStats(mPtr).isWerewolf()) + if (cls.getNpcStats(ptr).isWerewolf()) curType = Type_Werewolf; return curType; @@ -326,7 +332,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr par mViewMode(viewMode), mShowWeapons(false), mShowCarriedLeft(true), - mNpcType(getNpcType()), + mNpcType(getNpcType(ptr)), mFirstPersonFieldOfView(firstPersonFieldOfView), mSoundsDisabled(disableSounds), mAccurateAiming(false), diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 9e7969976..e102f5097 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -74,7 +74,7 @@ private: void updateNpcBase(); - NpcType getNpcType(); + NpcType getNpcType() const; PartHolderPtr insertBoundedPart(const std::string &model, const std::string &bonename, const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=nullptr); @@ -94,6 +94,7 @@ private: static bool isFirstPersonPart(const ESM::BodyPart* bodypart); static bool isFemalePart(const ESM::BodyPart* bodypart); + static NpcType getNpcType(const MWWorld::Ptr& ptr); protected: virtual void addControllers(); From b150d681a98344e850324888471df9c9973566fe Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 20 Feb 2020 15:05:50 -0800 Subject: [PATCH 034/227] Update same navmesh tile with limited frequency --- .../detournavigator/navigator.cpp | 38 ++++++++++++ .../detournavigator/asyncnavmeshupdater.cpp | 61 ++++++++++++++++--- .../detournavigator/asyncnavmeshupdater.hpp | 25 +++++++- components/detournavigator/settings.cpp | 1 + components/detournavigator/settings.hpp | 2 + .../reference/modding/settings/navigator.rst | 14 +++++ files/settings-default.cfg | 3 + 7 files changed, 132 insertions(+), 12 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 71b531513..276877508 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -73,6 +73,7 @@ namespace mSettings.mTrianglesPerChunk = 256; mSettings.mMaxPolys = 4096; mSettings.mMaxTilesNumber = 512; + mSettings.mMinUpdateInterval = std::chrono::milliseconds(50); mNavigator.reset(new NavigatorImpl(mSettings)); } }; @@ -766,4 +767,41 @@ namespace Vec3fEq(215, -215, 1.8782813549041748046875) )) << mPath; } + + TEST_F(DetourNavigatorNavigatorTest, update_changed_multiple_times_object_should_delay_navmesh_change) + { + const std::vector shapes(100, btVector3(64, 64, 64)); + + mNavigator->addAgent(mAgentHalfExtents); + + for (std::size_t i = 0; i < shapes.size(); ++i) + { + const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32, i * 32, i * 32)); + mNavigator->addObject(ObjectId(&shapes[i]), shapes[i], transform); + } + mNavigator->update(mPlayerPosition); + mNavigator->wait(); + + const auto start = std::chrono::steady_clock::now(); + for (std::size_t i = 0; i < shapes.size(); ++i) + { + const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 1, i * 32 + 1, i * 32 + 1)); + mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); + } + mNavigator->update(mPlayerPosition); + mNavigator->wait(); + + for (std::size_t i = 0; i < shapes.size(); ++i) + { + const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 2, i * 32 + 2, i * 32 + 2)); + mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); + } + mNavigator->update(mPlayerPosition); + mNavigator->wait(); + + const auto duration = std::chrono::steady_clock::now() - start; + + EXPECT_GT(duration, mSettings.mMinUpdateInterval) + << std::chrono::duration_cast>(duration).count() << " ms"; + } } diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 1c07384b8..0683a43bc 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -89,6 +89,9 @@ namespace DetourNavigator job.mChangeType = changedTile.second; job.mDistanceToPlayer = getManhattanDistance(changedTile.first, playerTile); job.mDistanceToOrigin = getManhattanDistance(changedTile.first, TilePosition {0, 0}); + job.mProcessTime = job.mChangeType == ChangeType::update + ? mLastUpdates[job.mAgentHalfExtents][job.mChangedTile] + mSettings.get().mMinUpdateInterval + : std::chrono::steady_clock::time_point(); mJobs.push(std::move(job)); } @@ -137,6 +140,8 @@ namespace DetourNavigator if (!processed) repost(std::move(*job)); } + else + cleanupLastUpdates(); } catch (const std::exception& e) { @@ -176,6 +181,7 @@ namespace DetourNavigator const auto locked = navMeshCacheItem->lockConst(); Log(Debug::Debug) << std::fixed << std::setprecision(2) << "Cache updated for agent=(" << job.mAgentHalfExtents << ")" << + " tile=" << job.mChangedTile << " status=" << status << " generation=" << locked->getGeneration() << " revision=" << locked->getNavMeshRevision() << @@ -195,12 +201,15 @@ namespace DetourNavigator while (true) { - const auto hasJob = [&] { return !mJobs.empty() || !threadQueue.mJobs.empty(); }; + const auto hasJob = [&] { + return (!mJobs.empty() && mJobs.top().mProcessTime <= std::chrono::steady_clock::now()) + || !threadQueue.mJobs.empty(); + }; if (!mHasJob.wait_for(lock, std::chrono::milliseconds(10), hasJob)) { mFirstStart.lock()->reset(); - if (getTotalThreadJobsUnsafe() == 0) + if (mJobs.empty() && getTotalThreadJobsUnsafe() == 0) mDone.notify_all(); return boost::none; } @@ -209,29 +218,40 @@ namespace DetourNavigator << threadQueue.mJobs.size() << " thread jobs by thread=" << std::this_thread::get_id(); auto job = threadQueue.mJobs.empty() - ? getJob(mJobs, mPushed) - : getJob(threadQueue.mJobs, threadQueue.mPushed); + ? getJob(mJobs, mPushed, true) + : getJob(threadQueue.mJobs, threadQueue.mPushed, false); - const auto owner = lockTile(job.mAgentHalfExtents, job.mChangedTile); + if (!job) + continue; + + const auto owner = lockTile(job->mAgentHalfExtents, job->mChangedTile); if (owner == threadId) return job; - postThreadJob(std::move(job), mThreadsQueues[owner]); + postThreadJob(std::move(*job), mThreadsQueues[owner]); } } - AsyncNavMeshUpdater::Job AsyncNavMeshUpdater::getJob(Jobs& jobs, Pushed& pushed) + boost::optional AsyncNavMeshUpdater::getJob(Jobs& jobs, Pushed& pushed, bool changeLastUpdate) { - auto job = jobs.top(); + const auto now = std::chrono::steady_clock::now(); + + if (jobs.top().mProcessTime > now) + return {}; + + Job job = std::move(jobs.top()); jobs.pop(); + if (changeLastUpdate && job.mChangeType == ChangeType::update) + mLastUpdates[job.mAgentHalfExtents][job.mChangedTile] = now; + const auto it = pushed.find(job.mAgentHalfExtents); it->second.erase(job.mChangedTile); if (it->second.empty()) pushed.erase(it); - return job; + return {std::move(job)}; } void AsyncNavMeshUpdater::writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const @@ -344,4 +364,27 @@ namespace DetourNavigator return std::accumulate(mThreadsQueues.begin(), mThreadsQueues.end(), std::size_t(0), [] (auto r, const auto& v) { return r + v.second.mJobs.size(); }); } + + void AsyncNavMeshUpdater::cleanupLastUpdates() + { + const auto now = std::chrono::steady_clock::now(); + + const std::lock_guard lock(mMutex); + + for (auto agent = mLastUpdates.begin(); agent != mLastUpdates.end();) + { + for (auto tile = agent->second.begin(); tile != agent->second.end();) + { + if (now - tile->second > mSettings.get().mMinUpdateInterval) + tile = agent->second.erase(tile); + else + ++tile; + } + + if (agent->second.empty()) + agent = mLastUpdates.erase(agent); + else + ++agent; + } + } } diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 6a3799969..4debcd6cd 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -32,6 +32,21 @@ namespace DetourNavigator update = 3, }; + inline std::ostream& operator <<(std::ostream& stream, ChangeType value) + { + switch (value) { + case ChangeType::remove: + return stream << "ChangeType::remove"; + case ChangeType::mixed: + return stream << "ChangeType::mixed"; + case ChangeType::add: + return stream << "ChangeType::add"; + case ChangeType::update: + return stream << "ChangeType::update"; + } + return stream << "ChangeType::" << static_cast(value); + } + class AsyncNavMeshUpdater { public: @@ -56,10 +71,11 @@ namespace DetourNavigator ChangeType mChangeType; int mDistanceToPlayer; int mDistanceToOrigin; + std::chrono::steady_clock::time_point mProcessTime; - std::tuple getPriority() const + std::tuple getPriority() const { - return std::make_tuple(mTryNumber, mChangeType, mDistanceToPlayer, mDistanceToOrigin); + return std::make_tuple(mProcessTime, mTryNumber, mChangeType, mDistanceToPlayer, mDistanceToOrigin); } friend inline bool operator <(const Job& lhs, const Job& rhs) @@ -93,6 +109,7 @@ namespace DetourNavigator Misc::ScopeGuarded> mFirstStart; NavMeshTilesCache mNavMeshTilesCache; Misc::ScopeGuarded>> mProcessingTiles; + std::map> mLastUpdates; std::map mThreadsQueues; std::vector mThreads; @@ -102,7 +119,7 @@ namespace DetourNavigator boost::optional getNextJob(); - static Job getJob(Jobs& jobs, Pushed& pushed); + boost::optional getJob(Jobs& jobs, Pushed& pushed, bool changeLastUpdate); void postThreadJob(Job&& job, Queue& queue); @@ -117,6 +134,8 @@ namespace DetourNavigator void unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile); inline std::size_t getTotalThreadJobsUnsafe() const; + + void cleanupLastUpdates(); }; } diff --git a/components/detournavigator/settings.cpp b/components/detournavigator/settings.cpp index 735194dba..49aec41ff 100644 --- a/components/detournavigator/settings.cpp +++ b/components/detournavigator/settings.cpp @@ -40,6 +40,7 @@ namespace DetourNavigator navigatorSettings.mNavMeshPathPrefix = ::Settings::Manager::getString("nav mesh path prefix", "Navigator"); navigatorSettings.mEnableRecastMeshFileNameRevision = ::Settings::Manager::getBool("enable recast mesh file name revision", "Navigator"); navigatorSettings.mEnableNavMeshFileNameRevision = ::Settings::Manager::getBool("enable nav mesh file name revision", "Navigator"); + navigatorSettings.mMinUpdateInterval = std::chrono::milliseconds(::Settings::Manager::getInt("min update interval ms", "Navigator")); return navigatorSettings; } diff --git a/components/detournavigator/settings.hpp b/components/detournavigator/settings.hpp index dc0e5dc5a..939d825a5 100644 --- a/components/detournavigator/settings.hpp +++ b/components/detournavigator/settings.hpp @@ -4,6 +4,7 @@ #include #include +#include namespace DetourNavigator { @@ -38,6 +39,7 @@ namespace DetourNavigator std::size_t mTrianglesPerChunk = 0; std::string mRecastMeshPathPrefix; std::string mNavMeshPathPrefix; + std::chrono::milliseconds mMinUpdateInterval; }; boost::optional makeSettingsFromSettingsManager(); diff --git a/docs/source/reference/modding/settings/navigator.rst b/docs/source/reference/modding/settings/navigator.rst index c7817b6e8..af40ac750 100644 --- a/docs/source/reference/modding/settings/navigator.rst +++ b/docs/source/reference/modding/settings/navigator.rst @@ -74,6 +74,20 @@ Game will not eat all memory at once. Memory will be consumed in approximately linear dependency from number of nav mesh updates. But only for new locations or already dropped from cache. +min update interval ms +---------------- + +:Type: integer +:Range: >= 0 +:Default: 250 + +Minimum time duration required to pass before next navmesh update for the same tile in milliseconds. +Only tiles affected where objects are transformed. +Next update for tile with added or removed object will not be delayed. +Visible ingame effect is navmesh update around opening or closing door. +Primary usage is for rotating signs like in Seyda Neen at Arrille's Tradehouse entrance. +Decreasing this value may increase CPU usage by background threads. + Developer's settings ******************** diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 6703e7732..5b587776c 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -776,6 +776,9 @@ enable recast mesh render = false # Max number of navmesh tiles (value >= 0) max tiles number = 512 +# Min time duration for the same tile update in milliseconds (value >= 0) +min update interval ms = 250 + [Shadows] # Enable or disable shadows. Bear in mind that this will force OpenMW to use shaders as if "[Shaders]/force shaders" was set to true. From edf002aa97854b6f909829d2be986dec638a506a Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 1 May 2020 19:11:04 +0200 Subject: [PATCH 035/227] Rename argument shaderTemplate to templateName --- components/shader/shadermanager.cpp | 50 ++++++++++++++--------------- components/shader/shadermanager.hpp | 2 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index c2126275f..10f2de819 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -58,7 +58,7 @@ namespace Shader return true; } - bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& shaderTemplate) + bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& templateName) { Misc::StringUtils::replaceAll(source, "\r\n", "\n"); @@ -70,13 +70,13 @@ namespace Shader size_t start = source.find('"', foundPos); if (start == std::string::npos || start == source.size()-1) { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Invalid #include"; + Log(Debug::Error) << "Shader " << templateName << " error: Invalid #include"; return false; } size_t end = source.find('"', start+1); if (end == std::string::npos) { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Invalid #include"; + Log(Debug::Error) << "Shader " << templateName << " error: Invalid #include"; return false; } std::string includeFilename = source.substr(start+1, end-(start+1)); @@ -85,7 +85,7 @@ namespace Shader includeFstream.open(includePath); if (includeFstream.fail()) { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Failed to open include " << includePath.string(); + Log(Debug::Error) << "Shader " << templateName << " error: Failed to open include " << includePath.string(); return false; } @@ -120,14 +120,14 @@ namespace Shader if (includedFiles.insert(includePath).second == false) { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Detected cyclic #includes"; + Log(Debug::Error) << "Shader " << templateName << " error: Detected cyclic #includes"; return false; } } return true; } - bool parseFors(std::string& source, const std::string& shaderTemplate) + bool parseFors(std::string& source, const std::string& templateName) { const char escapeCharacter = '$'; size_t foundPos = 0; @@ -136,13 +136,13 @@ namespace Shader size_t endPos = source.find_first_of(" \n\r()[].;,", foundPos); if (endPos == std::string::npos) { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unexpected EOF"; + Log(Debug::Error) << "Shader " << templateName << " error: Unexpected EOF"; return false; } std::string command = source.substr(foundPos + 1, endPos - (foundPos + 1)); if (command != "foreach") { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unknown shader directive: $" << command; + Log(Debug::Error) << "Shader " << templateName << " error: Unknown shader directive: $" << command; return false; } @@ -150,7 +150,7 @@ namespace Shader size_t iterNameEnd = source.find_first_of(" \n\r()[].;,", iterNameStart); if (iterNameEnd == std::string::npos) { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unexpected EOF"; + Log(Debug::Error) << "Shader " << templateName << " error: Unexpected EOF"; return false; } std::string iteratorName = "$" + source.substr(iterNameStart, iterNameEnd - iterNameStart); @@ -159,7 +159,7 @@ namespace Shader size_t listEnd = source.find_first_of("\n\r", listStart); if (listEnd == std::string::npos) { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unexpected EOF"; + Log(Debug::Error) << "Shader " << templateName << " error: Unexpected EOF"; return false; } std::string list = source.substr(listStart, listEnd - listStart); @@ -171,7 +171,7 @@ namespace Shader size_t contentEnd = source.find("$endforeach", contentStart); if (contentEnd == std::string::npos) { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unexpected EOF"; + Log(Debug::Error) << "Shader " << templateName << " error: Unexpected EOF"; return false; } std::string content = source.substr(contentStart, contentEnd - contentStart); @@ -211,7 +211,7 @@ namespace Shader } bool parseDefines(std::string& source, const ShaderManager::DefineMap& defines, - const ShaderManager::DefineMap& globalDefines, const std::string& shaderTemplate) + const ShaderManager::DefineMap& globalDefines, const std::string& templateName) { const char escapeCharacter = '@'; size_t foundPos = 0; @@ -221,7 +221,7 @@ namespace Shader size_t endPos = source.find_first_of(" \n\r()[].;,", foundPos); if (endPos == std::string::npos) { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unexpected EOF"; + Log(Debug::Error) << "Shader " << templateName << " error: Unexpected EOF"; return false; } std::string define = source.substr(foundPos+1, endPos - (foundPos+1)); @@ -234,7 +234,7 @@ namespace Shader size_t iterNameEnd = source.find_first_of(" \n\r()[].;,", iterNameStart); if (iterNameEnd == std::string::npos) { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unexpected EOF"; + Log(Debug::Error) << "Shader " << templateName << " error: Unexpected EOF"; return false; } forIterators.push_back(source.substr(iterNameStart, iterNameEnd - iterNameStart)); @@ -244,7 +244,7 @@ namespace Shader source.replace(foundPos, 1, "$"); if (forIterators.empty()) { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: endforeach without foreach"; + Log(Debug::Error) << "Shader " << templateName << " error: endforeach without foreach"; return false; } else @@ -264,22 +264,22 @@ namespace Shader } else { - Log(Debug::Error) << "Shader " << shaderTemplate << " error: Undefined " << define; + Log(Debug::Error) << "Shader " << templateName << " error: Undefined " << define; return false; } } return true; } - osg::ref_ptr ShaderManager::getShader(const std::string &shaderTemplate, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType) + osg::ref_ptr ShaderManager::getShader(const std::string &templateName, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType) { OpenThreads::ScopedLock lock(mMutex); // read the template if we haven't already - TemplateMap::iterator templateIt = mShaderTemplates.find(shaderTemplate); + TemplateMap::iterator templateIt = mShaderTemplates.find(templateName); if (templateIt == mShaderTemplates.end()) { - boost::filesystem::path p = (boost::filesystem::path(mPath) / shaderTemplate); + boost::filesystem::path p = (boost::filesystem::path(mPath) / templateName); boost::filesystem::ifstream stream; stream.open(p); if (stream.fail()) @@ -293,20 +293,20 @@ namespace Shader // parse includes std::string source = buffer.str(); if (!addLineDirectivesAfterConditionalBlocks(source) - || !parseIncludes(boost::filesystem::path(mPath), source, shaderTemplate)) + || !parseIncludes(boost::filesystem::path(mPath), source, templateName)) return nullptr; - templateIt = mShaderTemplates.insert(std::make_pair(shaderTemplate, source)).first; + templateIt = mShaderTemplates.insert(std::make_pair(templateName, source)).first; } - ShaderMap::iterator shaderIt = mShaders.find(std::make_pair(shaderTemplate, defines)); + ShaderMap::iterator shaderIt = mShaders.find(std::make_pair(templateName, defines)); if (shaderIt == mShaders.end()) { std::string shaderSource = templateIt->second; - if (!parseDefines(shaderSource, defines, mGlobalDefines, shaderTemplate) || !parseFors(shaderSource, shaderTemplate)) + if (!parseDefines(shaderSource, defines, mGlobalDefines, templateName) || !parseFors(shaderSource, templateName)) { // Add to the cache anyway to avoid logging the same error over and over. - mShaders.insert(std::make_pair(std::make_pair(shaderTemplate, defines), nullptr)); + mShaders.insert(std::make_pair(std::make_pair(templateName, defines), nullptr)); return nullptr; } @@ -316,7 +316,7 @@ namespace Shader static unsigned int counter = 0; shader->setName(std::to_string(counter++)); - shaderIt = mShaders.insert(std::make_pair(std::make_pair(shaderTemplate, defines), shader)).first; + shaderIt = mShaders.insert(std::make_pair(std::make_pair(templateName, defines), shader)).first; } return shaderIt->second; } diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index 05775edb6..dbe989476 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -30,7 +30,7 @@ namespace Shader /// @param shaderType The type of shader (usually vertex or fragment shader). /// @note May return nullptr on failure. /// @note Thread safe. - osg::ref_ptr getShader(const std::string& shaderTemplate, const DefineMap& defines, osg::Shader::Type shaderType); + osg::ref_ptr getShader(const std::string& templateName, const DefineMap& defines, osg::Shader::Type shaderType); osg::ref_ptr getProgram(osg::ref_ptr vertexShader, osg::ref_ptr fragmentShader); From ca649003ed74c81100ac8b41aed8dad9f19094cb Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 1 May 2020 23:01:47 +0200 Subject: [PATCH 036/227] Use googletest 1.10.0 To get support for INSTANTIATE_TEST_SUITE_P macro --- CI/build_googletest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/build_googletest.sh b/CI/build_googletest.sh index ee89ebda6..0ffda7f9b 100755 --- a/CI/build_googletest.sh +++ b/CI/build_googletest.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -git clone -b release-1.8.1 https://github.com/google/googletest.git +git clone -b release-1.10.0 https://github.com/google/googletest.git cd googletest mkdir build cd build From 1f3dfaedcc12c524c1e5d4a59517a0170f8d3592 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 1 May 2020 23:03:13 +0200 Subject: [PATCH 037/227] Add tests for ShaderManager --- apps/openmw_test_suite/CMakeLists.txt | 4 + .../openmw_test_suite/shader/parsedefines.cpp | 191 ++++++++++++++ apps/openmw_test_suite/shader/parsefors.cpp | 94 +++++++ .../shader/shadermanager.cpp | 240 ++++++++++++++++++ components/shader/shadermanager.hpp | 4 + 5 files changed, 533 insertions(+) create mode 100644 apps/openmw_test_suite/shader/parsedefines.cpp create mode 100644 apps/openmw_test_suite/shader/parsefors.cpp create mode 100644 apps/openmw_test_suite/shader/shadermanager.cpp diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index e216ec759..cd2d2e80a 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -27,6 +27,10 @@ if (GTEST_FOUND AND GMOCK_FOUND) detournavigator/tilecachedrecastmeshmanager.cpp settings/parser.cpp + + shader/parsedefines.cpp + shader/parsefors.cpp + shader/shadermanager.cpp ) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) diff --git a/apps/openmw_test_suite/shader/parsedefines.cpp b/apps/openmw_test_suite/shader/parsedefines.cpp new file mode 100644 index 000000000..65b4380a7 --- /dev/null +++ b/apps/openmw_test_suite/shader/parsedefines.cpp @@ -0,0 +1,191 @@ +#include + +#include +#include + +namespace +{ + using namespace testing; + using namespace Shader; + + using DefineMap = ShaderManager::DefineMap; + + struct ShaderParseDefinesTest : Test + { + std::string mSource; + const std::string mName = "shader"; + DefineMap mDefines; + DefineMap mGlobalDefines; + }; + + TEST_F(ShaderParseDefinesTest, empty_should_succeed) + { + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, ""); + } + + TEST_F(ShaderParseDefinesTest, should_fail_for_absent_define) + { + mSource = "@foo\n"; + ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "@foo\n"); + } + + TEST_F(ShaderParseDefinesTest, should_replace_by_existing_define) + { + mDefines["foo"] = "42"; + mSource = "@foo\n"; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "42\n"); + } + + TEST_F(ShaderParseDefinesTest, should_replace_by_existing_global_define) + { + mGlobalDefines["foo"] = "42"; + mSource = "@foo\n"; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "42\n"); + } + + TEST_F(ShaderParseDefinesTest, should_prefer_define_over_global_define) + { + mDefines["foo"] = "13"; + mGlobalDefines["foo"] = "42"; + mSource = "@foo\n"; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "13\n"); + } + + namespace SupportedTerminals + { + struct ShaderParseDefinesTest : ::ShaderParseDefinesTest, WithParamInterface {}; + + TEST_P(ShaderParseDefinesTest, support_defines_terminated_by) + { + mDefines["foo"] = "13"; + mSource = "@foo" + std::string(1, GetParam()); + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "13" + std::string(1, GetParam())); + } + + INSTANTIATE_TEST_SUITE_P( + SupportedTerminals, + ShaderParseDefinesTest, + Values(' ', '\n', '\r', '(', ')', '[', ']', '.', ';', ',') + ); + } + + TEST_F(ShaderParseDefinesTest, should_not_support_define_ending_with_source) + { + mDefines["foo"] = "42"; + mSource = "@foo"; + ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "@foo"); + } + + TEST_F(ShaderParseDefinesTest, should_replace_all_matched_values) + { + mDefines["foo"] = "42"; + mSource = "@foo @foo "; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "42 42 "); + } + + TEST_F(ShaderParseDefinesTest, should_support_define_with_empty_name) + { + mDefines[""] = "42"; + mSource = "@ "; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "42 "); + } + + TEST_F(ShaderParseDefinesTest, should_replace_all_found_defines) + { + mDefines["foo"] = "42"; + mDefines["bar"] = "13"; + mDefines["baz"] = "55"; + mSource = "@foo @bar "; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "42 13 "); + } + + TEST_F(ShaderParseDefinesTest, should_fail_on_foreach_without_endforeach) + { + mSource = "@foreach "; + ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "$foreach "); + } + + TEST_F(ShaderParseDefinesTest, should_fail_on_endforeach_without_foreach) + { + mSource = "@endforeach "; + ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "$endforeach "); + } + + TEST_F(ShaderParseDefinesTest, should_replace_at_sign_by_dollar_for_foreach_endforeach) + { + mSource = "@foreach @endforeach "; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "$foreach $endforeach "); + } + + TEST_F(ShaderParseDefinesTest, should_succeed_on_unmatched_nested_foreach) + { + mSource = "@foreach @foreach @endforeach "; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "$foreach $foreach $endforeach "); + } + + TEST_F(ShaderParseDefinesTest, should_fail_on_unmatched_nested_endforeach) + { + mSource = "@foreach @endforeach @endforeach "; + ASSERT_FALSE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "$foreach $endforeach $endforeach "); + } + + TEST_F(ShaderParseDefinesTest, should_support_nested_foreach) + { + mSource = "@foreach @foreach @endforeach @endforeach "; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "$foreach $foreach $endforeach $endforeach "); + } + + TEST_F(ShaderParseDefinesTest, should_support_foreach_variable) + { + mSource = "@foreach foo @foo @endforeach "; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "$foreach foo $foo $endforeach "); + } + + TEST_F(ShaderParseDefinesTest, should_not_replace_foreach_variable_by_define) + { + mDefines["foo"] = "42"; + mSource = "@foreach foo @foo @endforeach "; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "$foreach foo $foo $endforeach "); + } + + TEST_F(ShaderParseDefinesTest, should_support_nested_foreach_with_variable) + { + mSource = "@foreach foo @foo @foreach bar @bar @endforeach @endforeach "; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "$foreach foo $foo $foreach bar $bar $endforeach $endforeach "); + } + + TEST_F(ShaderParseDefinesTest, should_not_support_single_line_comments_for_defines) + { + mDefines["foo"] = "42"; + mSource = "@foo // @foo\n"; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "42 // 42\n"); + } + + TEST_F(ShaderParseDefinesTest, should_not_support_multiline_comments_for_defines) + { + mDefines["foo"] = "42"; + mSource = "/* @foo */ @foo "; + ASSERT_TRUE(parseDefines(mSource, mDefines, mGlobalDefines, mName)); + EXPECT_EQ(mSource, "/* 42 */ 42 "); + } +} diff --git a/apps/openmw_test_suite/shader/parsefors.cpp b/apps/openmw_test_suite/shader/parsefors.cpp new file mode 100644 index 000000000..330feb172 --- /dev/null +++ b/apps/openmw_test_suite/shader/parsefors.cpp @@ -0,0 +1,94 @@ +#include + +#include +#include + +namespace +{ + using namespace testing; + using namespace Shader; + + using DefineMap = ShaderManager::DefineMap; + + struct ShaderParseForsTest : Test + { + std::string mSource; + const std::string mName = "shader"; + }; + + TEST_F(ShaderParseForsTest, empty_should_succeed) + { + ASSERT_TRUE(parseFors(mSource, mName)); + EXPECT_EQ(mSource, ""); + } + + TEST_F(ShaderParseForsTest, should_fail_for_single_escape_symbol) + { + mSource = "$"; + ASSERT_FALSE(parseFors(mSource, mName)); + EXPECT_EQ(mSource, "$"); + } + + TEST_F(ShaderParseForsTest, should_fail_on_first_found_escaped_not_foreach) + { + mSource = "$foo "; + ASSERT_FALSE(parseFors(mSource, mName)); + EXPECT_EQ(mSource, "$foo "); + } + + TEST_F(ShaderParseForsTest, should_fail_on_absent_foreach_variable) + { + mSource = "$foreach "; + ASSERT_FALSE(parseFors(mSource, mName)); + EXPECT_EQ(mSource, "$foreach "); + } + + TEST_F(ShaderParseForsTest, should_fail_on_unmatched_after_variable) + { + mSource = "$foreach foo "; + ASSERT_FALSE(parseFors(mSource, mName)); + EXPECT_EQ(mSource, "$foreach foo "); + } + + TEST_F(ShaderParseForsTest, should_fail_on_absent_newline_after_foreach_list) + { + mSource = "$foreach foo 1,2,3 "; + ASSERT_FALSE(parseFors(mSource, mName)); + EXPECT_EQ(mSource, "$foreach foo 1,2,3 "); + } + + TEST_F(ShaderParseForsTest, should_fail_on_absent_endforeach_after_newline) + { + mSource = "$foreach foo 1,2,3\n"; + ASSERT_FALSE(parseFors(mSource, mName)); + EXPECT_EQ(mSource, "$foreach foo 1,2,3\n"); + } + + TEST_F(ShaderParseForsTest, should_replace_complete_foreach_by_line_number) + { + mSource = "$foreach foo 1,2,3\n$endforeach"; + ASSERT_TRUE(parseFors(mSource, mName)); + EXPECT_EQ(mSource, "\n#line 3"); + } + + TEST_F(ShaderParseForsTest, should_replace_loop_variable) + { + mSource = "$foreach foo 1,2,3\n$foo\n$endforeach"; + ASSERT_TRUE(parseFors(mSource, mName)); + EXPECT_EQ(mSource, "1\n2\n3\n\n#line 4"); + } + + TEST_F(ShaderParseForsTest, should_count_line_number_from_existing) + { + mSource = "$foreach foo 1,2,3\n#line 10\n$foo\n$endforeach"; + ASSERT_TRUE(parseFors(mSource, mName)); + EXPECT_EQ(mSource, "#line 10\n1\n#line 10\n2\n#line 10\n3\n\n#line 12"); + } + + TEST_F(ShaderParseForsTest, should_not_support_nested_loops) + { + mSource = "$foreach foo 1,2\n$foo\n$foreach bar 1,2\n$bar\n$endforeach\n$endforeach"; + ASSERT_FALSE(parseFors(mSource, mName)); + EXPECT_EQ(mSource, "1\n1\n2\n$foreach bar 1,2\n1\n\n#line 6\n2\n2\n$foreach bar 1,2\n2\n\n#line 6\n\n#line 7"); + } +} diff --git a/apps/openmw_test_suite/shader/shadermanager.cpp b/apps/openmw_test_suite/shader/shadermanager.cpp new file mode 100644 index 000000000..e823d5fe2 --- /dev/null +++ b/apps/openmw_test_suite/shader/shadermanager.cpp @@ -0,0 +1,240 @@ +#include + +#include + +#include + +namespace +{ + using namespace testing; + using namespace Shader; + + struct ShaderManagerTest : Test + { + ShaderManager mManager; + ShaderManager::DefineMap mDefines; + + ShaderManagerTest() + { + mManager.setShaderPath("."); + } + + template + void withShaderFile(const std::string& content, F&& f) + { + withShaderFile("", content, std::forward(f)); + } + + template + void withShaderFile(const std::string& suffix, const std::string& content, F&& f) + { + const auto path = UnitTest::GetInstance()->current_test_info()->name() + suffix + ".glsl"; + + { + boost::filesystem::ofstream stream; + stream.open(path); + stream << content; + stream.close(); + } + + f(path); + } + }; + + TEST_F(ShaderManagerTest, get_shader_with_empty_content_should_succeed) + { + const std::string content; + + withShaderFile(content, [this] (const std::string& templateName) { + EXPECT_TRUE(mManager.getShader(templateName, {}, osg::Shader::VERTEX)); + }); + } + + TEST_F(ShaderManagerTest, get_shader_should_not_change_source_without_template_parameters) + { + const std::string content = + "#version 120\n" + "void main() {}\n"; + + withShaderFile(content, [&] (const std::string& templateName) { + const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX); + ASSERT_TRUE(shader); + EXPECT_EQ(shader->getShaderSource(), content); + }); + } + + TEST_F(ShaderManagerTest, get_shader_should_replace_includes_with_content) + { + const std::string content0 = + "void foo() {}\n"; + + withShaderFile("_0", content0, [&] (const std::string& templateName0) { + const std::string content1 = + "#include \"" + templateName0 + "\"\n" + "void bar() { foo() }\n"; + + withShaderFile("_1", content1, [&] (const std::string& templateName1) { + const std::string content2 = + "#version 120\n" + "#include \"" + templateName1 + "\"\n" + "void main() { bar() }\n"; + + withShaderFile(content2, [&] (const std::string& templateName2) { + const auto shader = mManager.getShader(templateName2, mDefines, osg::Shader::VERTEX); + ASSERT_TRUE(shader); + const std::string expected = + "#version 120\n" + "#line 0 1\n" + "#line 0 2\n" + "void foo() {}\n" + "\n" + "#line 0 0\n" + "\n" + "void bar() { foo() }\n" + "\n" + "#line 2 0\n" + "\n" + "void main() { bar() }\n"; + EXPECT_EQ(shader->getShaderSource(), expected); + }); + }); + }); + } + + TEST_F(ShaderManagerTest, get_shader_should_replace_defines) + { + const std::string content = + "#version 120\n" + "#define FLAG @flag\n" + "void main() {}\n" + ; + + withShaderFile(content, [&] (const std::string& templateName) { + mDefines["flag"] = "1"; + const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX); + ASSERT_TRUE(shader); + const std::string expected = + "#version 120\n" + "#define FLAG 1\n" + "void main() {}\n"; + EXPECT_EQ(shader->getShaderSource(), expected); + }); + } + + TEST_F(ShaderManagerTest, get_shader_should_expand_loop) + { + const std::string content = + "#version 120\n" + "@foreach index @list\n" + " varying vec4 foo@index;\n" + "@endforeach\n" + "void main() {}\n" + ; + + withShaderFile(content, [&] (const std::string& templateName) { + mDefines["list"] = "1,2,3"; + const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX); + ASSERT_TRUE(shader); + const std::string expected = + "#version 120\n" + " varying vec4 foo1;\n" + " varying vec4 foo2;\n" + " varying vec4 foo3;\n" + "\n" + "#line 5\n" + "void main() {}\n"; + EXPECT_EQ(shader->getShaderSource(), expected); + }); + } + + TEST_F(ShaderManagerTest, get_shader_should_replace_loops_with_conditions) + { + const std::string content = + "#version 120\n" + "@foreach index @list\n" + " varying vec4 foo@index;\n" + "@endforeach\n" + "void main()\n" + "{\n" + "#ifdef BAR\n" + "@foreach index @list\n" + " foo@index = vec4(1.0);\n" + "@endforeach\n" + "#elif BAZ\n" + "@foreach index @list\n" + " foo@index = vec4(2.0);\n" + "@endforeach\n" + "#else\n" + "@foreach index @list\n" + " foo@index = vec4(3.0);\n" + "@endforeach\n" + "#endif\n" + "}\n" + ; + + withShaderFile(content, [&] (const std::string& templateName) { + mDefines["list"] = "1,2,3"; + const auto shader = mManager.getShader(templateName, mDefines, osg::Shader::VERTEX); + ASSERT_TRUE(shader); + const std::string expected = + "#version 120\n" + " varying vec4 foo1;\n" + " varying vec4 foo2;\n" + " varying vec4 foo3;\n" + "\n" + "#line 5\n" + "void main()\n" + "{\n" + "#ifdef BAR\n" + " foo1 = vec4(1.0);\n" + " foo2 = vec4(1.0);\n" + " foo3 = vec4(1.0);\n" + "\n" + "#line 11\n" + "#elif BAZ\n" + "#line 12\n" + " foo1 = vec4(2.0);\n" + " foo2 = vec4(2.0);\n" + " foo3 = vec4(2.0);\n" + "\n" + "#line 15\n" + "#else\n" + "#line 16\n" + " foo1 = vec4(3.0);\n" + " foo2 = vec4(3.0);\n" + " foo3 = vec4(3.0);\n" + "\n" + "#line 19\n" + "#endif\n" + "#line 20\n" + "}\n"; + EXPECT_EQ(shader->getShaderSource(), expected); + }); + } + + TEST_F(ShaderManagerTest, get_shader_should_fail_on_absent_template_parameters_in_single_line_comments) + { + const std::string content = + "#version 120\n" + "// #define FLAG @flag\n" + "void main() {}\n" + ; + + withShaderFile(content, [&] (const std::string& templateName) { + EXPECT_FALSE(mManager.getShader(templateName, mDefines, osg::Shader::VERTEX)); + }); + } + + TEST_F(ShaderManagerTest, get_shader_should_fail_on_absent_template_parameter_in_multi_line_comments) + { + const std::string content = + "#version 120\n" + "/* #define FLAG @flag */\n" + "void main() {}\n" + ; + + withShaderFile(content, [&] (const std::string& templateName) { + EXPECT_FALSE(mManager.getShader(templateName, mDefines, osg::Shader::VERTEX)); + }); + } +} diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index dbe989476..c602ac62b 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -63,6 +63,10 @@ namespace Shader OpenThreads::Mutex mMutex; }; + bool parseFors(std::string& source, const std::string& templateName); + + bool parseDefines(std::string& source, const ShaderManager::DefineMap& defines, + const ShaderManager::DefineMap& globalDefines, const std::string& templateName); } #endif From f9881b699c4f7b9026d83cd93c4214443df2d79a Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 5 May 2020 19:18:23 +0200 Subject: [PATCH 038/227] remove redundant templating --- apps/openmw/mwscript/transformationextensions.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 5ad51f887..d28d9c373 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -396,7 +396,6 @@ namespace MWScript } }; - template class OpPlaceItemCell : public Interpreter::Opcode0 { public: @@ -450,7 +449,6 @@ namespace MWScript } }; - template class OpPlaceItem : public Interpreter::Opcode0 { public: @@ -750,8 +748,8 @@ namespace MWScript interpreter.installSegment5(Compiler::Transformation::opcodePositionExplicit,new OpPosition); interpreter.installSegment5(Compiler::Transformation::opcodePositionCell,new OpPositionCell); interpreter.installSegment5(Compiler::Transformation::opcodePositionCellExplicit,new OpPositionCell); - interpreter.installSegment5(Compiler::Transformation::opcodePlaceItemCell,new OpPlaceItemCell); - interpreter.installSegment5(Compiler::Transformation::opcodePlaceItem,new OpPlaceItem); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceItemCell,new OpPlaceItemCell); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceItem,new OpPlaceItem); interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtPc,new OpPlaceAt); interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMe,new OpPlaceAt); interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMeExplicit,new OpPlaceAt); From 19f12cb3fe7a9163d708212bb642efcf21aaf403 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 5 May 2020 19:37:15 +0200 Subject: [PATCH 039/227] remove magic numbers and casts --- apps/openmw/mwscript/aiextensions.cpp | 80 +++++++++++++-------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 66ce65aa9..79639197d 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -233,24 +233,23 @@ namespace MWScript template class OpGetAiSetting : public Interpreter::Opcode0 { - int mIndex; + MWMechanics::CreatureStats::AiSetting mIndex; public: - OpGetAiSetting(int index) : mIndex(index) {} + OpGetAiSetting(MWMechanics::CreatureStats::AiSetting index) : mIndex(index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - runtime.push(ptr.getClass().getCreatureStats (ptr).getAiSetting ( - (MWMechanics::CreatureStats::AiSetting)mIndex).getModified()); + runtime.push(ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getModified()); } }; template class OpModAiSetting : public Interpreter::Opcode0 { - int mIndex; + MWMechanics::CreatureStats::AiSetting mIndex; public: - OpModAiSetting(int index) : mIndex(index) {} + OpModAiSetting(MWMechanics::CreatureStats::AiSetting index) : mIndex(index) {} virtual void execute (Interpreter::Runtime& runtime) { @@ -258,19 +257,16 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWMechanics::CreatureStats::AiSetting setting - = MWMechanics::CreatureStats::AiSetting(mIndex); - - ptr.getClass().getCreatureStats (ptr).setAiSetting (setting, - ptr.getClass().getCreatureStats (ptr).getAiSetting (setting).getBase() + value); + ptr.getClass().getCreatureStats (ptr).setAiSetting (mIndex, + ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getBase() + value); } }; template class OpSetAiSetting : public Interpreter::Opcode0 { - int mIndex; + MWMechanics::CreatureStats::AiSetting mIndex; public: - OpSetAiSetting(int index) : mIndex(index) {} + OpSetAiSetting(MWMechanics::CreatureStats::AiSetting index) : mIndex(index) {} virtual void execute (Interpreter::Runtime& runtime) { @@ -278,11 +274,9 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWMechanics::CreatureStats::AiSetting setting = (MWMechanics::CreatureStats::AiSetting)mIndex; - - MWMechanics::Stat stat = ptr.getClass().getCreatureStats(ptr).getAiSetting(setting); + MWMechanics::Stat stat = ptr.getClass().getCreatureStats(ptr).getAiSetting(mIndex); stat.setModified(value, 0); - ptr.getClass().getCreatureStats(ptr).setAiSetting(setting, stat); + ptr.getClass().getCreatureStats(ptr).setAiSetting(mIndex, stat); } }; @@ -541,32 +535,32 @@ namespace MWScript interpreter.installSegment5 (Compiler::Ai::opcodeStopCombatExplicit, new OpStopCombat); interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI); - interpreter.installSegment5 (Compiler::Ai::opcodeSetHello, new OpSetAiSetting(0)); - interpreter.installSegment5 (Compiler::Ai::opcodeSetHelloExplicit, new OpSetAiSetting(0)); - interpreter.installSegment5 (Compiler::Ai::opcodeSetFight, new OpSetAiSetting(1)); - interpreter.installSegment5 (Compiler::Ai::opcodeSetFightExplicit, new OpSetAiSetting(1)); - interpreter.installSegment5 (Compiler::Ai::opcodeSetFlee, new OpSetAiSetting(2)); - interpreter.installSegment5 (Compiler::Ai::opcodeSetFleeExplicit, new OpSetAiSetting(2)); - interpreter.installSegment5 (Compiler::Ai::opcodeSetAlarm, new OpSetAiSetting(3)); - interpreter.installSegment5 (Compiler::Ai::opcodeSetAlarmExplicit, new OpSetAiSetting(3)); - - interpreter.installSegment5 (Compiler::Ai::opcodeModHello, new OpModAiSetting(0)); - interpreter.installSegment5 (Compiler::Ai::opcodeModHelloExplicit, new OpModAiSetting(0)); - interpreter.installSegment5 (Compiler::Ai::opcodeModFight, new OpModAiSetting(1)); - interpreter.installSegment5 (Compiler::Ai::opcodeModFightExplicit, new OpModAiSetting(1)); - interpreter.installSegment5 (Compiler::Ai::opcodeModFlee, new OpModAiSetting(2)); - interpreter.installSegment5 (Compiler::Ai::opcodeModFleeExplicit, new OpModAiSetting(2)); - interpreter.installSegment5 (Compiler::Ai::opcodeModAlarm, new OpModAiSetting(3)); - interpreter.installSegment5 (Compiler::Ai::opcodeModAlarmExplicit, new OpModAiSetting(3)); - - interpreter.installSegment5 (Compiler::Ai::opcodeGetHello, new OpGetAiSetting(0)); - interpreter.installSegment5 (Compiler::Ai::opcodeGetHelloExplicit, new OpGetAiSetting(0)); - interpreter.installSegment5 (Compiler::Ai::opcodeGetFight, new OpGetAiSetting(1)); - interpreter.installSegment5 (Compiler::Ai::opcodeGetFightExplicit, new OpGetAiSetting(1)); - interpreter.installSegment5 (Compiler::Ai::opcodeGetFlee, new OpGetAiSetting(2)); - interpreter.installSegment5 (Compiler::Ai::opcodeGetFleeExplicit, new OpGetAiSetting(2)); - interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarm, new OpGetAiSetting(3)); - interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarmExplicit, new OpGetAiSetting(3)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetHello, new OpSetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Hello)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetHelloExplicit, new OpSetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Hello)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetFight, new OpSetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Fight)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetFightExplicit, new OpSetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Fight)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetFlee, new OpSetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Flee)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetFleeExplicit, new OpSetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Flee)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetAlarm, new OpSetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Alarm)); + interpreter.installSegment5 (Compiler::Ai::opcodeSetAlarmExplicit, new OpSetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Alarm)); + + interpreter.installSegment5 (Compiler::Ai::opcodeModHello, new OpModAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Hello)); + interpreter.installSegment5 (Compiler::Ai::opcodeModHelloExplicit, new OpModAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Hello)); + interpreter.installSegment5 (Compiler::Ai::opcodeModFight, new OpModAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Fight)); + interpreter.installSegment5 (Compiler::Ai::opcodeModFightExplicit, new OpModAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Fight)); + interpreter.installSegment5 (Compiler::Ai::opcodeModFlee, new OpModAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Flee)); + interpreter.installSegment5 (Compiler::Ai::opcodeModFleeExplicit, new OpModAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Flee)); + interpreter.installSegment5 (Compiler::Ai::opcodeModAlarm, new OpModAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Alarm)); + interpreter.installSegment5 (Compiler::Ai::opcodeModAlarmExplicit, new OpModAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Alarm)); + + interpreter.installSegment5 (Compiler::Ai::opcodeGetHello, new OpGetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Hello)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetHelloExplicit, new OpGetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Hello)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetFight, new OpGetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Fight)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetFightExplicit, new OpGetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Fight)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetFlee, new OpGetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Flee)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetFleeExplicit, new OpGetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Flee)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarm, new OpGetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Alarm)); + interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarmExplicit, new OpGetAiSetting(MWMechanics::CreatureStats::AiSetting::AI_Alarm)); interpreter.installSegment5 (Compiler::Ai::opcodeFace, new OpFace); interpreter.installSegment5 (Compiler::Ai::opcodeFaceExplicit, new OpFace); From 1bf2ddac4db6b266f92559d4bfc157375c4fb7b2 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 7 May 2020 15:35:34 +0300 Subject: [PATCH 040/227] Cleanup Move static variable declaration out of the loop Remove redundant boolean argument from applyDrawableProperties() Improve HeightCullCallback class formatting --- components/nifosg/nifloader.cpp | 6 +-- components/terrain/quadtreeworld.cpp | 2 +- components/terrain/world.hpp | 68 ++++++++++++++++------------ 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 72654da6d..00576943a 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1111,7 +1111,7 @@ namespace NifOsg std::vector drawableProps; collectDrawableProperties(nifNode, drawableProps); - applyDrawableProperties(parentNode, drawableProps, composite, true, animflags, true); + applyDrawableProperties(parentNode, drawableProps, composite, true, animflags); // particle system updater (after the emitters and affectors in the scene graph) // I think for correct culling needs to be *before* the ParticleSystem, though osg examples do it the other way @@ -1203,7 +1203,7 @@ namespace NifOsg // above the actual renderable would be tedious. std::vector drawableProps; collectDrawableProperties(nifNode, drawableProps); - applyDrawableProperties(parentNode, drawableProps, composite, vertexColorsPresent, animflags, false); + applyDrawableProperties(parentNode, drawableProps, composite, vertexColorsPresent, animflags); } void handleTriShape(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) @@ -1755,7 +1755,7 @@ namespace NifOsg } void applyDrawableProperties(osg::Node* node, const std::vector& properties, SceneUtil::CompositeStateSetUpdater* composite, - bool hasVertexColors, int animflags, bool particleMaterial) + bool hasVertexColors, int animflags) { osg::StateSet* stateset = node->getOrCreateStateSet(); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 0140ade49..c43a9a21b 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -323,6 +323,7 @@ void updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil: return; } cv->pushCurrentMask(); + static bool debug = getenv("OPENMW_WATER_CULLING_DEBUG") != nullptr; for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); @@ -337,7 +338,6 @@ void updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil: continue; lowZ = bb._min.z(); - static bool debug = getenv("OPENMW_WATER_CULLING_DEBUG") != nullptr; if (!debug) break; osg::Box* b = new osg::Box; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index fb6c45967..a69d03ca9 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -40,36 +40,46 @@ namespace Terrain class ChunkManager; class CompositeMapRenderer; -class HeightCullCallback : public osg::NodeCallback -{ -public: - HeightCullCallback() : mLowZ(-std::numeric_limits::max()), mHighZ(std::numeric_limits::max()), mMask(~0) {} - - void setLowZ(float z) + class HeightCullCallback : public osg::NodeCallback { - mLowZ = z; - } - float getLowZ() const { return mLowZ; } - - void setHighZ(float highZ) - { - mHighZ = highZ; - } - float getHighZ() const { return mHighZ; } - - void setCullMask(unsigned int mask) { mMask = mask; } - unsigned int getCullMask() const { return mMask; } - - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) - { - if (mLowZ <= mHighZ) - traverse(node, nv); - } -private: - float mLowZ; - float mHighZ; - unsigned int mMask; -}; + public: + void setLowZ(float z) + { + mLowZ = z; + } + float getLowZ() const + { + return mLowZ; + } + + void setHighZ(float highZ) + { + mHighZ = highZ; + } + float getHighZ() const + { + return mHighZ; + } + + void setCullMask(unsigned int mask) + { + mMask = mask; + } + unsigned int getCullMask() const + { + return mMask; + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (mLowZ <= mHighZ) + traverse(node, nv); + } + private: + float mLowZ{-std::numeric_limits::max()}; + float mHighZ{std::numeric_limits::max()}; + unsigned int mMask{~0u}; + }; /** * @brief A View is a collection of rendering objects that are visible from a given camera/intersection. From b19f53aab610429fe12e30abf4bc07a40965dc42 Mon Sep 17 00:00:00 2001 From: Sisah Date: Fri, 8 May 2020 15:55:22 +0200 Subject: [PATCH 041/227] Fix parallax and specular for android --- files/shaders/objects_fragment.glsl | 4 ++-- files/shaders/terrain_fragment.glsl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 31e929a90..bc6d03639 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -90,7 +90,7 @@ void main() vec3 cameraPos = (gl_ModelViewMatrixInverse * vec4(0,0,0,1)).xyz; vec3 objectPos = (gl_ModelViewMatrixInverse * vec4(passViewPos, 1)).xyz; vec3 eyeDir = normalize(cameraPos - objectPos); - vec2 offset = getParallaxOffset(eyeDir, tbnTranspose, normalTex.a, (passTangent.w > 0) ? -1.f : 1.f); + vec2 offset = getParallaxOffset(eyeDir, tbnTranspose, normalTex.a, (passTangent.w > 0.0) ? -1.f : 1.f); adjustedDiffuseUV += offset; // only offset diffuse for now, other textures are more likely to be using a completely different UV set // TODO: check not working as the same UV buffer is being bound to different targets @@ -171,7 +171,7 @@ void main() #if @specularMap vec4 specTex = texture2D(specularMap, specularMapUV); - float shininess = specTex.a * 255; + float shininess = specTex.a * 255.0; vec3 matSpec = specTex.xyz; #else float shininess = gl_FrontMaterial.shininess; diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index b1917d33b..505061681 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -82,7 +82,7 @@ void main() #endif #if @specularMap - float shininess = 128; // TODO: make configurable + float shininess = 128.0; // TODO: make configurable vec3 matSpec = vec3(diffuseTex.a, diffuseTex.a, diffuseTex.a); #else float shininess = gl_FrontMaterial.shininess; From 039c9a37ebfbae90a07509f7fba4c5046747682c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 9 May 2020 00:31:10 +0300 Subject: [PATCH 042/227] Verifier: Don't check race of non-skin body parts (bug #5400) Remove unnecessary flag field check Remove magic numbers --- CHANGELOG.md | 1 + apps/opencs/model/tools/bodypartcheck.cpp | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77b126d85..56e778c0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Bug #5367: Selecting a spell on an enchanted item per hotkey always plays the equip sound Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures Bug #5370: Opening an unlocked but trapped door uses the key + Bug #5400: Editor: Verifier checks race of non-skin bodyparts Feature #5362: Show the soul gems' trapped soul in count dialog 0.46.0 diff --git a/apps/opencs/model/tools/bodypartcheck.cpp b/apps/opencs/model/tools/bodypartcheck.cpp index 26b807360..1490a8103 100644 --- a/apps/opencs/model/tools/bodypartcheck.cpp +++ b/apps/opencs/model/tools/bodypartcheck.cpp @@ -33,13 +33,10 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message CSMWorld::UniversalId id( CSMWorld::UniversalId::Type_BodyPart, bodyPart.mId ); // Check BYDT - if (bodyPart.mData.mPart > 14 ) + if (bodyPart.mData.mPart >= ESM::BodyPart::MP_Count ) messages.add(id, "Invalid part", "", CSMDoc::Message::Severity_Error); - if (bodyPart.mData.mFlags > 3 ) - messages.add(id, "Invalid flags", "", CSMDoc::Message::Severity_Error); - - if (bodyPart.mData.mType > 2 ) + if (bodyPart.mData.mType > ESM::BodyPart::MT_Armor ) messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error); // Check MODL @@ -48,9 +45,12 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message else if ( mMeshes.searchId( bodyPart.mModel ) == -1 ) messages.add(id, "Model '" + bodyPart.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); - // Check FNAM - if ( bodyPart.mRace.empty() ) - messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error); - else if ( mRaces.searchId( bodyPart.mRace ) == -1 ) - messages.add(id, "Race '" + bodyPart.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error); + // Check FNAM for skin body parts (for non-skin body parts it's meaningless) + if ( bodyPart.mData.mType == ESM::BodyPart::MT_Skin ) + { + if ( bodyPart.mRace.empty() ) + messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error); + else if ( mRaces.searchId( bodyPart.mRace ) == -1 ) + messages.add(id, "Race '" + bodyPart.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error); + } } From 45e6a03937010ce074a02669a1003e8c44c63a67 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 9 May 2020 02:01:55 +0300 Subject: [PATCH 043/227] Only reset dialogue history of dialogue GUI mode is gone --- apps/openmw/mwgui/dialogue.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index bb40bea33..76b219009 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -477,6 +477,8 @@ namespace MWGui void DialogueWindow::onClose() { + if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Dialogue)) + return; // Reset history for (DialogueText* text : mHistoryContents) delete text; From 15dc4d241dd2f0a92c5e9c28611282747f75d059 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 8 Apr 2020 10:55:35 +0400 Subject: [PATCH 044/227] Split sensors handling so the separate file --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 241 ++------------------- apps/openmw/mwinput/inputmanagerimp.hpp | 42 +--- apps/openmw/mwinput/sensormanager.cpp | 274 ++++++++++++++++++++++++ apps/openmw/mwinput/sensormanager.hpp | 73 +++++++ 5 files changed, 371 insertions(+), 261 deletions(-) create mode 100644 apps/openmw/mwinput/sensormanager.cpp create mode 100644 apps/openmw/mwinput/sensormanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2b4739ba9..19cf4ef69 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,7 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - inputmanagerimp + inputmanagerimp sensormanager ) add_openmw_dir (mwgui diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index acfe4f8cd..590cf61a6 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -31,6 +31,8 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" +#include "sensormanager.hpp" + namespace MWInput { InputManager::InputManager( @@ -79,20 +81,10 @@ namespace MWInput , mAttemptJump(false) , mInvUiScalingFactor(1.f) , mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input")) - , mGyroXSpeed(0.f) - , mGyroYSpeed(0.f) - , mGyroUpdateTimer(0.f) - , mGyroHSensitivity (Settings::Manager::getFloat("gyro horizontal sensitivity", "Input")) - , mGyroVSensitivity (Settings::Manager::getFloat("gyro vertical sensitivity", "Input")) - , mGyroHAxis(GyroscopeAxis::Minus_X) - , mGyroVAxis(GyroscopeAxis::Y) - , mGyroInputThreshold(Settings::Manager::getFloat("gyro input threshold", "Input")) , mFakeDeviceID(1) - , mGyroscope(nullptr) { mInputManager = new SDLUtil::InputWrapper(window, viewer, grab); mInputManager->setMouseEventCallback (this); - mInputManager->setSensorEventCallback (this); mInputManager->setKeyboardEventCallback (this); mInputManager->setWindowEventCallback(this); mInputManager->setControllerEventCallback(this); @@ -149,8 +141,8 @@ namespace MWInput } } - correctGyroscopeAxes(); - updateSensors(); + mSensorManager = new SensorManager(); + mInputManager->setSensorEventCallback (mSensorManager); float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); if (uiScale != 0.f) @@ -168,17 +160,15 @@ namespace MWInput // Enable all controls for (std::map::iterator it = mControlSwitch.begin(); it != mControlSwitch.end(); ++it) it->second = true; + + mSensorManager->clear(); } InputManager::~InputManager() { mInputBinder->save (mUserFile); - if (mGyroscope != nullptr) - { - SDL_SensorClose(mGyroscope); - mGyroscope = nullptr; - } + delete mSensorManager; delete mInputBinder; @@ -187,112 +177,6 @@ namespace MWInput delete mVideoWrapper; } - InputManager::GyroscopeAxis InputManager::mapGyroscopeAxis(const std::string& axis) - { - if (axis == "x") - return GyroscopeAxis::X; - else if (axis == "y") - return GyroscopeAxis::Y; - else if (axis == "z") - return GyroscopeAxis::Z; - else if (axis == "-x") - return GyroscopeAxis::Minus_X; - else if (axis == "-y") - return GyroscopeAxis::Minus_Y; - else if (axis == "-z") - return GyroscopeAxis::Minus_Z; - - return GyroscopeAxis::Unknown; - } - - void InputManager::correctGyroscopeAxes() - { - if (!Settings::Manager::getBool("enable gyroscope", "Input")) - return; - - // Treat setting from config as axes for landscape mode. - // If the device does not support orientation change, do nothing. - // Note: in is unclear how to correct axes for devices with non-standart Z axis direction. - mGyroHAxis = mapGyroscopeAxis(Settings::Manager::getString("gyro horizontal axis", "Input")); - mGyroVAxis = mapGyroscopeAxis(Settings::Manager::getString("gyro vertical axis", "Input")); - - SDL_DisplayOrientation currentOrientation = SDL_GetDisplayOrientation(Settings::Manager::getInt("screen", "Video")); - switch (currentOrientation) - { - case SDL_ORIENTATION_UNKNOWN: - return; - case SDL_ORIENTATION_LANDSCAPE: - break; - case SDL_ORIENTATION_LANDSCAPE_FLIPPED: - { - mGyroHAxis = GyroscopeAxis(-mGyroHAxis); - mGyroVAxis = GyroscopeAxis(-mGyroVAxis); - - break; - } - case SDL_ORIENTATION_PORTRAIT: - { - GyroscopeAxis oldVAxis = mGyroVAxis; - mGyroVAxis = mGyroHAxis; - mGyroHAxis = GyroscopeAxis(-oldVAxis); - - break; - } - case SDL_ORIENTATION_PORTRAIT_FLIPPED: - { - GyroscopeAxis oldVAxis = mGyroVAxis; - mGyroVAxis = GyroscopeAxis(-mGyroHAxis); - mGyroHAxis = oldVAxis; - - break; - } - } - } - - void InputManager::updateSensors() - { - if (Settings::Manager::getBool("enable gyroscope", "Input")) - { - int numSensors = SDL_NumSensors(); - - for (int i = 0; i < numSensors; ++i) - { - if (SDL_SensorGetDeviceType(i) == SDL_SENSOR_GYRO) - { - // It is unclear how to handle several enabled gyroscopes, so use the first one. - // Note: Android registers some gyroscope as two separate sensors, for non-wake-up mode and for wake-up mode. - if (mGyroscope != nullptr) - { - SDL_SensorClose(mGyroscope); - mGyroscope = nullptr; - mGyroXSpeed = mGyroYSpeed = 0.f; - mGyroUpdateTimer = 0.f; - } - - // FIXME: SDL2 does not provide a way to configure a sensor update frequency so far. - SDL_Sensor *sensor = SDL_SensorOpen(i); - if (sensor == nullptr) - Log(Debug::Error) << "Couldn't open sensor " << SDL_SensorGetDeviceName(i) << ": " << SDL_GetError(); - else - { - mGyroscope = sensor; - break; - } - } - } - } - else - { - if (mGyroscope != nullptr) - { - SDL_SensorClose(mGyroscope); - mGyroscope = nullptr; - mGyroXSpeed = mGyroYSpeed = 0.f; - mGyroUpdateTimer = 0.f; - } - } - } - bool InputManager::isWindowVisible() { return mWindowVisible; @@ -731,36 +615,8 @@ namespace MWInput } } - if (mGyroXSpeed != 0.f || mGyroYSpeed != 0.f) - { - if (mGyroUpdateTimer > 0.5f) - { - // More than half of second passed since the last gyroscope update. - // A device more likely was disconnected or switched to the sleep mode. - // Reset current rotation speed and wait for update. - mGyroXSpeed = mGyroYSpeed = 0.f; - mGyroUpdateTimer = 0.f; - } - - if (!mGuiCursorEnabled) - { - resetIdleTime(); - - float rot[3]; - rot[0] = mGyroYSpeed * dt * mGyroVSensitivity * 4 * (mInvertY ? -1 : 1); - rot[1] = 0.0f; - rot[2] = mGyroXSpeed * dt * mGyroHSensitivity * 4 * (mInvertX ? -1 : 1); - - // Only actually turn player when we're not in vanity mode - if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && mControlSwitch["playerlooking"]) - { - mPlayer->yaw(rot[2]); - mPlayer->pitch(rot[0]); - } - } - - mGyroUpdateTimer += dt; - } + if (mSensorManager->update(dt, mGuiCursorEnabled, mControlSwitch["playerlooking"])) + resetIdleTime(); // Disable movement in Gui mode if (!(MWBase::Environment::get().getWindowManager()->isGuiMode() @@ -957,27 +813,6 @@ namespace MWInput if (it->first == "Input" && it->second == "camera sensitivity") mCameraSensitivity = Settings::Manager::getFloat("camera sensitivity", "Input"); - if (it->first == "Input" && it->second == "gyro horizontal sensitivity") - mGyroHSensitivity = Settings::Manager::getFloat("gyro horizontal sensitivity", "Input"); - - if (it->first == "Input" && it->second == "gyro vertical sensitivity") - mGyroVSensitivity = Settings::Manager::getFloat("gyro vertical sensitivity", "Input"); - - if (it->first == "Input" && it->second == "enable gyroscope") - { - correctGyroscopeAxes(); - updateSensors(); - } - - if (it->first == "Input" && it->second == "gyro horizontal axis") - correctGyroscopeAxes(); - - if (it->first == "Input" && it->second == "gyro vertical axis") - correctGyroscopeAxes(); - - if (it->first == "Input" && it->second == "gyro input threshold") - mGyroInputThreshold = Settings::Manager::getFloat("gyro input threshold", "Input"); - if (it->first == "Input" && it->second == "grab cursor") mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); @@ -1006,6 +841,8 @@ namespace MWInput Settings::Manager::getBool("fullscreen", "Video"), Settings::Manager::getBool("window border", "Video")); } + + mSensorManager->processChangedSettings(changed); } bool InputManager::getControlSwitch (const std::string& sw) @@ -1131,57 +968,6 @@ namespace MWInput mJoystickLastUsed = false; } - float InputManager::getGyroAxisSpeed(GyroscopeAxis axis, const SDL_SensorEvent &arg) const - { - switch (axis) - { - case GyroscopeAxis::X: - case GyroscopeAxis::Y: - case GyroscopeAxis::Z: - return std::abs(arg.data[0]) >= mGyroInputThreshold ? arg.data[axis-1] : 0.f; - case GyroscopeAxis::Minus_X: - case GyroscopeAxis::Minus_Y: - case GyroscopeAxis::Minus_Z: - return std::abs(arg.data[0]) >= mGyroInputThreshold ? -arg.data[std::abs(axis)-1] : 0.f; - default: - return 0.f; - } - } - - void InputManager::displayOrientationChanged() - { - correctGyroscopeAxes(); - } - - void InputManager::sensorUpdated(const SDL_SensorEvent &arg) - { - if (!Settings::Manager::getBool("enable gyroscope", "Input")) - return; - - SDL_Sensor *sensor = SDL_SensorFromInstanceID(arg.which); - if (!sensor) - { - Log(Debug::Info) << "Couldn't get sensor for sensor event"; - return; - } - - switch (SDL_SensorGetType(sensor)) - { - case SDL_SENSOR_ACCEL: - break; - case SDL_SENSOR_GYRO: - { - mGyroXSpeed = getGyroAxisSpeed(mGyroHAxis, arg); - mGyroYSpeed = getGyroAxisSpeed(mGyroVAxis, arg); - mGyroUpdateTimer = 0.f; - - break; - } - default: - break; - } - } - void InputManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg ) { mInputBinder->mouseMoved (arg); @@ -2235,4 +2021,9 @@ namespace MWInput //MyGUI's buttons are 0 indexed return MyGUI::MouseButton::Enum(button - 1); } + + void InputManager::setPlayer (MWWorld::Player* player) + { + mPlayer = player; + } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 23562ba79..6b5b21653 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -17,6 +17,11 @@ #include "../mwbase/inputmanager.hpp" +namespace MWInput +{ + class SensorManager; +} + namespace MWWorld { class Player; @@ -67,7 +72,6 @@ namespace MWInput public MWBase::InputManager, public SDLUtil::KeyListener, public SDLUtil::MouseListener, - public SDLUtil::SensorListener, public SDLUtil::WindowListener, public SDLUtil::ControllerListener, public ICS::ChannelListener, @@ -92,7 +96,7 @@ namespace MWInput virtual void update(float dt, bool disableControls=false, bool disableEvents=false); - void setPlayer (MWWorld::Player* player) { mPlayer = player; } + void setPlayer (MWWorld::Player* player); virtual void changeInputMode(bool guiMode); @@ -115,7 +119,6 @@ namespace MWInput virtual bool joystickLastUsed() {return mJoystickLastUsed;} - public: virtual void keyPressed(const SDL_KeyboardEvent &arg ); virtual void keyReleased( const SDL_KeyboardEvent &arg ); virtual void textInput (const SDL_TextInputEvent &arg); @@ -126,9 +129,6 @@ namespace MWInput virtual void mouseWheelMoved( const SDL_MouseWheelEvent &arg); - virtual void sensorUpdated(const SDL_SensorEvent &arg); - virtual void displayOrientationChanged(); - virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg); virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg); virtual void axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg); @@ -168,17 +168,6 @@ namespace MWInput virtual void readRecord(ESM::ESMReader& reader, uint32_t type); private: - enum GyroscopeAxis - { - Unknown = 0, - X = 1, - Y = 2, - Z = 3, - Minus_X = -1, - Minus_Y = -2, - Minus_Z = -3 - }; - SDL_Window* mWindow; bool mWindowVisible; osg::ref_ptr mViewer; @@ -235,17 +224,8 @@ namespace MWInput float mInvUiScalingFactor; float mGamepadCursorSpeed; - float mGyroXSpeed; - float mGyroYSpeed; - float mGyroUpdateTimer; + SensorManager* mSensorManager; - float mGyroHSensitivity; - float mGyroVSensitivity; - GyroscopeAxis mGyroHAxis; - GyroscopeAxis mGyroVAxis; - float mGyroInputThreshold; - - private: void convertMousePosForMyGUI(int& x, int& y); MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button); @@ -263,15 +243,9 @@ namespace MWInput bool gamepadToGuiControl(const SDL_ControllerAxisEvent &arg); void updateCursorMode(); - void updateSensors(); - void correctGyroscopeAxes(); - GyroscopeAxis mapGyroscopeAxis(const std::string& axis); bool checkAllowedToUseItems() const; - float getGyroAxisSpeed(GyroscopeAxis axis, const SDL_SensorEvent &arg) const; - - private: void toggleMainMenu(); void toggleSpell(); void toggleWeapon(); @@ -296,9 +270,7 @@ namespace MWInput void loadControllerDefaults(bool force = false); int mFakeDeviceID; //As we only support one controller at a time, use a fake deviceID so we don't lose bindings when switching controllers - SDL_Sensor* mGyroscope; - private: enum Actions { // please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files diff --git a/apps/openmw/mwinput/sensormanager.cpp b/apps/openmw/mwinput/sensormanager.cpp new file mode 100644 index 000000000..55a0882f5 --- /dev/null +++ b/apps/openmw/mwinput/sensormanager.cpp @@ -0,0 +1,274 @@ +#include "sensormanager.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" + +namespace MWInput +{ + SensorManager::SensorManager() + : mInvertX(Settings::Manager::getBool("invert x axis", "Input")) + , mInvertY(Settings::Manager::getBool("invert y axis", "Input")) + , mGyroXSpeed(0.f) + , mGyroYSpeed(0.f) + , mGyroUpdateTimer(0.f) + , mGyroHSensitivity(Settings::Manager::getFloat("gyro horizontal sensitivity", "Input")) + , mGyroVSensitivity(Settings::Manager::getFloat("gyro vertical sensitivity", "Input")) + , mGyroHAxis(GyroscopeAxis::Minus_X) + , mGyroVAxis(GyroscopeAxis::Y) + , mGyroInputThreshold(Settings::Manager::getFloat("gyro input threshold", "Input")) + , mGyroscope(nullptr) + { + init(); + } + + void SensorManager::init() + { + correctGyroscopeAxes(); + updateSensors(); + } + + void SensorManager::clear() + { + mGyroXSpeed = 0.f; + mGyroYSpeed = 0.f; + mGyroUpdateTimer = 0.f; + } + + SensorManager::~SensorManager() + { + if (mGyroscope != nullptr) + { + SDL_SensorClose(mGyroscope); + mGyroscope = nullptr; + } + } + + SensorManager::GyroscopeAxis SensorManager::mapGyroscopeAxis(const std::string& axis) + { + if (axis == "x") + return GyroscopeAxis::X; + else if (axis == "y") + return GyroscopeAxis::Y; + else if (axis == "z") + return GyroscopeAxis::Z; + else if (axis == "-x") + return GyroscopeAxis::Minus_X; + else if (axis == "-y") + return GyroscopeAxis::Minus_Y; + else if (axis == "-z") + return GyroscopeAxis::Minus_Z; + + return GyroscopeAxis::Unknown; + } + + void SensorManager::correctGyroscopeAxes() + { + if (!Settings::Manager::getBool("enable gyroscope", "Input")) + return; + + // Treat setting from config as axes for landscape mode. + // If the device does not support orientation change, do nothing. + // Note: in is unclear how to correct axes for devices with non-standart Z axis direction. + mGyroHAxis = mapGyroscopeAxis(Settings::Manager::getString("gyro horizontal axis", "Input")); + mGyroVAxis = mapGyroscopeAxis(Settings::Manager::getString("gyro vertical axis", "Input")); + + SDL_DisplayOrientation currentOrientation = SDL_GetDisplayOrientation(Settings::Manager::getInt("screen", "Video")); + switch (currentOrientation) + { + case SDL_ORIENTATION_UNKNOWN: + return; + case SDL_ORIENTATION_LANDSCAPE: + break; + case SDL_ORIENTATION_LANDSCAPE_FLIPPED: + { + mGyroHAxis = GyroscopeAxis(-mGyroHAxis); + mGyroVAxis = GyroscopeAxis(-mGyroVAxis); + + break; + } + case SDL_ORIENTATION_PORTRAIT: + { + GyroscopeAxis oldVAxis = mGyroVAxis; + mGyroVAxis = mGyroHAxis; + mGyroHAxis = GyroscopeAxis(-oldVAxis); + + break; + } + case SDL_ORIENTATION_PORTRAIT_FLIPPED: + { + GyroscopeAxis oldVAxis = mGyroVAxis; + mGyroVAxis = GyroscopeAxis(-mGyroHAxis); + mGyroHAxis = oldVAxis; + + break; + } + } + } + + void SensorManager::updateSensors() + { + if (Settings::Manager::getBool("enable gyroscope", "Input")) + { + int numSensors = SDL_NumSensors(); + + for (int i = 0; i < numSensors; ++i) + { + if (SDL_SensorGetDeviceType(i) == SDL_SENSOR_GYRO) + { + // It is unclear how to handle several enabled gyroscopes, so use the first one. + // Note: Android registers some gyroscope as two separate sensors, for non-wake-up mode and for wake-up mode. + if (mGyroscope != nullptr) + { + SDL_SensorClose(mGyroscope); + mGyroscope = nullptr; + mGyroXSpeed = mGyroYSpeed = 0.f; + mGyroUpdateTimer = 0.f; + } + + // FIXME: SDL2 does not provide a way to configure a sensor update frequency so far. + SDL_Sensor *sensor = SDL_SensorOpen(i); + if (sensor == nullptr) + Log(Debug::Error) << "Couldn't open sensor " << SDL_SensorGetDeviceName(i) << ": " << SDL_GetError(); + else + { + mGyroscope = sensor; + break; + } + } + } + } + else + { + if (mGyroscope != nullptr) + { + SDL_SensorClose(mGyroscope); + mGyroscope = nullptr; + mGyroXSpeed = mGyroYSpeed = 0.f; + mGyroUpdateTimer = 0.f; + } + } + } + + void SensorManager::processChangedSettings(const Settings::CategorySettingVector& changed) + { + for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) + { + if (it->first == "Input" && it->second == "invert x axis") + mInvertX = Settings::Manager::getBool("invert x axis", "Input"); + + if (it->first == "Input" && it->second == "invert y axis") + mInvertY = Settings::Manager::getBool("invert y axis", "Input"); + + if (it->first == "Input" && it->second == "gyro horizontal sensitivity") + mGyroHSensitivity = Settings::Manager::getFloat("gyro horizontal sensitivity", "Input"); + + if (it->first == "Input" && it->second == "gyro vertical sensitivity") + mGyroVSensitivity = Settings::Manager::getFloat("gyro vertical sensitivity", "Input"); + + if (it->first == "Input" && it->second == "enable gyroscope") + init(); + + if (it->first == "Input" && it->second == "gyro horizontal axis") + correctGyroscopeAxes(); + + if (it->first == "Input" && it->second == "gyro vertical axis") + correctGyroscopeAxes(); + + if (it->first == "Input" && it->second == "gyro input threshold") + mGyroInputThreshold = Settings::Manager::getFloat("gyro input threshold", "Input"); + } + } + + float SensorManager::getGyroAxisSpeed(GyroscopeAxis axis, const SDL_SensorEvent &arg) const + { + switch (axis) + { + case GyroscopeAxis::X: + case GyroscopeAxis::Y: + case GyroscopeAxis::Z: + return std::abs(arg.data[0]) >= mGyroInputThreshold ? arg.data[axis-1] : 0.f; + case GyroscopeAxis::Minus_X: + case GyroscopeAxis::Minus_Y: + case GyroscopeAxis::Minus_Z: + return std::abs(arg.data[0]) >= mGyroInputThreshold ? -arg.data[std::abs(axis)-1] : 0.f; + default: + return 0.f; + } + } + + void SensorManager::displayOrientationChanged() + { + correctGyroscopeAxes(); + } + + void SensorManager::sensorUpdated(const SDL_SensorEvent &arg) + { + if (!Settings::Manager::getBool("enable gyroscope", "Input")) + return; + + SDL_Sensor *sensor = SDL_SensorFromInstanceID(arg.which); + if (!sensor) + { + Log(Debug::Info) << "Couldn't get sensor for sensor event"; + return; + } + + switch (SDL_SensorGetType(sensor)) + { + case SDL_SENSOR_ACCEL: + break; + case SDL_SENSOR_GYRO: + { + mGyroXSpeed = getGyroAxisSpeed(mGyroHAxis, arg); + mGyroYSpeed = getGyroAxisSpeed(mGyroVAxis, arg); + mGyroUpdateTimer = 0.f; + + break; + } + default: + break; + } + } + + bool SensorManager::update(float dt, bool isCursorEnabled, bool isTurningEnabled) + { + if (mGyroXSpeed == 0.f && mGyroYSpeed == 0.f) + return false; + + if (mGyroUpdateTimer > 0.5f) + { + // More than half of second passed since the last gyroscope update. + // A device more likely was disconnected or switched to the sleep mode. + // Reset current rotation speed and wait for update. + clear(); + mGyroUpdateTimer = 0.f; + return false; + } + + mGyroUpdateTimer += dt; + + if (!isCursorEnabled) + { + float rot[3]; + rot[0] = mGyroYSpeed * dt * mGyroVSensitivity * 4 * (mInvertY ? -1 : 1); + rot[1] = 0.0f; + rot[2] = mGyroXSpeed * dt * mGyroHSensitivity * 4 * (mInvertX ? -1 : 1); + + // Only actually turn player when we're not in vanity mode + if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && isTurningEnabled) + { + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + player.yaw(rot[2]); + player.pitch(rot[0]); + } + + return true; + } + + return false; + } +} diff --git a/apps/openmw/mwinput/sensormanager.hpp b/apps/openmw/mwinput/sensormanager.hpp new file mode 100644 index 000000000..d655e9c07 --- /dev/null +++ b/apps/openmw/mwinput/sensormanager.hpp @@ -0,0 +1,73 @@ +#ifndef MWINPUT_MWSENSORMANAGER_H +#define MWINPUT_MWSENSORMANAGER_H + +#include + +#include +#include + +namespace SDLUtil +{ + class InputWrapper; +} + +namespace MWWorld +{ + class Player; +} + +namespace MWInput +{ + class SensorManager : public SDLUtil::SensorListener + { + public: + SensorManager(); + + virtual ~SensorManager(); + + void init(); + + void clear(); + + bool update(float dt, bool isCursorEnabled, bool isTurningEnabled); + + public: + virtual void sensorUpdated(const SDL_SensorEvent &arg); + virtual void displayOrientationChanged(); + void processChangedSettings(const Settings::CategorySettingVector& changed); + + private: + enum GyroscopeAxis + { + Unknown = 0, + X = 1, + Y = 2, + Z = 3, + Minus_X = -1, + Minus_Y = -2, + Minus_Z = -3 + }; + + bool mInvertX; + bool mInvertY; + + float mGyroXSpeed; + float mGyroYSpeed; + float mGyroUpdateTimer; + + float mGyroHSensitivity; + float mGyroVSensitivity; + GyroscopeAxis mGyroHAxis; + GyroscopeAxis mGyroVAxis; + float mGyroInputThreshold; + + private: + + void updateSensors(); + void correctGyroscopeAxes(); + GyroscopeAxis mapGyroscopeAxis(const std::string& axis); + float getGyroAxisSpeed(GyroscopeAxis axis, const SDL_SensorEvent &arg) const; + SDL_Sensor* mGyroscope; + }; +} +#endif From 3c09d05615d73daa1627fef48eff386dfb6dd20f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 8 Apr 2020 11:10:17 +0400 Subject: [PATCH 045/227] Split actions enum to the separate file --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwinput/actions.hpp | 79 +++++++++++++++++++++++++ apps/openmw/mwinput/inputmanagerimp.hpp | 75 +---------------------- 3 files changed, 82 insertions(+), 74 deletions(-) create mode 100644 apps/openmw/mwinput/actions.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 19cf4ef69..100d56525 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,7 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - inputmanagerimp sensormanager + inputmanagerimp sensormanager actions ) add_openmw_dir (mwgui diff --git a/apps/openmw/mwinput/actions.hpp b/apps/openmw/mwinput/actions.hpp new file mode 100644 index 000000000..a1c160712 --- /dev/null +++ b/apps/openmw/mwinput/actions.hpp @@ -0,0 +1,79 @@ +#ifndef MWINPUT_ACTIONS_H +#define MWINPUT_ACTIONS_H + +namespace MWInput +{ + enum Actions + { + // please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files + + A_GameMenu, + + A_Unused, + + A_Screenshot, // Take a screenshot + + A_Inventory, // Toggle inventory screen + + A_Console, // Toggle console screen + + A_MoveLeft, // Move player left / right + A_MoveRight, + A_MoveForward, // Forward / Backward + A_MoveBackward, + + A_Activate, + + A_Use, //Use weapon, spell, etc. + A_Jump, + A_AutoMove, //Toggle Auto-move forward + A_Rest, //Rest + A_Journal, //Journal + A_Weapon, //Draw/Sheath weapon + A_Spell, //Ready/Unready Casting + A_Run, //Run when held + A_CycleSpellLeft, //cycling through spells + A_CycleSpellRight, + A_CycleWeaponLeft, //Cycling through weapons + A_CycleWeaponRight, + A_ToggleSneak, //Toggles Sneak + A_AlwaysRun, //Toggle Walking/Running + A_Sneak, + + A_QuickSave, + A_QuickLoad, + A_QuickMenu, + A_ToggleWeapon, + A_ToggleSpell, + + A_TogglePOV, + + A_QuickKey1, + A_QuickKey2, + A_QuickKey3, + A_QuickKey4, + A_QuickKey5, + A_QuickKey6, + A_QuickKey7, + A_QuickKey8, + A_QuickKey9, + A_QuickKey10, + + A_QuickKeysMenu, + + A_ToggleHUD, + + A_ToggleDebug, + + A_LookUpDown, //Joystick look + A_LookLeftRight, + A_MoveForwardBackward, + A_MoveLeftRight, + + A_ZoomIn, + A_ZoomOut, + + A_Last // Marker for the last item + }; +} +#endif diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 6b5b21653..d2278483d 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -17,6 +17,8 @@ #include "../mwbase/inputmanager.hpp" +#include "actions.hpp" + namespace MWInput { class SensorManager; @@ -270,79 +272,6 @@ namespace MWInput void loadControllerDefaults(bool force = false); int mFakeDeviceID; //As we only support one controller at a time, use a fake deviceID so we don't lose bindings when switching controllers - - enum Actions - { - // please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files - - A_GameMenu, - - A_Unused, - - A_Screenshot, // Take a screenshot - - A_Inventory, // Toggle inventory screen - - A_Console, // Toggle console screen - - A_MoveLeft, // Move player left / right - A_MoveRight, - A_MoveForward, // Forward / Backward - A_MoveBackward, - - A_Activate, - - A_Use, //Use weapon, spell, etc. - A_Jump, - A_AutoMove, //Toggle Auto-move forward - A_Rest, //Rest - A_Journal, //Journal - A_Weapon, //Draw/Sheath weapon - A_Spell, //Ready/Unready Casting - A_Run, //Run when held - A_CycleSpellLeft, //cycling through spells - A_CycleSpellRight, - A_CycleWeaponLeft, //Cycling through weapons - A_CycleWeaponRight, - A_ToggleSneak, //Toggles Sneak - A_AlwaysRun, //Toggle Walking/Running - A_Sneak, - - A_QuickSave, - A_QuickLoad, - A_QuickMenu, - A_ToggleWeapon, - A_ToggleSpell, - - A_TogglePOV, - - A_QuickKey1, - A_QuickKey2, - A_QuickKey3, - A_QuickKey4, - A_QuickKey5, - A_QuickKey6, - A_QuickKey7, - A_QuickKey8, - A_QuickKey9, - A_QuickKey10, - - A_QuickKeysMenu, - - A_ToggleHUD, - - A_ToggleDebug, - - A_LookUpDown, //Joystick look - A_LookLeftRight, - A_MoveForwardBackward, - A_MoveLeftRight, - - A_ZoomIn, - A_ZoomOut, - - A_Last // Marker for the last item - }; }; } #endif From 1560e71f4ee394d751e66b5d1139cfa2d666b2f9 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 8 Apr 2020 11:43:45 +0400 Subject: [PATCH 046/227] Move SDL mappigs to the separate file --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/inputmanager.hpp | 2 - apps/openmw/mwinput/inputmanagerimp.cpp | 72 +--------------------- apps/openmw/mwinput/inputmanagerimp.hpp | 10 --- apps/openmw/mwinput/sdlmappings.cpp | 81 +++++++++++++++++++++++++ apps/openmw/mwinput/sdlmappings.hpp | 21 +++++++ 6 files changed, 104 insertions(+), 84 deletions(-) create mode 100644 apps/openmw/mwinput/sdlmappings.cpp create mode 100644 apps/openmw/mwinput/sdlmappings.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 100d56525..ff24b880f 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,7 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - inputmanagerimp sensormanager actions + actions inputmanagerimp sdlmappings sensormanager ) add_openmw_dir (mwgui diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 0eb06ee3d..bb2ff9998 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -54,8 +54,6 @@ namespace MWBase virtual std::string getActionDescription (int action) = 0; virtual std::string getActionKeyBindingName (int action) = 0; virtual std::string getActionControllerBindingName (int action) = 0; - virtual std::string sdlControllerAxisToString(int axis) = 0; - virtual std::string sdlControllerButtonToString(int button) = 0; ///Actions available for binding to keyboard buttons virtual std::vector getActionKeySorting() = 0; ///Actions available for binding to controller buttons diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 590cf61a6..5326c2d5c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -31,6 +31,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" +#include "sdlmappings.hpp" #include "sensormanager.hpp" namespace MWInput @@ -1705,65 +1706,6 @@ namespace MWInput return "#{sNone}"; } - std::string InputManager::sdlControllerButtonToString(int button) - { - switch(button) - { - case SDL_CONTROLLER_BUTTON_A: - return "A Button"; - case SDL_CONTROLLER_BUTTON_B: - return "B Button"; - case SDL_CONTROLLER_BUTTON_BACK: - return "Back Button"; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: - return "DPad Down"; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: - return "DPad Left"; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: - return "DPad Right"; - case SDL_CONTROLLER_BUTTON_DPAD_UP: - return "DPad Up"; - case SDL_CONTROLLER_BUTTON_GUIDE: - return "Guide Button"; - case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: - return "Left Shoulder"; - case SDL_CONTROLLER_BUTTON_LEFTSTICK: - return "Left Stick Button"; - case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: - return "Right Shoulder"; - case SDL_CONTROLLER_BUTTON_RIGHTSTICK: - return "Right Stick Button"; - case SDL_CONTROLLER_BUTTON_START: - return "Start Button"; - case SDL_CONTROLLER_BUTTON_X: - return "X Button"; - case SDL_CONTROLLER_BUTTON_Y: - return "Y Button"; - default: - return "Button " + std::to_string(button); - } - } - std::string InputManager::sdlControllerAxisToString(int axis) - { - switch(axis) - { - case SDL_CONTROLLER_AXIS_LEFTX: - return "Left Stick X"; - case SDL_CONTROLLER_AXIS_LEFTY: - return "Left Stick Y"; - case SDL_CONTROLLER_AXIS_RIGHTX: - return "Right Stick X"; - case SDL_CONTROLLER_AXIS_RIGHTY: - return "Right Stick Y"; - case SDL_CONTROLLER_AXIS_TRIGGERLEFT: - return "Left Trigger"; - case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: - return "Right Trigger"; - default: - return "Axis " + std::to_string(axis); - } - } - std::vector InputManager::getActionKeySorting() { std::vector ret; @@ -2010,18 +1952,6 @@ namespace MWInput loadControllerDefaults(true); } - MyGUI::MouseButton InputManager::sdlButtonToMyGUI(Uint8 button) - { - //The right button is the second button, according to MyGUI - if(button == SDL_BUTTON_RIGHT) - button = SDL_BUTTON_MIDDLE; - else if(button == SDL_BUTTON_MIDDLE) - button = SDL_BUTTON_RIGHT; - - //MyGUI's buttons are 0 indexed - return MyGUI::MouseButton::Enum(button - 1); - } - void InputManager::setPlayer (MWWorld::Player* player) { mPlayer = player; diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d2278483d..1e5c89cda 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -39,11 +39,6 @@ namespace ICS class InputControlSystem; } -namespace MyGUI -{ - struct MouseButton; -} - namespace Files { struct ConfigurationManager; @@ -230,11 +225,6 @@ namespace MWInput void convertMousePosForMyGUI(int& x, int& y); - MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button); - - virtual std::string sdlControllerAxisToString(int axis); - virtual std::string sdlControllerButtonToString(int button); - void resetIdleTime(); void updateIdleTime(float dt); diff --git a/apps/openmw/mwinput/sdlmappings.cpp b/apps/openmw/mwinput/sdlmappings.cpp new file mode 100644 index 000000000..53c9e77fe --- /dev/null +++ b/apps/openmw/mwinput/sdlmappings.cpp @@ -0,0 +1,81 @@ +#include "sdlmappings.hpp" + +#include + +#include +#include + +namespace MWInput +{ + std::string sdlControllerButtonToString(int button) + { + switch(button) + { + case SDL_CONTROLLER_BUTTON_A: + return "A Button"; + case SDL_CONTROLLER_BUTTON_B: + return "B Button"; + case SDL_CONTROLLER_BUTTON_BACK: + return "Back Button"; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + return "DPad Down"; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + return "DPad Left"; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + return "DPad Right"; + case SDL_CONTROLLER_BUTTON_DPAD_UP: + return "DPad Up"; + case SDL_CONTROLLER_BUTTON_GUIDE: + return "Guide Button"; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: + return "Left Shoulder"; + case SDL_CONTROLLER_BUTTON_LEFTSTICK: + return "Left Stick Button"; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: + return "Right Shoulder"; + case SDL_CONTROLLER_BUTTON_RIGHTSTICK: + return "Right Stick Button"; + case SDL_CONTROLLER_BUTTON_START: + return "Start Button"; + case SDL_CONTROLLER_BUTTON_X: + return "X Button"; + case SDL_CONTROLLER_BUTTON_Y: + return "Y Button"; + default: + return "Button " + std::to_string(button); + } + } + + std::string sdlControllerAxisToString(int axis) + { + switch(axis) + { + case SDL_CONTROLLER_AXIS_LEFTX: + return "Left Stick X"; + case SDL_CONTROLLER_AXIS_LEFTY: + return "Left Stick Y"; + case SDL_CONTROLLER_AXIS_RIGHTX: + return "Right Stick X"; + case SDL_CONTROLLER_AXIS_RIGHTY: + return "Right Stick Y"; + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: + return "Left Trigger"; + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + return "Right Trigger"; + default: + return "Axis " + std::to_string(axis); + } + } + + MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button) + { + //The right button is the second button, according to MyGUI + if(button == SDL_BUTTON_RIGHT) + button = SDL_BUTTON_MIDDLE; + else if(button == SDL_BUTTON_MIDDLE) + button = SDL_BUTTON_RIGHT; + + //MyGUI's buttons are 0 indexed + return MyGUI::MouseButton::Enum(button - 1); + } +} diff --git a/apps/openmw/mwinput/sdlmappings.hpp b/apps/openmw/mwinput/sdlmappings.hpp new file mode 100644 index 000000000..dd6d750cb --- /dev/null +++ b/apps/openmw/mwinput/sdlmappings.hpp @@ -0,0 +1,21 @@ +#ifndef MWINPUT_SDLMAPPINGS_H +#define MWINPUT_SDLMAPPINGS_H + +#include + +#include + +namespace MyGUI +{ + struct MouseButton; +} + +namespace MWInput +{ + std::string sdlControllerButtonToString(int button); + + std::string sdlControllerAxisToString(int axis); + + MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button); +} +#endif From fcac7d3ab770c07468169b506c65660a1a84e435 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 8 Apr 2020 14:48:23 +0400 Subject: [PATCH 047/227] Split mouse handling to the separate file --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/inputmanager.hpp | 6 + apps/openmw/mwinput/inputmanagerimp.cpp | 185 +++---------------- apps/openmw/mwinput/inputmanagerimp.hpp | 23 +-- apps/openmw/mwinput/mousemanager.cpp | 231 ++++++++++++++++++++++++ apps/openmw/mwinput/mousemanager.hpp | 70 +++++++ 6 files changed, 339 insertions(+), 178 deletions(-) create mode 100644 apps/openmw/mwinput/mousemanager.cpp create mode 100644 apps/openmw/mwinput/mousemanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index ff24b880f..b5ea4fabf 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,7 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - actions inputmanagerimp sdlmappings sensormanager + actions inputmanagerimp mousemanager sdlmappings sensormanager ) add_openmw_dir (mwgui diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index bb2ff9998..29c876e2b 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -47,6 +47,7 @@ namespace MWBase virtual void processChangedSettings(const std::set< std::pair >& changed) = 0; virtual void setDragDrop(bool dragDrop) = 0; + virtual void setGamepadGuiCursorEnabled(bool enabled) = 0; virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; virtual bool getControlSwitch (const std::string& sw) = 0; @@ -67,10 +68,15 @@ namespace MWBase /// Returns if the last used input device was a joystick or a keyboard /// @return true if joystick, false otherwise virtual bool joystickLastUsed() = 0; + virtual void setJoystickLastUsed(bool enabled) = 0; virtual int countSavedGameRecords() const = 0; virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) = 0; virtual void readRecord(ESM::ESMReader& reader, uint32_t type) = 0; + + virtual void setPlayerControlsEnabled(bool enabled) = 0; + + virtual void resetIdleTime() = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 5326c2d5c..e79ec145a 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -31,6 +30,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" +#include "mousemanager.hpp" #include "sdlmappings.hpp" #include "sensormanager.hpp" @@ -60,18 +60,12 @@ namespace MWInput , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) , mControlsDisabled(false) , mJoystickEnabled (Settings::Manager::getBool("enable controller", "Input")) - , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) - , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) - , mMouseLookEnabled(false) , mGuiCursorEnabled(true) , mGamepadGuiCursorEnabled(true) , mDetectingKeyboard(false) , mOverencumberedMessageDelay(0.f) - , mGuiCursorX(0) - , mGuiCursorY(0) - , mMouseWheel(0) , mGamepadZoom(0) , mUserFileExists(userFileExists) , mAlwaysRunActive(Settings::Manager::getBool("always run", "Input")) @@ -80,12 +74,10 @@ namespace MWInput , mSneakGamepadShortcut(false) , mSneaking(false) , mAttemptJump(false) - , mInvUiScalingFactor(1.f) , mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input")) , mFakeDeviceID(1) { mInputManager = new SDLUtil::InputWrapper(window, viewer, grab); - mInputManager->setMouseEventCallback (this); mInputManager->setKeyboardEventCallback (this); mInputManager->setWindowEventCallback(this); mInputManager->setControllerEventCallback(this); @@ -145,15 +137,8 @@ namespace MWInput mSensorManager = new SensorManager(); mInputManager->setSensorEventCallback (mSensorManager); - float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); - if (uiScale != 0.f) - mInvUiScalingFactor = 1.f / uiScale; - - int w,h; - SDL_GetWindowSize(window, &w, &h); - - mGuiCursorX = mInvUiScalingFactor * w / 2.f; - mGuiCursorY = mInvUiScalingFactor * h / 2.f; + mMouseManager = new MouseManager(mInputBinder, mInputManager, window); + mInputManager->setMouseEventCallback (mMouseManager); } void InputManager::clear() @@ -163,6 +148,7 @@ namespace MWInput it->second = true; mSensorManager->clear(); + mMouseManager->clear(); } InputManager::~InputManager() @@ -529,9 +515,9 @@ namespace MWInput //we switched to non-relative mode, move our cursor to where the in-game //cursor is - if( !is_relative && was_relative != is_relative ) + if(!is_relative && was_relative != is_relative) { - mInputManager->warpMouse(static_cast(mGuiCursorX/mInvUiScalingFactor), static_cast(mGuiCursorY/mInvUiScalingFactor)); + mMouseManager->warpMouse(); } } @@ -571,50 +557,26 @@ namespace MWInput float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue()*2.0f-1.0f; float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue()*2.0f-1.0f; float zAxis = mInputBinder->getChannel(A_LookUpDown)->getValue()*2.0f-1.0f; - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); xAxis *= (1.5f - mInputBinder->getChannel(A_Use)->getValue()); yAxis *= (1.5f - mInputBinder->getChannel(A_Use)->getValue()); // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor - float xmove = xAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; - float ymove = yAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; - if (xmove != 0|| ymove != 0 || zAxis != 0) + float xMove = xAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; + float yMove = yAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; + if (xMove != 0|| yMove != 0 || zAxis != 0) { - mGuiCursorX += xmove; - mGuiCursorY += ymove; - mMouseWheel -= static_cast(zAxis * dt * 1500.0f); - - mGuiCursorX = std::max(0.f, std::min(mGuiCursorX, float(viewSize.width-1))); - mGuiCursorY = std::max(0.f, std::min(mGuiCursorY, float(viewSize.height-1))); + int mouseWheelMove = static_cast(-zAxis * dt * 1500.0f); - MyGUI::InputManager::getInstance().injectMouseMove(static_cast(mGuiCursorX), static_cast(mGuiCursorY), mMouseWheel); - mInputManager->warpMouse(static_cast(mGuiCursorX/mInvUiScalingFactor), static_cast(mGuiCursorY/mInvUiScalingFactor)); + mMouseManager->injectMouseMove(xMove, yMove, mouseWheelMove); + mMouseManager->warpMouse(); MWBase::Environment::get().getWindowManager()->setCursorActive(true); } } - if (mMouseLookEnabled) - { - float xAxis = mInputBinder->getChannel(A_LookLeftRight)->getValue()*2.0f-1.0f; - float yAxis = mInputBinder->getChannel(A_LookUpDown)->getValue()*2.0f-1.0f; - if (xAxis != 0 || yAxis != 0) - { - resetIdleTime(); - - float rot[3]; - rot[0] = yAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; - rot[1] = 0.0f; - rot[2] = xAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertX ? -1 : 1); - // Only actually turn player when we're not in vanity mode - if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && mControlSwitch["playerlooking"]) - { - mPlayer->yaw(rot[2]); - mPlayer->pitch(rot[0]); - } - } - } + if (mMouseManager->update(dt, disableControls)) + resetIdleTime(); if (mSensorManager->update(dt, mGuiCursorEnabled, mControlSwitch["playerlooking"])) resetIdleTime(); @@ -788,10 +750,16 @@ namespace MWInput mDragDrop = dragDrop; } + void InputManager::setGamepadGuiCursorEnabled(bool enabled) + { + mGamepadGuiCursorEnabled = enabled; + } + void InputManager::changeInputMode(bool guiMode) { mGuiCursorEnabled = guiMode; - mMouseLookEnabled = !guiMode; + mMouseManager->setGuiCursorEnabled(guiMode); + mMouseManager->setMouseLookEnabled(!guiMode); if (guiMode) MWBase::Environment::get().getWindowManager()->showCrosshair(false); MWBase::Environment::get().getWindowManager()->setCursorVisible(guiMode && (!mJoystickLastUsed || mGamepadGuiCursorEnabled)); @@ -811,9 +779,6 @@ namespace MWInput if (it->first == "Input" && it->second == "invert y axis") mInvertY = Settings::Manager::getBool("invert y axis", "Input"); - if (it->first == "Input" && it->second == "camera sensitivity") - mCameraSensitivity = Settings::Manager::getFloat("camera sensitivity", "Input"); - if (it->first == "Input" && it->second == "grab cursor") mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); @@ -916,110 +881,6 @@ namespace MWInput mInputBinder->keyReleased (arg); } - void InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) - { - mJoystickLastUsed = false; - bool guiMode = false; - - if (id == SDL_BUTTON_LEFT || id == SDL_BUTTON_RIGHT) // MyGUI only uses these mouse events - { - guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); - guiMode = MyGUI::InputManager::getInstance().injectMousePress(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode; - if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) - { - MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType(false); - if (b && b->getEnabled() && id == SDL_BUTTON_LEFT) - { - MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); - } - } - MWBase::Environment::get().getWindowManager()->setCursorActive(true); - } - - setPlayerControlsEnabled(!guiMode); - - // Don't trigger any mouse bindings while in settings menu, otherwise rebinding controls becomes impossible - if (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings) - mInputBinder->mousePressed (arg, id); - } - - void InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) - { - mJoystickLastUsed = false; - - if(mInputBinder->detectingBindingState()) - { - mInputBinder->mouseReleased (arg, id); - } else { - bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); - guiMode = MyGUI::InputManager::getInstance().injectMouseRelease(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode; - - if(mInputBinder->detectingBindingState()) return; // don't allow same mouseup to bind as initiated bind - - setPlayerControlsEnabled(!guiMode); - mInputBinder->mouseReleased (arg, id); - } - } - - void InputManager::mouseWheelMoved(const SDL_MouseWheelEvent &arg) - { - if (mInputBinder->detectingBindingState() || !mControlsDisabled) - mInputBinder->mouseWheelMoved(arg); - - mJoystickLastUsed = false; - } - - void InputManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg ) - { - mInputBinder->mouseMoved (arg); - - mJoystickLastUsed = false; - resetIdleTime (); - - if (mGuiCursorEnabled) - { - if (!mGamepadGuiCursorEnabled) - mGamepadGuiCursorEnabled = true; - // We keep track of our own mouse position, so that moving the mouse while in - // game mode does not move the position of the GUI cursor - mGuiCursorX = static_cast(arg.x) * mInvUiScalingFactor; - mGuiCursorY = static_cast(arg.y) * mInvUiScalingFactor; - - mMouseWheel = int(arg.z); - - MyGUI::InputManager::getInstance().injectMouseMove( int(mGuiCursorX), int(mGuiCursorY), mMouseWheel); - // FIXME: inject twice to force updating focused widget states (tooltips) resulting from changing the viewport by scroll wheel - MyGUI::InputManager::getInstance().injectMouseMove( int(mGuiCursorX), int(mGuiCursorY), mMouseWheel); - - MWBase::Environment::get().getWindowManager()->setCursorActive(true); - } - - if (mMouseLookEnabled && !mControlsDisabled) - { - resetIdleTime(); - - float x = arg.xrel * mCameraSensitivity * (1.0f/256.f) * (mInvertX ? -1 : 1); - float y = arg.yrel * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; - - float rot[3]; - rot[0] = -y; - rot[1] = 0.0f; - rot[2] = -x; - - // Only actually turn player when we're not in vanity mode - if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && mControlSwitch["playerlooking"]) - { - mPlayer->yaw(x); - mPlayer->pitch(y); - } - - if (arg.zrel && mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"]) //Check to make sure you are allowed to zoomout and there is a change - { - MWBase::Environment::get().getWorld()->changeVanityModeScale(static_cast(arg.zrel)); - } - } - } - void InputManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg ) { if (!mJoystickEnabled || mInputBinder->detectingBindingState()) @@ -1035,7 +896,7 @@ namespace MWInput // Temporary mouse binding until keyboard controls are available: if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click. { - bool mousePressSuccess = MyGUI::InputManager::getInstance().injectMousePress(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(SDL_BUTTON_LEFT)); + bool mousePressSuccess = mMouseManager->injectMouseButtonPress(SDL_BUTTON_LEFT); if (MyGUI::InputManager::getInstance().getMouseFocusWidget()) { MyGUI::Button* b = MyGUI::InputManager::getInstance().getMouseFocusWidget()->castType(false); @@ -1076,7 +937,7 @@ namespace MWInput // Temporary mouse binding until keyboard controls are available: if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click. { - bool mousePressSuccess = MyGUI::InputManager::getInstance().injectMouseRelease(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(SDL_BUTTON_LEFT)); + bool mousePressSuccess = mMouseManager->injectMouseButtonRelease(SDL_BUTTON_LEFT); if (mInputBinder->detectingBindingState()) // If the player just triggered binding, don't let button release bind. return; diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 1e5c89cda..f73aa7f3d 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -21,6 +21,7 @@ namespace MWInput { + class MouseManager; class SensorManager; } @@ -68,7 +69,6 @@ namespace MWInput class InputManager : public MWBase::InputManager, public SDLUtil::KeyListener, - public SDLUtil::MouseListener, public SDLUtil::WindowListener, public SDLUtil::ControllerListener, public ICS::ChannelListener, @@ -100,6 +100,7 @@ namespace MWInput virtual void processChangedSettings(const Settings::CategorySettingVector& changed); virtual void setDragDrop(bool dragDrop); + virtual void setGamepadGuiCursorEnabled(bool enabled); virtual void toggleControlSwitch (const std::string& sw, bool value); virtual bool getControlSwitch (const std::string& sw); @@ -114,18 +115,13 @@ namespace MWInput virtual void resetToDefaultKeyBindings(); virtual void resetToDefaultControllerBindings(); + virtual void setJoystickLastUsed(bool enabled) { mJoystickLastUsed = enabled; } virtual bool joystickLastUsed() {return mJoystickLastUsed;} virtual void keyPressed(const SDL_KeyboardEvent &arg ); virtual void keyReleased( const SDL_KeyboardEvent &arg ); virtual void textInput (const SDL_TextInputEvent &arg); - virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ); - virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); - virtual void mouseMoved( const SDLUtil::MouseMotionEvent &arg ); - - virtual void mouseWheelMoved( const SDL_MouseWheelEvent &arg); - virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg); virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg); virtual void axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg); @@ -164,6 +160,10 @@ namespace MWInput virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress); virtual void readRecord(ESM::ESMReader& reader, uint32_t type); + virtual void setPlayerControlsEnabled(bool enabled); + + virtual void resetIdleTime(); + private: SDL_Window* mWindow; bool mWindowVisible; @@ -191,12 +191,9 @@ namespace MWInput bool mControlsDisabled; bool mJoystickEnabled; - float mCameraSensitivity; - float mCameraYMultiplier; float mPreviewPOVDelay; float mTimeIdle; - bool mMouseLookEnabled; bool mGuiCursorEnabled; bool mGamepadGuiCursorEnabled; @@ -204,9 +201,6 @@ namespace MWInput float mOverencumberedMessageDelay; - float mGuiCursorX; - float mGuiCursorY; - int mMouseWheel; float mGamepadZoom; bool mUserFileExists; bool mAlwaysRunActive; @@ -221,14 +215,13 @@ namespace MWInput float mInvUiScalingFactor; float mGamepadCursorSpeed; + MouseManager* mMouseManager; SensorManager* mSensorManager; void convertMousePosForMyGUI(int& x, int& y); - void resetIdleTime(); void updateIdleTime(float dt); - void setPlayerControlsEnabled(bool enabled); void handleGuiArrowKey(int action); // Return true if GUI consumes input. bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg); diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp new file mode 100644 index 000000000..b34124773 --- /dev/null +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -0,0 +1,231 @@ +#include "mousemanager.hpp" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" + +#include "actions.hpp" +#include "sdlmappings.hpp" + +namespace MWInput +{ + MouseManager::MouseManager(ICS::InputControlSystem* inputBinder, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window) + : mInvertX(Settings::Manager::getBool("invert x axis", "Input")) + , mInvertY(Settings::Manager::getBool("invert y axis", "Input")) + , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) + , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) + , mInputBinder(inputBinder) + , mInputWrapper(inputWrapper) + , mInvUiScalingFactor(1.f) + , mGuiCursorX(0) + , mGuiCursorY(0) + , mMouseWheel(0) + , mMouseLookEnabled(false) + , mControlsDisabled(false) + , mGuiCursorEnabled(true) + { + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + if (uiScale != 0.f) + mInvUiScalingFactor = 1.f / uiScale; + + int w,h; + SDL_GetWindowSize(window, &w, &h); + + mGuiCursorX = mInvUiScalingFactor * w / 2.f; + mGuiCursorY = mInvUiScalingFactor * h / 2.f; + } + + void MouseManager::clear() + { + } + + void MouseManager::processChangedSettings(const Settings::CategorySettingVector& changed) + { + for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) + { + if (it->first == "Input" && it->second == "invert x axis") + mInvertX = Settings::Manager::getBool("invert x axis", "Input"); + + if (it->first == "Input" && it->second == "invert y axis") + mInvertY = Settings::Manager::getBool("invert y axis", "Input"); + + if (it->first == "Input" && it->second == "camera sensitivity") + mCameraSensitivity = Settings::Manager::getFloat("camera sensitivity", "Input"); + } + } + + void MouseManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg) + { + mInputBinder->mouseMoved (arg); + + MWBase::InputManager* input = MWBase::Environment::get().getInputManager(); + input->setJoystickLastUsed(false); + input->resetIdleTime(); + + if (mGuiCursorEnabled) + { + input->setGamepadGuiCursorEnabled(true); + + // We keep track of our own mouse position, so that moving the mouse while in + // game mode does not move the position of the GUI cursor + mGuiCursorX = static_cast(arg.x) * mInvUiScalingFactor; + mGuiCursorY = static_cast(arg.y) * mInvUiScalingFactor; + + mMouseWheel = static_cast(arg.z); + + MyGUI::InputManager::getInstance().injectMouseMove(static_cast(mGuiCursorX), static_cast(mGuiCursorY), mMouseWheel); + // FIXME: inject twice to force updating focused widget states (tooltips) resulting from changing the viewport by scroll wheel + MyGUI::InputManager::getInstance().injectMouseMove(static_cast(mGuiCursorX), static_cast(mGuiCursorY), mMouseWheel); + + MWBase::Environment::get().getWindowManager()->setCursorActive(true); + } + + if (mMouseLookEnabled && !mControlsDisabled) + { + float x = arg.xrel * mCameraSensitivity * (1.0f/256.f) * (mInvertX ? -1 : 1); + float y = arg.yrel * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; + + float rot[3]; + rot[0] = -y; + rot[1] = 0.0f; + rot[2] = -x; + + // Only actually turn player when we're not in vanity mode + if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && input->getControlSwitch("playerlooking")) + { + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + player.yaw(x); + player.pitch(y); + } + + if (arg.zrel && input->getControlSwitch("playerviewswitch") && input->getControlSwitch("playercontrols")) //Check to make sure you are allowed to zoomout and there is a change + { + MWBase::Environment::get().getWorld()->changeVanityModeScale(static_cast(arg.zrel)); + } + } + } + + void MouseManager::mouseReleased(const SDL_MouseButtonEvent &arg, Uint8 id) + { + MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); + + if(mInputBinder->detectingBindingState()) + { + mInputBinder->mouseReleased (arg, id); + } + else + { + bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); + guiMode = MyGUI::InputManager::getInstance().injectMouseRelease(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode; + + if(mInputBinder->detectingBindingState()) return; // don't allow same mouseup to bind as initiated bind + + MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!guiMode); + mInputBinder->mouseReleased (arg, id); + } + } + + void MouseManager::mouseWheelMoved(const SDL_MouseWheelEvent &arg) + { + if (mInputBinder->detectingBindingState() || !mControlsDisabled) + mInputBinder->mouseWheelMoved(arg); + + MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); + } + + void MouseManager::mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id) + { + MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); + bool guiMode = false; + + if (id == SDL_BUTTON_LEFT || id == SDL_BUTTON_RIGHT) // MyGUI only uses these mouse events + { + guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); + guiMode = MyGUI::InputManager::getInstance().injectMousePress(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode; + if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) + { + MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType(false); + if (b && b->getEnabled() && id == SDL_BUTTON_LEFT) + { + MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); + } + } + MWBase::Environment::get().getWindowManager()->setCursorActive(true); + } + + MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!guiMode); + + // Don't trigger any mouse bindings while in settings menu, otherwise rebinding controls becomes impossible + if (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings) + mInputBinder->mousePressed (arg, id); + } + + bool MouseManager::update(float dt, bool disableControls) + { + mControlsDisabled = disableControls; + + if (!mMouseLookEnabled) + return false; + + float xAxis = mInputBinder->getChannel(A_LookLeftRight)->getValue()*2.0f-1.0f; + float yAxis = mInputBinder->getChannel(A_LookUpDown)->getValue()*2.0f-1.0f; + if (xAxis == 0 && yAxis == 0) + return false; + + float rot[3]; + rot[0] = yAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; + rot[1] = 0.0f; + rot[2] = xAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertX ? -1 : 1); + + // Only actually turn player when we're not in vanity mode + if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + { + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + player.yaw(rot[2]); + player.pitch(rot[0]); + } + + return true; + } + + bool MouseManager::injectMouseButtonPress(Uint8 button) + { + return MyGUI::InputManager::getInstance().injectMousePress(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(button)); + } + + bool MouseManager::injectMouseButtonRelease(Uint8 button) + { + return MyGUI::InputManager::getInstance().injectMousePress(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(button)); + } + + void MouseManager::injectMouseMove(int xMove, int yMove, int mouseWheelMove) + { + mGuiCursorX += xMove; + mGuiCursorY += yMove; + mMouseWheel += mouseWheelMove; + + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + mGuiCursorX = std::max(0.f, std::min(mGuiCursorX, float(viewSize.width-1))); + mGuiCursorY = std::max(0.f, std::min(mGuiCursorY, float(viewSize.height-1))); + + MyGUI::InputManager::getInstance().injectMouseMove(static_cast(mGuiCursorX), static_cast(mGuiCursorY), mMouseWheel); + } + + void MouseManager::warpMouse() + { + mInputWrapper->warpMouse(static_cast(mGuiCursorX/mInvUiScalingFactor), static_cast(mGuiCursorY/mInvUiScalingFactor)); + } +} diff --git a/apps/openmw/mwinput/mousemanager.hpp b/apps/openmw/mwinput/mousemanager.hpp new file mode 100644 index 000000000..c0a9408c6 --- /dev/null +++ b/apps/openmw/mwinput/mousemanager.hpp @@ -0,0 +1,70 @@ +#ifndef MWINPUT_MWMOUSEMANAGER_H +#define MWINPUT_MWMOUSEMANAGER_H + +#include + +#include +#include + +namespace SDLUtil +{ + class InputWrapper; +} + +namespace MWWorld +{ + class Player; +} + +namespace ICS +{ + class InputControlSystem; +} + +namespace MWInput +{ + class MouseManager : public SDLUtil::MouseListener + { + public: + MouseManager(ICS::InputControlSystem* inputBinder, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window); + + virtual ~MouseManager() = default; + + void clear(); + + bool update(float dt, bool disableControls); + + virtual void mouseMoved(const SDLUtil::MouseMotionEvent &arg); + virtual void mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id); + virtual void mouseReleased(const SDL_MouseButtonEvent &arg, Uint8 id); + virtual void mouseWheelMoved(const SDL_MouseWheelEvent &arg); + + void processChangedSettings(const Settings::CategorySettingVector& changed); + + bool injectMouseButtonPress(Uint8 button); + bool injectMouseButtonRelease(Uint8 button); + void injectMouseMove(int xMove, int yMove, int mouseWheelMove); + void warpMouse(); + + void setMouseLookEnabled(bool enabled) { mMouseLookEnabled = enabled; } + void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; } + + private: + bool mInvertX; + bool mInvertY; + float mCameraSensitivity; + float mCameraYMultiplier; + + ICS::InputControlSystem* mInputBinder; + SDLUtil::InputWrapper* mInputWrapper; + float mInvUiScalingFactor; + + float mGuiCursorX; + float mGuiCursorY; + int mMouseWheel; + bool mMouseLookEnabled; + bool mControlsDisabled; + bool mGuiCursorEnabled; + }; +} +#endif From ce40294124964c1298858d52a7a87659d83f42e7 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 8 Apr 2020 19:33:07 +0400 Subject: [PATCH 048/227] Move input actions handling to the separate file --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwinput/actionmanager.cpp | 484 ++++++++++++++++++++++++ apps/openmw/mwinput/actionmanager.hpp | 68 ++++ apps/openmw/mwinput/inputmanagerimp.cpp | 459 +--------------------- apps/openmw/mwinput/inputmanagerimp.hpp | 36 +- 5 files changed, 572 insertions(+), 477 deletions(-) create mode 100644 apps/openmw/mwinput/actionmanager.cpp create mode 100644 apps/openmw/mwinput/actionmanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b5ea4fabf..255bbbec1 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,7 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - actions inputmanagerimp mousemanager sdlmappings sensormanager + actions actionmanager inputmanagerimp mousemanager sdlmappings sensormanager ) add_openmw_dir (mwgui diff --git a/apps/openmw/mwinput/actionmanager.cpp b/apps/openmw/mwinput/actionmanager.cpp new file mode 100644 index 000000000..22c7d96a6 --- /dev/null +++ b/apps/openmw/mwinput/actionmanager.cpp @@ -0,0 +1,484 @@ +#include "actionmanager.hpp" + +#include + +#include + +#include + +#include + +#include "../mwbase/inputmanager.hpp" +#include "../mwbase/statemanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwworld/esmstore.hpp" + +#include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/actorutil.hpp" + +#include "actions.hpp" + +namespace MWInput +{ + const float ZOOM_SCALE = 120.f; /// Used for scrolling camera in and out + const int fakeDeviceID = 1; + + ActionManager::ActionManager(ICS::InputControlSystem* inputBinder, + osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation, + osg::ref_ptr viewer, + osg::ref_ptr screenCaptureHandler) + : mInputBinder(inputBinder) + , mViewer(viewer) + , mScreenCaptureHandler(screenCaptureHandler) + , mScreenCaptureOperation(screenCaptureOperation) + , mAlwaysRunActive(Settings::Manager::getBool("always run", "Input")) + , mSneaking(false) + { + } + + void ActionManager::executeAction(int action) + { + // trigger action activated + switch (action) + { + case A_GameMenu: + toggleMainMenu (); + break; + case A_Screenshot: + screenshot(); + break; + case A_Inventory: + toggleInventory (); + break; + case A_Console: + toggleConsole (); + break; + case A_Activate: + MWBase::Environment::get().getInputManager()->resetIdleTime(); + activate(); + break; + case A_MoveLeft: + case A_MoveRight: + case A_MoveForward: + case A_MoveBackward: + handleGuiArrowKey(action); + break; + case A_Journal: + toggleJournal (); + break; + case A_AutoMove: + toggleAutoMove (); + break; + case A_AlwaysRun: + toggleWalking (); + break; + case A_ToggleWeapon: + toggleWeapon (); + break; + case A_Rest: + rest(); + break; + case A_ToggleSpell: + toggleSpell (); + break; + case A_QuickKey1: + quickKey(1); + break; + case A_QuickKey2: + quickKey(2); + break; + case A_QuickKey3: + quickKey(3); + break; + case A_QuickKey4: + quickKey(4); + break; + case A_QuickKey5: + quickKey(5); + break; + case A_QuickKey6: + quickKey(6); + break; + case A_QuickKey7: + quickKey(7); + break; + case A_QuickKey8: + quickKey(8); + break; + case A_QuickKey9: + quickKey(9); + break; + case A_QuickKey10: + quickKey(10); + break; + case A_QuickKeysMenu: + showQuickKeysMenu(); + break; + case A_ToggleHUD: + MWBase::Environment::get().getWindowManager()->toggleHud(); + break; + case A_ToggleDebug: + MWBase::Environment::get().getWindowManager()->toggleDebugWindow(); + break; + case A_ZoomIn: + if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch") && MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols") && !MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWorld()->setCameraDistance(ZOOM_SCALE, true, true); + break; + case A_ZoomOut: + if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch") && MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols") && !MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWorld()->setCameraDistance(-ZOOM_SCALE, true, true); + break; + case A_QuickSave: + quickSave(); + break; + case A_QuickLoad: + quickLoad(); + break; + case A_CycleSpellLeft: + if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic)) + MWBase::Environment::get().getWindowManager()->cycleSpell(false); + break; + case A_CycleSpellRight: + if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic)) + MWBase::Environment::get().getWindowManager()->cycleSpell(true); + break; + case A_CycleWeaponLeft: + if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + MWBase::Environment::get().getWindowManager()->cycleWeapon(false); + break; + case A_CycleWeaponRight: + if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + MWBase::Environment::get().getWindowManager()->cycleWeapon(true); + break; + case A_Sneak: + static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); + if (isToggleSneak) + { + toggleSneaking(); + } + break; + } + } + + bool isLeftOrRightButton(int action, ICS::InputControlSystem* ics, int deviceId, bool joystick) + { + int mouseBinding = ics->getMouseButtonBinding(ics->getControl(action), ICS::Control::INCREASE); + if (mouseBinding != ICS_MAX_DEVICE_BUTTONS) + return true; + int buttonBinding = ics->getJoystickButtonBinding(ics->getControl(action), deviceId, ICS::Control::INCREASE); + if (joystick && (buttonBinding == 0 || buttonBinding == 1)) + return true; + return false; + } + + bool ActionManager::checkAllowedToUseItems() const + { + MWWorld::Ptr player = MWMechanics::getPlayer(); + if (player.getClass().getNpcStats(player).isWerewolf()) + { + // Cannot use items or spells while in werewolf form + MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); + return false; + } + return true; + } + + void ActionManager::screenshot() + { + bool regularScreenshot = true; + + std::string settingStr; + + settingStr = Settings::Manager::getString("screenshot type","Video"); + regularScreenshot = settingStr.size() == 0 || settingStr.compare("regular") == 0; + + if (regularScreenshot) + { + mScreenCaptureHandler->setFramesToCapture(1); + mScreenCaptureHandler->captureNextFrame(*mViewer); + } + else + { + osg::ref_ptr screenshot (new osg::Image); + + if (MWBase::Environment::get().getWorld()->screenshot360(screenshot.get(),settingStr)) + { + (*mScreenCaptureOperation) (*(screenshot.get()),0); + // FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason + } + } + } + + void ActionManager::toggleMainMenu() + { + if (MyGUI::InputManager::getInstance().isModalAny()) + { + MWBase::Environment::get().getWindowManager()->exitCurrentModal(); + return; + } + + if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) + { + MWBase::Environment::get().getWindowManager()->toggleConsole(); + return; + } + + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu + { + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + else //Close current GUI + { + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); + } + } + + void ActionManager::toggleSpell() + { + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; + + // Not allowed before the magic window is accessible + if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playermagic") || !MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + return; + + if (!checkAllowedToUseItems()) + return; + + // Not allowed if no spell selected + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + MWWorld::InventoryStore& inventory = player.getPlayer().getClass().getInventoryStore(player.getPlayer()); + if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty() && + inventory.getSelectedEnchantItem() == inventory.end()) + return; + + if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player.getPlayer())) + return; + + MWMechanics::DrawState_ state = player.getDrawState(); + if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) + player.setDrawState(MWMechanics::DrawState_Spell); + else + player.setDrawState(MWMechanics::DrawState_Nothing); + } + + void ActionManager::quickLoad() + { + if (!MyGUI::InputManager::getInstance().isModalAny()) + MWBase::Environment::get().getStateManager()->quickLoad(); + } + + void ActionManager::quickSave() + { + if (!MyGUI::InputManager::getInstance().isModalAny()) + MWBase::Environment::get().getStateManager()->quickSave(); + } + + void ActionManager::toggleWeapon() + { + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; + + // Not allowed before the inventory window is accessible + if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playerfighting") || !MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + return; + + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + // We want to interrupt animation only if attack is preparing, but still is not triggered + // Otherwise we will get a "speedshooting" exploit, when player can skip reload animation by hitting "Toggle Weapon" key twice + if (MWBase::Environment::get().getMechanicsManager()->isAttackPreparing(player.getPlayer())) + player.setAttackingOrSpell(false); + else if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player.getPlayer())) + return; + + MWMechanics::DrawState_ state = player.getDrawState(); + if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) + player.setDrawState(MWMechanics::DrawState_Weapon); + else + player.setDrawState(MWMechanics::DrawState_Nothing); + } + + void ActionManager::rest() + { + if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + return; + + if (!MWBase::Environment::get().getWindowManager()->getRestEnabled () || MWBase::Environment::get().getWindowManager()->isGuiMode ()) + return; + + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_Rest); //Open rest GUI + } + + void ActionManager::toggleInventory() + { + if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + return; + + if (MyGUI::InputManager::getInstance().isModalAny()) + return; + + if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) + return; + + // Toggle between game mode and inventory mode + if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Inventory); + else + { + MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); + if(mode == MWGui::GM_Inventory || mode == MWGui::GM_Container) + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + + // .. but don't touch any other mode, except container. + } + + void ActionManager::toggleConsole() + { + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; + + MWBase::Environment::get().getWindowManager()->toggleConsole(); + } + + void ActionManager::toggleJournal() + { + if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + return; + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; + + if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal + && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu + && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings + && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); + } + else if(MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Journal)) + { + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Journal); + } + } + + void ActionManager::quickKey (int index) + { + if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols") || !MWBase::Environment::get().getInputManager()->getControlSwitch("playerfighting") || !MWBase::Environment::get().getInputManager()->getControlSwitch("playermagic")) + return; + if (!checkAllowedToUseItems()) + return; + + if (MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")!=-1) + return; + + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWindowManager()->activateQuickKey (index); + } + + void ActionManager::showQuickKeysMenu() + { + if (!MWBase::Environment::get().getWindowManager()->isGuiMode () + && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1) + { + if (!checkAllowedToUseItems()) + return; + + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu); + } + else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) + { + while(MyGUI::InputManager::getInstance().isModalAny()) + { //Handle any open Modal windows + MWBase::Environment::get().getWindowManager()->exitCurrentModal(); + } + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); //And handle the actual main window + } + } + + void ActionManager::activate() + { + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed(); + if (!SDL_IsTextInputActive() && !isLeftOrRightButton(A_Activate, mInputBinder, fakeDeviceID, joystickUsed)) + MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0, false); + } + else if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + { + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + player.activate(); + } + } + + void ActionManager::toggleAutoMove() + { + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; + + if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + { + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + player.setAutoMove (!player.getAutoMove()); + } + } + + void ActionManager::toggleWalking() + { + if (MWBase::Environment::get().getWindowManager()->isGuiMode() || SDL_IsTextInputActive()) return; + mAlwaysRunActive = !mAlwaysRunActive; + + Settings::Manager::setBool("always run", "Input", mAlwaysRunActive); + } + + void ActionManager::toggleSneaking() + { + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; + if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) return; + mSneaking = !mSneaking; + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + player.setSneak(mSneaking); + } + + void ActionManager::clear() + { + } + + void ActionManager::handleGuiArrowKey(int action) + { + bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed(); + // This is currently keyboard-specific code + // TODO: see if GUI controls can be refactored into a single function + if (joystickUsed) + return; + + if (SDL_IsTextInputActive()) + return; + + if (isLeftOrRightButton(action, mInputBinder, fakeDeviceID, joystickUsed)) + return; + + MyGUI::KeyCode key; + switch (action) + { + case A_MoveLeft: + key = MyGUI::KeyCode::ArrowLeft; + break; + case A_MoveRight: + key = MyGUI::KeyCode::ArrowRight; + break; + case A_MoveForward: + key = MyGUI::KeyCode::ArrowUp; + break; + case A_MoveBackward: + default: + key = MyGUI::KeyCode::ArrowDown; + break; + } + + MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); + } +} diff --git a/apps/openmw/mwinput/actionmanager.hpp b/apps/openmw/mwinput/actionmanager.hpp new file mode 100644 index 000000000..7c1fe3f70 --- /dev/null +++ b/apps/openmw/mwinput/actionmanager.hpp @@ -0,0 +1,68 @@ +#ifndef MWINPUT_ACTIONMANAGER_H +#define MWINPUT_ACTIONMANAGER_H + +#include +#include + +namespace ICS +{ + class InputControlSystem; +} + +namespace osgViewer +{ + class Viewer; + class ScreenCaptureHandler; +} + +namespace MWInput +{ + class ActionManager + { + public: + + ActionManager(ICS::InputControlSystem* inputBinder, + osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation, + osg::ref_ptr viewer, + osg::ref_ptr screenCaptureHandler); + + void clear(); + + void executeAction(int action); + + bool checkAllowedToUseItems() const; + + void toggleMainMenu(); + void toggleSpell(); + void toggleWeapon(); + void toggleInventory(); + void toggleConsole(); + void screenshot(); + void toggleJournal(); + void activate(); + void toggleWalking(); + void toggleSneaking(); + void toggleAutoMove(); + void rest(); + void quickLoad(); + void quickSave(); + + void quickKey (int index); + void showQuickKeysMenu(); + + bool isAlwaysRunActive() const { return mAlwaysRunActive; }; + bool isSneaking() const { return mSneaking; }; + + private: + void handleGuiArrowKey(int action); + + ICS::InputControlSystem* mInputBinder; + osg::ref_ptr mViewer; + osg::ref_ptr mScreenCaptureHandler; + osgViewer::ScreenCaptureHandler::CaptureOperation* mScreenCaptureOperation; + + bool mAlwaysRunActive; + bool mSneaking; + }; +} +#endif diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index e79ec145a..f332b03af 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -30,6 +30,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" +#include "actionmanager.hpp" #include "mousemanager.hpp" #include "sdlmappings.hpp" #include "sensormanager.hpp" @@ -41,14 +42,10 @@ namespace MWInput osg::ref_ptr viewer, osg::ref_ptr screenCaptureHandler, osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation, - const std::string& userFile, bool userFileExists, - const std::string& userControllerBindingsFile, + const std::string& userFile, bool userFileExists, const std::string& userControllerBindingsFile, const std::string& controllerBindingsFile, bool grab) : mWindow(window) , mWindowVisible(true) - , mViewer(viewer) - , mScreenCaptureHandler(screenCaptureHandler) - , mScreenCaptureOperation(screenCaptureOperation) , mJoystickLastUsed(false) , mPlayer(nullptr) , mInputManager(nullptr) @@ -56,8 +53,6 @@ namespace MWInput , mUserFile(userFile) , mDragDrop(false) , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) - , mInvertX (Settings::Manager::getBool("invert x axis", "Input")) - , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) , mControlsDisabled(false) , mJoystickEnabled (Settings::Manager::getBool("enable controller", "Input")) , mPreviewPOVDelay(0.f) @@ -67,12 +62,8 @@ namespace MWInput , mDetectingKeyboard(false) , mOverencumberedMessageDelay(0.f) , mGamepadZoom(0) - , mUserFileExists(userFileExists) - , mAlwaysRunActive(Settings::Manager::getBool("always run", "Input")) - , mSneakToggles(Settings::Manager::getBool("toggle sneak", "Input")) , mSneakToggleShortcutTimer(0.f) , mSneakGamepadShortcut(false) - , mSneaking(false) , mAttemptJump(false) , mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input")) , mFakeDeviceID(1) @@ -139,6 +130,8 @@ namespace MWInput mMouseManager = new MouseManager(mInputBinder, mInputManager, window); mInputManager->setMouseEventCallback (mMouseManager); + + mActionManager = new ActionManager(mInputBinder, screenCaptureOperation, viewer, screenCaptureHandler); } void InputManager::clear() @@ -147,6 +140,7 @@ namespace MWInput for (std::map::iterator it = mControlSwitch.begin(); it != mControlSwitch.end(); ++it) it->second = true; + mActionManager->clear(); mSensorManager->clear(); mMouseManager->clear(); } @@ -155,6 +149,8 @@ namespace MWInput { mInputBinder->save (mUserFile); + delete mActionManager; + delete mMouseManager; delete mSensorManager; delete mInputBinder; @@ -183,51 +179,6 @@ namespace MWInput } } - bool isLeftOrRightButton(int action, ICS::InputControlSystem* ics, int deviceId, bool joystick) - { - int mouseBinding = ics->getMouseButtonBinding(ics->getControl(action), ICS::Control::INCREASE); - if (mouseBinding != ICS_MAX_DEVICE_BUTTONS) - return true; - int buttonBinding = ics->getJoystickButtonBinding(ics->getControl(action), deviceId, ICS::Control::INCREASE); - if (joystick && (buttonBinding == 0 || buttonBinding == 1)) - return true; - return false; - } - - void InputManager::handleGuiArrowKey(int action) - { - // This is currently keyboard-specific code - // TODO: see if GUI controls can be refactored into a single function - if (mJoystickLastUsed) - return; - - if (SDL_IsTextInputActive()) - return; - - if (isLeftOrRightButton(action, mInputBinder, mFakeDeviceID, mJoystickLastUsed)) - return; - - MyGUI::KeyCode key; - switch (action) - { - case A_MoveLeft: - key = MyGUI::KeyCode::ArrowLeft; - break; - case A_MoveRight: - key = MyGUI::KeyCode::ArrowRight; - break; - case A_MoveForward: - key = MyGUI::KeyCode::ArrowUp; - break; - case A_MoveBackward: - default: - key = MyGUI::KeyCode::ArrowDown; - break; - } - - MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); - } - bool InputManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg) { // Presumption of GUI mode will be removed in the future. @@ -375,127 +326,7 @@ namespace MWInput } if (currentValue == 1) - { - // trigger action activated - switch (action) - { - case A_GameMenu: - toggleMainMenu (); - break; - case A_Screenshot: - screenshot(); - break; - case A_Inventory: - toggleInventory (); - break; - case A_Console: - toggleConsole (); - break; - case A_Activate: - resetIdleTime(); - activate(); - break; - case A_MoveLeft: - case A_MoveRight: - case A_MoveForward: - case A_MoveBackward: - handleGuiArrowKey(action); - break; - case A_Journal: - toggleJournal (); - break; - case A_AutoMove: - toggleAutoMove (); - break; - case A_AlwaysRun: - toggleWalking (); - break; - case A_ToggleWeapon: - toggleWeapon (); - break; - case A_Rest: - rest(); - break; - case A_ToggleSpell: - toggleSpell (); - break; - case A_QuickKey1: - quickKey(1); - break; - case A_QuickKey2: - quickKey(2); - break; - case A_QuickKey3: - quickKey(3); - break; - case A_QuickKey4: - quickKey(4); - break; - case A_QuickKey5: - quickKey(5); - break; - case A_QuickKey6: - quickKey(6); - break; - case A_QuickKey7: - quickKey(7); - break; - case A_QuickKey8: - quickKey(8); - break; - case A_QuickKey9: - quickKey(9); - break; - case A_QuickKey10: - quickKey(10); - break; - case A_QuickKeysMenu: - showQuickKeysMenu(); - break; - case A_ToggleHUD: - MWBase::Environment::get().getWindowManager()->toggleHud(); - break; - case A_ToggleDebug: - MWBase::Environment::get().getWindowManager()->toggleDebugWindow(); - break; - case A_ZoomIn: - if (mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"] && !MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->setCameraDistance(ZOOM_SCALE, true, true); - break; - case A_ZoomOut: - if (mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"] && !MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->setCameraDistance(-ZOOM_SCALE, true, true); - break; - case A_QuickSave: - quickSave(); - break; - case A_QuickLoad: - quickLoad(); - break; - case A_CycleSpellLeft: - if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic)) - MWBase::Environment::get().getWindowManager()->cycleSpell(false); - break; - case A_CycleSpellRight: - if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic)) - MWBase::Environment::get().getWindowManager()->cycleSpell(true); - break; - case A_CycleWeaponLeft: - if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) - MWBase::Environment::get().getWindowManager()->cycleWeapon(false); - break; - case A_CycleWeaponRight: - if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) - MWBase::Environment::get().getWindowManager()->cycleWeapon(true); - break; - case A_Sneak: - if (mSneakToggles) - { - toggleSneaking(); - } - break; - } - } + mActionManager->executeAction(action); } void InputManager::updateCursorMode() @@ -521,18 +352,6 @@ namespace MWInput } } - bool InputManager::checkAllowedToUseItems() const - { - MWWorld::Ptr player = MWMechanics::getPlayer(); - if (player.getClass().getNpcStats(player).isWerewolf()) - { - // Cannot use items or spells while in werewolf form - MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); - return false; - } - return true; - } - void InputManager::update(float dt, bool disableControls, bool disableEvents) { mControlsDisabled = disableControls; @@ -638,7 +457,8 @@ namespace MWInput mPlayer->setForwardBackward (1); } - if (!mSneakToggles) + static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); + if (!isToggleSneak) { if(mJoystickLastUsed) { @@ -649,20 +469,20 @@ namespace MWInput if(mSneakToggleShortcutTimer <= 0.3f) { mSneakGamepadShortcut = true; - toggleSneaking(); + mActionManager->toggleSneaking(); } else mSneakGamepadShortcut = false; } - if(!mSneaking) - toggleSneaking(); + if(!mActionManager->isSneaking()) + mActionManager->toggleSneaking(); mSneakToggleShortcutTimer = 0.f; } else { - if(!mSneakGamepadShortcut && mSneaking) - toggleSneaking(); + if(!mSneakGamepadShortcut && mActionManager->isSneaking()) + mActionManager->toggleSneaking(); if(mSneakToggleShortcutTimer <= 0.3f) mSneakToggleShortcutTimer += dt; } @@ -678,7 +498,7 @@ namespace MWInput mOverencumberedMessageDelay = 0.f; } - if ((mAlwaysRunActive && alwaysRunAllowed) || isRunning) + if ((mActionManager->isAlwaysRunActive() && alwaysRunAllowed) || isRunning) mPlayer->setRunState(!actionIsActive(A_Run)); else mPlayer->setRunState(actionIsActive(A_Run)); @@ -773,12 +593,6 @@ namespace MWInput for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) { - if (it->first == "Input" && it->second == "invert x axis") - mInvertX = Settings::Manager::getBool("invert x axis", "Input"); - - if (it->first == "Input" && it->second == "invert y axis") - mInvertY = Settings::Manager::getBool("invert y axis", "Input"); - if (it->first == "Input" && it->second == "grab cursor") mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); @@ -808,6 +622,7 @@ namespace MWInput Settings::Manager::getBool("window border", "Video")); } + mMouseManager->processChangedSettings(changed); mSensorManager->processChangedSettings(changed); } @@ -1022,246 +837,6 @@ namespace MWInput MWBase::Environment::get().getStateManager()->requestQuit(); } - void InputManager::toggleMainMenu() - { - if (MyGUI::InputManager::getInstance().isModalAny()) - { - MWBase::Environment::get().getWindowManager()->exitCurrentModal(); - return; - } - - if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) - { - MWBase::Environment::get().getWindowManager()->toggleConsole(); - return; - } - - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu - { - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - else //Close current GUI - { - MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); - } - } - - void InputManager::quickLoad() { - if (!MyGUI::InputManager::getInstance().isModalAny()) - MWBase::Environment::get().getStateManager()->quickLoad(); - } - - void InputManager::quickSave() { - if (!MyGUI::InputManager::getInstance().isModalAny()) - MWBase::Environment::get().getStateManager()->quickSave(); - } - void InputManager::toggleSpell() - { - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; - - // Not allowed before the magic window is accessible - if (!mControlSwitch["playermagic"] || !mControlSwitch["playercontrols"]) - return; - - if (!checkAllowedToUseItems()) - return; - - // Not allowed if no spell selected - MWWorld::InventoryStore& inventory = mPlayer->getPlayer().getClass().getInventoryStore(mPlayer->getPlayer()); - if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty() && - inventory.getSelectedEnchantItem() == inventory.end()) - return; - - if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer())) - return; - - MWMechanics::DrawState_ state = mPlayer->getDrawState(); - if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) - mPlayer->setDrawState(MWMechanics::DrawState_Spell); - else - mPlayer->setDrawState(MWMechanics::DrawState_Nothing); - } - - void InputManager::toggleWeapon() - { - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; - - // Not allowed before the inventory window is accessible - if (!mControlSwitch["playerfighting"] || !mControlSwitch["playercontrols"]) - return; - - // We want to interrupt animation only if attack is preparing, but still is not triggered - // Otherwise we will get a "speedshooting" exploit, when player can skip reload animation by hitting "Toggle Weapon" key twice - if (MWBase::Environment::get().getMechanicsManager()->isAttackPreparing(mPlayer->getPlayer())) - mPlayer->setAttackingOrSpell(false); - else if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer())) - return; - - MWMechanics::DrawState_ state = mPlayer->getDrawState(); - if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - mPlayer->setDrawState(MWMechanics::DrawState_Weapon); - else - mPlayer->setDrawState(MWMechanics::DrawState_Nothing); - } - - void InputManager::rest() - { - if (!mControlSwitch["playercontrols"]) - return; - - if (!MWBase::Environment::get().getWindowManager()->getRestEnabled () || MWBase::Environment::get().getWindowManager()->isGuiMode ()) - return; - - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_Rest); //Open rest GUI - - } - - void InputManager::screenshot() - { - bool regularScreenshot = true; - - std::string settingStr; - - settingStr = Settings::Manager::getString("screenshot type","Video"); - regularScreenshot = settingStr.size() == 0 || settingStr.compare("regular") == 0; - - if (regularScreenshot) - { - mScreenCaptureHandler->setFramesToCapture(1); - mScreenCaptureHandler->captureNextFrame(*mViewer); - } - else - { - osg::ref_ptr screenshot (new osg::Image); - - if (MWBase::Environment::get().getWorld()->screenshot360(screenshot.get(),settingStr)) - { - (*mScreenCaptureOperation) (*(screenshot.get()),0); - // FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason - } - } - } - - void InputManager::toggleInventory() - { - if (!mControlSwitch["playercontrols"]) - return; - - if (MyGUI::InputManager::getInstance ().isModalAny()) - return; - - if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) - return; - - // Toggle between game mode and inventory mode - if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Inventory); - else - { - MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); - if(mode == MWGui::GM_Inventory || mode == MWGui::GM_Container) - MWBase::Environment::get().getWindowManager()->popGuiMode(); - } - - // .. but don't touch any other mode, except container. - } - - void InputManager::toggleConsole() - { - if (MyGUI::InputManager::getInstance ().isModalAny()) - return; - - MWBase::Environment::get().getWindowManager()->toggleConsole(); - } - - void InputManager::toggleJournal() - { - if (!mControlSwitch["playercontrols"]) - return; - if (MyGUI::InputManager::getInstance ().isModalAny()) - return; - - if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal - && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu - && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings - && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) - { - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); - } - else if(MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Journal)) - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Journal); - } - } - - void InputManager::quickKey (int index) - { - if (!mControlSwitch["playercontrols"] || !mControlSwitch["playerfighting"] || !mControlSwitch["playermagic"]) - return; - if (!checkAllowedToUseItems()) - return; - - if (MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")!=-1) - return; - - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWindowManager()->activateQuickKey (index); - } - - void InputManager::showQuickKeysMenu() - { - if (!MWBase::Environment::get().getWindowManager()->isGuiMode () - && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1) - { - if (!checkAllowedToUseItems()) - return; - - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu); - - } - else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) { - while(MyGUI::InputManager::getInstance().isModalAny()) { //Handle any open Modal windows - MWBase::Environment::get().getWindowManager()->exitCurrentModal(); - } - MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); //And handle the actual main window - } - } - - void InputManager::activate() - { - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - if (!SDL_IsTextInputActive() && !isLeftOrRightButton(A_Activate, mInputBinder, mFakeDeviceID, mJoystickLastUsed)) - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0, false); - } - else if (mControlSwitch["playercontrols"]) - mPlayer->activate(); - } - - void InputManager::toggleAutoMove() - { - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; - - if (mControlSwitch["playercontrols"]) - mPlayer->setAutoMove (!mPlayer->getAutoMove()); - } - - void InputManager::toggleWalking() - { - if (MWBase::Environment::get().getWindowManager()->isGuiMode() || SDL_IsTextInputActive()) return; - mAlwaysRunActive = !mAlwaysRunActive; - - Settings::Manager::setBool("always run", "Input", mAlwaysRunActive); - } - - void InputManager::toggleSneaking() - { - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; - if (!mControlSwitch["playercontrols"]) return; - mSneaking = !mSneaking; - mPlayer->setSneak(mSneaking); - } - void InputManager::resetIdleTime() { if (mTimeIdle < 0) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index f73aa7f3d..761180f12 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -21,6 +21,7 @@ namespace MWInput { + class ActionManager; class MouseManager; class SensorManager; } @@ -51,18 +52,10 @@ namespace SDLUtil class VideoWrapper; } -namespace osgViewer -{ - class Viewer; - class ScreenCaptureHandler; -} - struct SDL_Window; namespace MWInput { - const float ZOOM_SCALE = 120.f; /// Used for scrolling camera in and out - /** * @brief Class that handles all input and key bindings for OpenMW. */ @@ -167,9 +160,6 @@ namespace MWInput private: SDL_Window* mWindow; bool mWindowVisible; - osg::ref_ptr mViewer; - osg::ref_ptr mScreenCaptureHandler; - osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation; bool mJoystickLastUsed; MWWorld::Player* mPlayer; @@ -185,9 +175,6 @@ namespace MWInput bool mGrabCursor; - bool mInvertX; - bool mInvertY; - bool mControlsDisabled; bool mJoystickEnabled; @@ -202,12 +189,9 @@ namespace MWInput float mOverencumberedMessageDelay; float mGamepadZoom; - bool mUserFileExists; - bool mAlwaysRunActive; bool mSneakToggles; float mSneakToggleShortcutTimer; bool mSneakGamepadShortcut; - bool mSneaking; bool mAttemptJump; std::map mControlSwitch; @@ -215,6 +199,7 @@ namespace MWInput float mInvUiScalingFactor; float mGamepadCursorSpeed; + ActionManager* mActionManager; MouseManager* mMouseManager; SensorManager* mSensorManager; @@ -229,23 +214,6 @@ namespace MWInput void updateCursorMode(); - bool checkAllowedToUseItems() const; - - void toggleMainMenu(); - void toggleSpell(); - void toggleWeapon(); - void toggleInventory(); - void toggleConsole(); - void screenshot(); - void toggleJournal(); - void activate(); - void toggleWalking(); - void toggleSneaking(); - void toggleAutoMove(); - void rest(); - void quickLoad(); - void quickSave(); - void quickKey (int index); void showQuickKeysMenu(); From f9d6137a29c0c4b396ba51f27aaaf2b9103b5806 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 8 Apr 2020 19:42:25 +0400 Subject: [PATCH 049/227] Do not store player reference in the InputManager --- apps/openmw/engine.cpp | 1 - apps/openmw/mwinput/inputmanagerimp.cpp | 72 +++++++++++++------------ apps/openmw/mwinput/inputmanagerimp.hpp | 2 - 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 7d6348789..efb3c01a3 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -561,7 +561,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName, mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string())); mEnvironment.getWorld()->setupPlayer(); - input->setPlayer(&mEnvironment.getWorld()->getPlayer()); window->setStore(mEnvironment.getWorld()->getStore()); window->initUI(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f332b03af..4376951b5 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -47,7 +47,6 @@ namespace MWInput : mWindow(window) , mWindowVisible(true) , mJoystickLastUsed(false) - , mPlayer(nullptr) , mInputManager(nullptr) , mVideoWrapper(nullptr) , mUserFile(userFile) @@ -308,8 +307,9 @@ namespace MWInput else { - MWMechanics::DrawState_ state = MWBase::Environment::get().getWorld()->getPlayer().getDrawState(); - mPlayer->setAttackingOrSpell(currentValue != 0 && state != MWMechanics::DrawState_Nothing); + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + MWMechanics::DrawState_ state = player.getDrawState(); + player.setAttackingOrSpell(currentValue != 0 && state != MWMechanics::DrawState_Nothing); } } else if (action == A_Jump) @@ -408,6 +408,8 @@ namespace MWInput // be done in the physics system. if (mControlSwitch["playercontrols"]) { + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + bool triedToMove = false; bool isRunning = false; bool alwaysRunAllowed = false; @@ -418,14 +420,14 @@ namespace MWInput if (xAxis != .5) { triedToMove = true; - mPlayer->setLeftRight((xAxis - 0.5f) * 2); + player.setLeftRight((xAxis - 0.5f) * 2); } if (yAxis != .5) { triedToMove = true; - mPlayer->setAutoMove (false); - mPlayer->setForwardBackward((yAxis - 0.5f) * 2 * -1); + player.setAutoMove (false); + player.setForwardBackward((yAxis - 0.5f) * 2 * -1); } if (triedToMove) @@ -439,22 +441,22 @@ namespace MWInput { alwaysRunAllowed = true; triedToMove = true; - mPlayer->setLeftRight (actionIsActive(A_MoveRight) ? 1 : -1); + player.setLeftRight (actionIsActive(A_MoveRight) ? 1 : -1); } if (actionIsActive(A_MoveForward) != actionIsActive(A_MoveBackward)) { alwaysRunAllowed = true; triedToMove = true; - mPlayer->setAutoMove (false); - mPlayer->setForwardBackward (actionIsActive(A_MoveForward) ? 1 : -1); + player.setAutoMove (false); + player.setForwardBackward (actionIsActive(A_MoveForward) ? 1 : -1); } - if (mPlayer->getAutoMove()) + if (player.getAutoMove()) { alwaysRunAllowed = true; triedToMove = true; - mPlayer->setForwardBackward (1); + player.setForwardBackward (1); } static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); @@ -488,29 +490,29 @@ namespace MWInput } } else - mPlayer->setSneak(actionIsActive(A_Sneak)); + player.setSneak(actionIsActive(A_Sneak)); } if (mAttemptJump && mControlSwitch["playerjumping"]) { - mPlayer->setUpDown (1); + player.setUpDown (1); triedToMove = true; mOverencumberedMessageDelay = 0.f; } if ((mActionManager->isAlwaysRunActive() && alwaysRunAllowed) || isRunning) - mPlayer->setRunState(!actionIsActive(A_Run)); + player.setRunState(!actionIsActive(A_Run)); else - mPlayer->setRunState(actionIsActive(A_Run)); + player.setRunState(actionIsActive(A_Run)); // if player tried to start moving, but can't (due to being overencumbered), display a notification. if (triedToMove) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); mOverencumberedMessageDelay -= dt; - if (player.getClass().getEncumbrance(player) > player.getClass().getCapacity(player)) + if (playerPtr.getClass().getEncumbrance(playerPtr) > playerPtr.getClass().getCapacity(playerPtr)) { - mPlayer->setAutoMove (false); + player.setAutoMove (false); if (mOverencumberedMessageDelay <= 0) { MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage59}"); @@ -633,19 +635,28 @@ namespace MWInput void InputManager::toggleControlSwitch (const std::string& sw, bool value) { + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + /// \note 7 switches at all, if-else is relevant - if (sw == "playercontrols" && !value) { - mPlayer->setLeftRight(0); - mPlayer->setForwardBackward(0); - mPlayer->setAutoMove(false); - mPlayer->setUpDown(0); - } else if (sw == "playerjumping" && !value) { + if (sw == "playercontrols" && !value) + { + player.setLeftRight(0); + player.setForwardBackward(0); + player.setAutoMove(false); + player.setUpDown(0); + } + else if (sw == "playerjumping" && !value) + { /// \fixme maybe crouching at this time - mPlayer->setUpDown(0); - } else if (sw == "vanitymode") { + player.setUpDown(0); + } + else if (sw == "vanitymode") + { MWBase::Environment::get().getWorld()->allowVanityMode(value); - } else if (sw == "playerlooking" && !value) { - MWBase::Environment::get().getWorld()->rotateObject(mPlayer->getPlayer(), 0.f, 0.f, 0.f); + } + else if (sw == "playerlooking" && !value) + { + MWBase::Environment::get().getWorld()->rotateObject(player.getPlayer(), 0.f, 0.f, 0.f); } mControlSwitch[sw] = value; } @@ -1387,9 +1398,4 @@ namespace MWInput { loadControllerDefaults(true); } - - void InputManager::setPlayer (MWWorld::Player* player) - { - mPlayer = player; - } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 761180f12..da7075e4e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -86,8 +86,6 @@ namespace MWInput virtual void update(float dt, bool disableControls=false, bool disableEvents=false); - void setPlayer (MWWorld::Player* player); - virtual void changeInputMode(bool guiMode); virtual void processChangedSettings(const Settings::CategorySettingVector& changed); From e353647d15c122e23eff2ecf55f6e62d5ba9eff8 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 8 Apr 2020 22:02:53 +0400 Subject: [PATCH 050/227] Move gamepads handling to the separate file --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwinput/controllermanager.cpp | 409 ++++++++++++++++++++++ apps/openmw/mwinput/controllermanager.hpp | 77 ++++ apps/openmw/mwinput/inputmanagerimp.cpp | 362 ++----------------- apps/openmw/mwinput/inputmanagerimp.hpp | 28 +- apps/openmw/mwinput/mousemanager.hpp | 2 - 6 files changed, 524 insertions(+), 356 deletions(-) create mode 100644 apps/openmw/mwinput/controllermanager.cpp create mode 100644 apps/openmw/mwinput/controllermanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 255bbbec1..405320881 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,7 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - actions actionmanager inputmanagerimp mousemanager sdlmappings sensormanager + actions actionmanager controllermanager inputmanagerimp mousemanager sdlmappings sensormanager ) add_openmw_dir (mwgui diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp new file mode 100644 index 000000000..ec687ed7b --- /dev/null +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -0,0 +1,409 @@ +#include "controllermanager.hpp" + +#include +#include +#include + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" +#include "../mwbase/statemanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" + +#include "actions.hpp" +#include "actionmanager.hpp" +#include "mousemanager.hpp" + +namespace MWInput +{ + static const int sFakeDeviceID = 1; + + ControllerManager::ControllerManager(ICS::InputControlSystem* inputBinder, + SDLUtil::InputWrapper* inputWrapper, + ActionManager* actionManager, + MouseManager* mouseManager, + const std::string& userControllerBindingsFile, + const std::string& controllerBindingsFile) + : mInputBinder(inputBinder) + , mInputWrapper(inputWrapper) + , mActionManager(actionManager) + , mMouseManager(mouseManager) + , mJoystickEnabled (Settings::Manager::getBool("enable controller", "Input")) + , mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input")) + , mInvUiScalingFactor(1.f) + , mSneakToggleShortcutTimer(0.f) + , mGamepadZoom(0) + , mGamepadGuiCursorEnabled(true) + , mControlsDisabled(false) + , mJoystickLastUsed(false) + , mSneakGamepadShortcut(false) + , mGamepadPreviewMode(false) + { + if(!controllerBindingsFile.empty()) + { + SDL_GameControllerAddMappingsFromFile(controllerBindingsFile.c_str()); + } + if(!userControllerBindingsFile.empty()) + { + SDL_GameControllerAddMappingsFromFile(userControllerBindingsFile.c_str()); + } + + // Open all presently connected sticks + int numSticks = SDL_NumJoysticks(); + for(int i = 0; i < numSticks; i++) + { + if(SDL_IsGameController(i)) + { + SDL_ControllerDeviceEvent evt; + evt.which = i; + controllerAdded(sFakeDeviceID, evt); + Log(Debug::Info) << "Detected game controller: " << SDL_GameControllerNameForIndex(i); + } + else + { + Log(Debug::Info) << "Detected unusable controller: " << SDL_JoystickNameForIndex(i); + } + } + + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + if (uiScale != 0.f) + mInvUiScalingFactor = 1.f / uiScale; + } + + void ControllerManager::clear() + { + } + + void ControllerManager::processChangedSettings(const Settings::CategorySettingVector& changed) + { + for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) + { + if (it->first == "Input" && it->second == "enable controller") + mJoystickEnabled = Settings::Manager::getBool("enable controller", "Input"); + } + } + + bool ControllerManager::actionIsActive (int id) + { + return (mInputBinder->getChannel (id)->getValue ()==1.0); + } + + void ControllerManager::update(float dt, bool disableControls, bool gamepadPreviewMode) + { + mControlsDisabled = disableControls; + mGamepadPreviewMode = gamepadPreviewMode; + + if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled)) + { + float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue()*2.0f-1.0f; + float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue()*2.0f-1.0f; + float zAxis = mInputBinder->getChannel(A_LookUpDown)->getValue()*2.0f-1.0f; + + xAxis *= (1.5f - mInputBinder->getChannel(A_Use)->getValue()); + yAxis *= (1.5f - mInputBinder->getChannel(A_Use)->getValue()); + + // We keep track of our own mouse position, so that moving the mouse while in + // game mode does not move the position of the GUI cursor + float xMove = xAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; + float yMove = yAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; + if (xMove != 0 || yMove != 0 || zAxis != 0) + { + int mouseWheelMove = static_cast(-zAxis * dt * 1500.0f); + + mMouseManager->injectMouseMove(xMove, yMove, mouseWheelMove); + mMouseManager->warpMouse(); + MWBase::Environment::get().getWindowManager()->setCursorActive(true); + } + } + + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + + // Disable movement in Gui mode + if (MWBase::Environment::get().getWindowManager()->isGuiMode() + || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running) + { + mGamepadZoom = 0; + return; + } + + // Configure player movement according to keyboard input. Actual movement will + // be done in the physics system. + if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + { + bool triedToMove = false; + + // joystick movement + float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue(); + float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue(); + if (xAxis != .5) + { + triedToMove = true; + player.setLeftRight((xAxis - 0.5f) * 2); + } + + if (yAxis != .5) + { + triedToMove = true; + player.setAutoMove (false); + player.setForwardBackward((yAxis - 0.5f) * 2 * -1); + } + + if (triedToMove) + mJoystickLastUsed = true; + + if(triedToMove) MWBase::Environment::get().getInputManager()->resetIdleTime(); + + static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); + if (!isToggleSneak) + { + if(mJoystickLastUsed) + { + if(actionIsActive(A_Sneak)) + { + if(mSneakToggleShortcutTimer) // New Sneak Button Press + { + if(mSneakToggleShortcutTimer <= 0.3f) + { + mSneakGamepadShortcut = true; + mActionManager->toggleSneaking(); + } + else + mSneakGamepadShortcut = false; + } + + if(!mActionManager->isSneaking()) + mActionManager->toggleSneaking(); + mSneakToggleShortcutTimer = 0.f; + } + else + { + if(!mSneakGamepadShortcut && mActionManager->isSneaking()) + mActionManager->toggleSneaking(); + if(mSneakToggleShortcutTimer <= 0.3f) + mSneakToggleShortcutTimer += dt; + } + } + else + player.setSneak(actionIsActive(A_Sneak)); + } + } + + if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch")) + { + if (!actionIsActive(A_TogglePOV)) + mGamepadZoom = 0; + + if(mGamepadZoom) + { + MWBase::Environment::get().getWorld()->changeVanityModeScale(mGamepadZoom); + MWBase::Environment::get().getWorld()->setCameraDistance(mGamepadZoom, true, true); + } + } + } + + void ControllerManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg ) + { + if (!mJoystickEnabled || mInputBinder->detectingBindingState()) + return; + + mJoystickLastUsed = true; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + if (gamepadToGuiControl(arg)) + return; + if (mGamepadGuiCursorEnabled) + { + // Temporary mouse binding until keyboard controls are available: + if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click. + { + bool mousePressSuccess = mMouseManager->injectMouseButtonPress(SDL_BUTTON_LEFT); + if (MyGUI::InputManager::getInstance().getMouseFocusWidget()) + { + MyGUI::Button* b = MyGUI::InputManager::getInstance().getMouseFocusWidget()->castType(false); + if (b && b->getEnabled()) + MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); + } + + MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!mousePressSuccess); + } + } + } + else + MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(true); + + //esc, to leave initial movie screen + auto kc = mInputWrapper->sdl2OISKeyCode(SDLK_ESCAPE); + MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0)); + + if (!mControlsDisabled) + mInputBinder->buttonPressed(deviceID, arg); + } + + void ControllerManager::buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg ) + { + if(mInputBinder->detectingBindingState()) + { + mInputBinder->buttonReleased(deviceID, arg); + return; + } + if (!mJoystickEnabled || mControlsDisabled) + return; + + mJoystickLastUsed = true; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + if (mGamepadGuiCursorEnabled) + { + // Temporary mouse binding until keyboard controls are available: + if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click. + { + bool mousePressSuccess = mMouseManager->injectMouseButtonRelease(SDL_BUTTON_LEFT); + if (mInputBinder->detectingBindingState()) // If the player just triggered binding, don't let button release bind. + return; + + MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!mousePressSuccess); + } + } + } + else + MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(true); + + //esc, to leave initial movie screen + auto kc = mInputWrapper->sdl2OISKeyCode(SDLK_ESCAPE); + MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); + + mInputBinder->buttonReleased(deviceID, arg); + } + + void ControllerManager::axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg) + { + if(!mJoystickEnabled || mControlsDisabled) + return; + + mJoystickLastUsed = true; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + gamepadToGuiControl(arg); + } + else + { + if(mGamepadPreviewMode && arg.value) // Preview Mode Gamepad Zooming + { + if(arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + { + mGamepadZoom = arg.value * 0.85f / 1000.f; + return; // Do not propagate event. + } + else if(arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT) + { + mGamepadZoom = -arg.value * 0.85f / 1000.f; + return; // Do not propagate event. + } + } + } + mInputBinder->axisMoved(deviceID, arg); + } + + void ControllerManager::controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg) + { + mInputBinder->controllerAdded(deviceID, arg); + } + + void ControllerManager::controllerRemoved(const SDL_ControllerDeviceEvent &arg) + { + mInputBinder->controllerRemoved(arg); + } + + bool ControllerManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg) + { + // Presumption of GUI mode will be removed in the future. + // MyGUI KeyCodes *may* change. + + MyGUI::KeyCode key = MyGUI::KeyCode::None; + switch (arg.button) + { + case SDL_CONTROLLER_BUTTON_DPAD_UP: + key = MyGUI::KeyCode::ArrowUp; + break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + key = MyGUI::KeyCode::ArrowRight; + break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + key = MyGUI::KeyCode::ArrowDown; + break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + key = MyGUI::KeyCode::ArrowLeft; + break; + case SDL_CONTROLLER_BUTTON_A: + // If we are using the joystick as a GUI mouse, A must be handled via mouse. + if (mGamepadGuiCursorEnabled) + return false; + key = MyGUI::KeyCode::Space; + break; + case SDL_CONTROLLER_BUTTON_B: + if (MyGUI::InputManager::getInstance().isModalAny()) + MWBase::Environment::get().getWindowManager()->exitCurrentModal(); + else + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); + return true; + case SDL_CONTROLLER_BUTTON_X: + key = MyGUI::KeyCode::Semicolon; + break; + case SDL_CONTROLLER_BUTTON_Y: + key = MyGUI::KeyCode::Apostrophe; + break; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: + key = MyGUI::KeyCode::Period; + break; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: + key = MyGUI::KeyCode::Slash; + break; + case SDL_CONTROLLER_BUTTON_LEFTSTICK: + mGamepadGuiCursorEnabled = !mGamepadGuiCursorEnabled; + MWBase::Environment::get().getWindowManager()->setCursorActive(mGamepadGuiCursorEnabled); + return true; + default: + return false; + } + + // Some keys will work even when Text Input windows/modals are in focus. + if (SDL_IsTextInputActive()) + return false; + + MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); + return true; + } + + bool ControllerManager::gamepadToGuiControl(const SDL_ControllerAxisEvent &arg) + { + switch (arg.axis) + { + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + if (arg.value == 32767) // Treat like a button. + MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Minus, 0, false); + break; + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: + if (arg.value == 32767) // Treat like a button. + MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Equals, 0, false); + break; + case SDL_CONTROLLER_AXIS_LEFTX: + case SDL_CONTROLLER_AXIS_LEFTY: + case SDL_CONTROLLER_AXIS_RIGHTX: + case SDL_CONTROLLER_AXIS_RIGHTY: + // If we are using the joystick as a GUI mouse, process mouse movement elsewhere. + if (mGamepadGuiCursorEnabled) + return false; + break; + default: + return false; + } + + return true; + } +} diff --git a/apps/openmw/mwinput/controllermanager.hpp b/apps/openmw/mwinput/controllermanager.hpp new file mode 100644 index 000000000..2343cab39 --- /dev/null +++ b/apps/openmw/mwinput/controllermanager.hpp @@ -0,0 +1,77 @@ +#ifndef MWINPUT_MWCONTROLLERMANAGER_H +#define MWINPUT_MWCONTROLLERMANAGER_H + +#include + +#include +#include +#include + +namespace ICS +{ + class InputControlSystem; +} + +namespace MWInput +{ + class ActionManager; + class MouseManager; + + class ControllerManager : public SDLUtil::ControllerListener + { + public: + ControllerManager(ICS::InputControlSystem* inputBinder, + SDLUtil::InputWrapper* inputWrapper, + ActionManager* actionManager, + MouseManager* mouseManager, + const std::string& userControllerBindingsFile, + const std::string& controllerBindingsFile); + + virtual ~ControllerManager() = default; + + void clear(); + + void update(float dt, bool disableControls, bool gamepadPreviewMode); + + virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg); + virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg); + virtual void axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg); + virtual void controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg); + virtual void controllerRemoved(const SDL_ControllerDeviceEvent &arg); + + void processChangedSettings(const Settings::CategorySettingVector& changed); + + void setJoystickLastUsed(bool enabled) { mJoystickLastUsed = enabled; } + bool joystickLastUsed() { return mJoystickLastUsed; } + + void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; } + + void setGamepadGuiCursorEnabled(bool enabled) { mGamepadGuiCursorEnabled = enabled; } + bool gamepadGuiCursorEnabled() { return mGamepadGuiCursorEnabled; } + + private: + // Return true if GUI consumes input. + bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg); + bool gamepadToGuiControl(const SDL_ControllerAxisEvent &arg); + + bool actionIsActive(int id); + + ICS::InputControlSystem* mInputBinder; + SDLUtil::InputWrapper* mInputWrapper; + ActionManager* mActionManager; + MouseManager* mMouseManager; + + bool mJoystickEnabled; + float mGamepadCursorSpeed; + float mInvUiScalingFactor; + float mSneakToggleShortcutTimer; + float mGamepadZoom; + bool mGamepadGuiCursorEnabled; + bool mControlsDisabled; + bool mJoystickLastUsed; + bool mGuiCursorEnabled; + bool mSneakGamepadShortcut; + bool mGamepadPreviewMode; + }; +} +#endif diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 4376951b5..a647aac20 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -31,6 +31,7 @@ #include "../mwmechanics/actorutil.hpp" #include "actionmanager.hpp" +#include "controllermanager.hpp" #include "mousemanager.hpp" #include "sdlmappings.hpp" #include "sensormanager.hpp" @@ -46,31 +47,23 @@ namespace MWInput const std::string& controllerBindingsFile, bool grab) : mWindow(window) , mWindowVisible(true) - , mJoystickLastUsed(false) , mInputManager(nullptr) , mVideoWrapper(nullptr) , mUserFile(userFile) , mDragDrop(false) , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) , mControlsDisabled(false) - , mJoystickEnabled (Settings::Manager::getBool("enable controller", "Input")) , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) , mGuiCursorEnabled(true) - , mGamepadGuiCursorEnabled(true) , mDetectingKeyboard(false) , mOverencumberedMessageDelay(0.f) - , mGamepadZoom(0) - , mSneakToggleShortcutTimer(0.f) - , mSneakGamepadShortcut(false) , mAttemptJump(false) - , mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input")) , mFakeDeviceID(1) { mInputManager = new SDLUtil::InputWrapper(window, viewer, grab); mInputManager->setKeyboardEventCallback (this); mInputManager->setWindowEventCallback(this); - mInputManager->setControllerEventCallback(this); mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer); mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"), @@ -95,42 +88,16 @@ namespace MWInput mControlSwitch["playerviewswitch"] = true; mControlSwitch["vanitymode"] = true; - /* Joystick Init */ + mActionManager = new ActionManager(mInputBinder, screenCaptureOperation, viewer, screenCaptureHandler); - // Load controller mappings - if(!controllerBindingsFile.empty()) - { - SDL_GameControllerAddMappingsFromFile(controllerBindingsFile.c_str()); - } - if(!userControllerBindingsFile.empty()) - { - SDL_GameControllerAddMappingsFromFile(userControllerBindingsFile.c_str()); - } + mMouseManager = new MouseManager(mInputBinder, mInputManager, window); + mInputManager->setMouseEventCallback (mMouseManager); - // Open all presently connected sticks - int numSticks = SDL_NumJoysticks(); - for(int i = 0; i < numSticks; i++) - { - if(SDL_IsGameController(i)) - { - SDL_ControllerDeviceEvent evt; - evt.which = i; - controllerAdded(mFakeDeviceID, evt); - Log(Debug::Info) << "Detected game controller: " << SDL_GameControllerNameForIndex(i); - } - else - { - Log(Debug::Info) << "Detected unusable controller: " << SDL_JoystickNameForIndex(i); - } - } + mControllerManager = new ControllerManager(mInputBinder, mInputManager, mActionManager, mMouseManager, userControllerBindingsFile, controllerBindingsFile); + mInputManager->setControllerEventCallback(mControllerManager); mSensorManager = new SensorManager(); mInputManager->setSensorEventCallback (mSensorManager); - - mMouseManager = new MouseManager(mInputBinder, mInputManager, window); - mInputManager->setMouseEventCallback (mMouseManager); - - mActionManager = new ActionManager(mInputBinder, screenCaptureOperation, viewer, screenCaptureHandler); } void InputManager::clear() @@ -140,6 +107,7 @@ namespace MWInput it->second = true; mActionManager->clear(); + mControllerManager->clear(); mSensorManager->clear(); mMouseManager->clear(); } @@ -149,6 +117,7 @@ namespace MWInput mInputBinder->save (mUserFile); delete mActionManager; + delete mControllerManager; delete mMouseManager; delete mSensorManager; @@ -178,93 +147,6 @@ namespace MWInput } } - bool InputManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg) - { - // Presumption of GUI mode will be removed in the future. - // MyGUI KeyCodes *may* change. - - MyGUI::KeyCode key = MyGUI::KeyCode::None; - switch (arg.button) - { - case SDL_CONTROLLER_BUTTON_DPAD_UP: - key = MyGUI::KeyCode::ArrowUp; - break; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: - key = MyGUI::KeyCode::ArrowRight; - break; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: - key = MyGUI::KeyCode::ArrowDown; - break; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: - key = MyGUI::KeyCode::ArrowLeft; - break; - case SDL_CONTROLLER_BUTTON_A: - // If we are using the joystick as a GUI mouse, A must be handled via mouse. - if (mGamepadGuiCursorEnabled) - return false; - key = MyGUI::KeyCode::Space; - break; - case SDL_CONTROLLER_BUTTON_B: - if (MyGUI::InputManager::getInstance().isModalAny()) - MWBase::Environment::get().getWindowManager()->exitCurrentModal(); - else - MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); - return true; - case SDL_CONTROLLER_BUTTON_X: - key = MyGUI::KeyCode::Semicolon; - break; - case SDL_CONTROLLER_BUTTON_Y: - key = MyGUI::KeyCode::Apostrophe; - break; - case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: - key = MyGUI::KeyCode::Period; - break; - case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: - key = MyGUI::KeyCode::Slash; - break; - case SDL_CONTROLLER_BUTTON_LEFTSTICK: - mGamepadGuiCursorEnabled = !mGamepadGuiCursorEnabled; - MWBase::Environment::get().getWindowManager()->setCursorActive(mGamepadGuiCursorEnabled); - return true; - default: - return false; - } - - // Some keys will work even when Text Input windows/modals are in focus. - if (SDL_IsTextInputActive()) - return false; - - MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); - return true; - } - - bool InputManager::gamepadToGuiControl(const SDL_ControllerAxisEvent &arg) - { - switch (arg.axis) - { - case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: - if (arg.value == 32767) // Treat like a button. - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Minus, 0, false); - break; - case SDL_CONTROLLER_AXIS_TRIGGERLEFT: - if (arg.value == 32767) // Treat like a button. - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Equals, 0, false); - break; - case SDL_CONTROLLER_AXIS_LEFTX: - case SDL_CONTROLLER_AXIS_LEFTY: - case SDL_CONTROLLER_AXIS_RIGHTX: - case SDL_CONTROLLER_AXIS_RIGHTY: - // If we are using the joystick as a GUI mouse, process mouse movement elsewhere. - if (mGamepadGuiCursorEnabled) - return false; - break; - default: - return false; - } - - return true; - } - void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) { resetIdleTime (); @@ -297,12 +179,13 @@ namespace MWInput if (mControlSwitch["playercontrols"]) { + bool joystickUsed = mControllerManager->joystickLastUsed(); if (action == A_Use) { - if(mJoystickLastUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) + if(joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) action = A_CycleWeaponRight; - else if (mJoystickLastUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell)) + else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell)) action = A_CycleSpellRight; else @@ -314,10 +197,10 @@ namespace MWInput } else if (action == A_Jump) { - if(mJoystickLastUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) + if(joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) action = A_CycleWeaponLeft; - else if (mJoystickLastUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell)) + else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell)) action = A_CycleSpellLeft; else @@ -371,28 +254,7 @@ namespace MWInput updateCursorMode(); - if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled)) - { - float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue()*2.0f-1.0f; - float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue()*2.0f-1.0f; - float zAxis = mInputBinder->getChannel(A_LookUpDown)->getValue()*2.0f-1.0f; - - xAxis *= (1.5f - mInputBinder->getChannel(A_Use)->getValue()); - yAxis *= (1.5f - mInputBinder->getChannel(A_Use)->getValue()); - - // We keep track of our own mouse position, so that moving the mouse while in - // game mode does not move the position of the GUI cursor - float xMove = xAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; - float yMove = yAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; - if (xMove != 0|| yMove != 0 || zAxis != 0) - { - int mouseWheelMove = static_cast(-zAxis * dt * 1500.0f); - - mMouseManager->injectMouseMove(xMove, yMove, mouseWheelMove); - mMouseManager->warpMouse(); - MWBase::Environment::get().getWindowManager()->setCursorActive(true); - } - } + mControllerManager->update(dt, disableControls, mPreviewPOVDelay == 1.f); if (mMouseManager->update(dt, disableControls)) resetIdleTime(); @@ -414,28 +276,10 @@ namespace MWInput bool isRunning = false; bool alwaysRunAllowed = false; - // joystick movement + // keyboard movement float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue(); float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue(); - if (xAxis != .5) - { - triedToMove = true; - player.setLeftRight((xAxis - 0.5f) * 2); - } - - if (yAxis != .5) - { - triedToMove = true; - player.setAutoMove (false); - player.setForwardBackward((yAxis - 0.5f) * 2 * -1); - } - - if (triedToMove) - mJoystickLastUsed = true; - - // keyboard movement isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25; - if(triedToMove) resetIdleTime(); if (actionIsActive(A_MoveLeft) != actionIsActive(A_MoveRight)) { @@ -462,34 +306,7 @@ namespace MWInput static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); if (!isToggleSneak) { - if(mJoystickLastUsed) - { - if(actionIsActive(A_Sneak)) - { - if(mSneakToggleShortcutTimer) // New Sneak Button Press - { - if(mSneakToggleShortcutTimer <= 0.3f) - { - mSneakGamepadShortcut = true; - mActionManager->toggleSneaking(); - } - else - mSneakGamepadShortcut = false; - } - - if(!mActionManager->isSneaking()) - mActionManager->toggleSneaking(); - mSneakToggleShortcutTimer = 0.f; - } - else - { - if(!mSneakGamepadShortcut && mActionManager->isSneaking()) - mActionManager->toggleSneaking(); - if(mSneakToggleShortcutTimer <= 0.3f) - mSneakToggleShortcutTimer += dt; - } - } - else + if(!mControllerManager->joystickLastUsed()) player.setSneak(actionIsActive(A_Sneak)); } @@ -537,13 +354,6 @@ namespace MWInput MWBase::Environment::get().getWorld()->togglePOV(); } mPreviewPOVDelay = 0.f; - mGamepadZoom = 0; - } - - if(mGamepadZoom) - { - MWBase::Environment::get().getWorld()->changeVanityModeScale(mGamepadZoom); - MWBase::Environment::get().getWorld()->setCameraDistance(mGamepadZoom, true, true); } } } @@ -562,8 +372,6 @@ namespace MWInput updateIdleTime(dt); } } - else - mGamepadZoom = 0; mAttemptJump = false; // Can only jump on first frame input is on } @@ -574,17 +382,18 @@ namespace MWInput void InputManager::setGamepadGuiCursorEnabled(bool enabled) { - mGamepadGuiCursorEnabled = enabled; + mControllerManager->setGamepadGuiCursorEnabled(enabled); } void InputManager::changeInputMode(bool guiMode) { mGuiCursorEnabled = guiMode; - mMouseManager->setGuiCursorEnabled(guiMode); - mMouseManager->setMouseLookEnabled(!guiMode); + mControllerManager->setGuiCursorEnabled(mGuiCursorEnabled); + mMouseManager->setGuiCursorEnabled(mGuiCursorEnabled); + mMouseManager->setMouseLookEnabled(!mGuiCursorEnabled); if (guiMode) MWBase::Environment::get().getWindowManager()->showCrosshair(false); - MWBase::Environment::get().getWindowManager()->setCursorVisible(guiMode && (!mJoystickLastUsed || mGamepadGuiCursorEnabled)); + MWBase::Environment::get().getWindowManager()->setCursorVisible(guiMode && (!mControllerManager->joystickLastUsed() || mControllerManager->gamepadGuiCursorEnabled())); // if not in gui mode, the camera decides whether to show crosshair or not. } @@ -598,9 +407,6 @@ namespace MWInput if (it->first == "Input" && it->second == "grab cursor") mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); - if (it->first == "Input" && it->second == "enable controller") - mJoystickEnabled = Settings::Manager::getBool("enable controller", "Input"); - if (it->first == "Video" && ( it->second == "resolution x" || it->second == "resolution y" @@ -686,7 +492,7 @@ namespace MWInput if (!mControlsDisabled && !consumed) mInputBinder->keyPressed (arg); - mJoystickLastUsed = false; + mControllerManager->setJoystickLastUsed(false); } void InputManager::textInput(const SDL_TextInputEvent &arg) @@ -699,7 +505,7 @@ namespace MWInput void InputManager::keyReleased(const SDL_KeyboardEvent &arg ) { - mJoystickLastUsed = false; + mControllerManager->setJoystickLastUsed(false); OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); if (!mInputBinder->detectingBindingState()) @@ -707,118 +513,6 @@ namespace MWInput mInputBinder->keyReleased (arg); } - void InputManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg ) - { - if (!mJoystickEnabled || mInputBinder->detectingBindingState()) - return; - - mJoystickLastUsed = true; - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - if (gamepadToGuiControl(arg)) - return; - if (mGamepadGuiCursorEnabled) - { - // Temporary mouse binding until keyboard controls are available: - if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click. - { - bool mousePressSuccess = mMouseManager->injectMouseButtonPress(SDL_BUTTON_LEFT); - if (MyGUI::InputManager::getInstance().getMouseFocusWidget()) - { - MyGUI::Button* b = MyGUI::InputManager::getInstance().getMouseFocusWidget()->castType(false); - if (b && b->getEnabled()) - MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); - } - - setPlayerControlsEnabled(!mousePressSuccess); - } - } - } - else - setPlayerControlsEnabled(true); - - //esc, to leave initial movie screen - OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(SDLK_ESCAPE); - setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0)); - - if (!mControlsDisabled) - mInputBinder->buttonPressed(deviceID, arg); - } - - void InputManager::buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg ) - { - if(mInputBinder->detectingBindingState()) - { - mInputBinder->buttonReleased(deviceID, arg); - return; - } - if (!mJoystickEnabled || mControlsDisabled) - return; - - mJoystickLastUsed = true; - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - if (mGamepadGuiCursorEnabled) - { - // Temporary mouse binding until keyboard controls are available: - if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click. - { - bool mousePressSuccess = mMouseManager->injectMouseButtonRelease(SDL_BUTTON_LEFT); - if (mInputBinder->detectingBindingState()) // If the player just triggered binding, don't let button release bind. - return; - - setPlayerControlsEnabled(!mousePressSuccess); - } - } - } - else - setPlayerControlsEnabled(true); - - //esc, to leave initial movie screen - OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(SDLK_ESCAPE); - setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); - - mInputBinder->buttonReleased(deviceID, arg); - } - - void InputManager::axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg ) - { - if(!mJoystickEnabled || mControlsDisabled) - return; - - mJoystickLastUsed = true; - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - gamepadToGuiControl(arg); - } - else - { - if(mPreviewPOVDelay == 1.f && arg.value) // Preview Mode Gamepad Zooming - { - if(arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) - { - mGamepadZoom = arg.value * 0.85f / 1000.f; - return; // Do not propagate event. - } - else if(arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT) - { - mGamepadZoom = -arg.value * 0.85f / 1000.f; - return; // Do not propagate event. - } - } - } - mInputBinder->axisMoved(deviceID, arg); - } - - void InputManager::controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg) - { - mInputBinder->controllerAdded(deviceID, arg); - } - void InputManager::controllerRemoved(const SDL_ControllerDeviceEvent &arg) - { - mInputBinder->controllerRemoved(arg); - } - void InputManager::windowFocusChange(bool have_focus) { } @@ -1398,4 +1092,14 @@ namespace MWInput { loadControllerDefaults(true); } + + void InputManager::setJoystickLastUsed(bool enabled) + { + mControllerManager->setJoystickLastUsed(enabled); + } + + bool InputManager::joystickLastUsed() + { + return mControllerManager->joystickLastUsed(); + } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index da7075e4e..93bcf13ee 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -22,6 +22,7 @@ namespace MWInput { class ActionManager; + class ControllerManager; class MouseManager; class SensorManager; } @@ -63,7 +64,6 @@ namespace MWInput public MWBase::InputManager, public SDLUtil::KeyListener, public SDLUtil::WindowListener, - public SDLUtil::ControllerListener, public ICS::ChannelListener, public ICS::DetectingBindingListener { @@ -106,19 +106,13 @@ namespace MWInput virtual void resetToDefaultKeyBindings(); virtual void resetToDefaultControllerBindings(); - virtual void setJoystickLastUsed(bool enabled) { mJoystickLastUsed = enabled; } - virtual bool joystickLastUsed() {return mJoystickLastUsed;} + virtual void setJoystickLastUsed(bool enabled); + virtual bool joystickLastUsed(); virtual void keyPressed(const SDL_KeyboardEvent &arg ); virtual void keyReleased( const SDL_KeyboardEvent &arg ); virtual void textInput (const SDL_TextInputEvent &arg); - virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg); - virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg); - virtual void axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg); - virtual void controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg); - virtual void controllerRemoved(const SDL_ControllerDeviceEvent &arg); - virtual void windowVisibilityChange( bool visible ); virtual void windowFocusChange( bool have_focus ); virtual void windowResized (int x, int y); @@ -159,9 +153,6 @@ namespace MWInput SDL_Window* mWindow; bool mWindowVisible; - bool mJoystickLastUsed; - MWWorld::Player* mPlayer; - ICS::InputControlSystem* mInputBinder; SDLUtil::InputWrapper* mInputManager; @@ -174,30 +165,22 @@ namespace MWInput bool mGrabCursor; bool mControlsDisabled; - bool mJoystickEnabled; float mPreviewPOVDelay; float mTimeIdle; bool mGuiCursorEnabled; - bool mGamepadGuiCursorEnabled; bool mDetectingKeyboard; float mOverencumberedMessageDelay; - float mGamepadZoom; - bool mSneakToggles; - float mSneakToggleShortcutTimer; - bool mSneakGamepadShortcut; bool mAttemptJump; std::map mControlSwitch; - float mInvUiScalingFactor; - float mGamepadCursorSpeed; - ActionManager* mActionManager; + ControllerManager* mControllerManager; MouseManager* mMouseManager; SensorManager* mSensorManager; @@ -206,9 +189,6 @@ namespace MWInput void updateIdleTime(float dt); void handleGuiArrowKey(int action); - // Return true if GUI consumes input. - bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg); - bool gamepadToGuiControl(const SDL_ControllerAxisEvent &arg); void updateCursorMode(); diff --git a/apps/openmw/mwinput/mousemanager.hpp b/apps/openmw/mwinput/mousemanager.hpp index c0a9408c6..f4517da47 100644 --- a/apps/openmw/mwinput/mousemanager.hpp +++ b/apps/openmw/mwinput/mousemanager.hpp @@ -1,8 +1,6 @@ #ifndef MWINPUT_MWMOUSEMANAGER_H #define MWINPUT_MWMOUSEMANAGER_H -#include - #include #include From c368250e6a9bbb907f973d52069e5af04fb0cf29 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 15 Apr 2020 12:48:18 +0400 Subject: [PATCH 051/227] Rename misleading mInputManager variable --- apps/openmw/mwinput/inputmanagerimp.cpp | 35 ++++++++++++------------- apps/openmw/mwinput/inputmanagerimp.hpp | 2 +- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index a647aac20..ca07f6645 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -47,7 +47,7 @@ namespace MWInput const std::string& controllerBindingsFile, bool grab) : mWindow(window) , mWindowVisible(true) - , mInputManager(nullptr) + , mInputWrapper(nullptr) , mVideoWrapper(nullptr) , mUserFile(userFile) , mDragDrop(false) @@ -61,9 +61,9 @@ namespace MWInput , mAttemptJump(false) , mFakeDeviceID(1) { - mInputManager = new SDLUtil::InputWrapper(window, viewer, grab); - mInputManager->setKeyboardEventCallback (this); - mInputManager->setWindowEventCallback(this); + mInputWrapper = new SDLUtil::InputWrapper(window, viewer, grab); + mInputWrapper->setKeyboardEventCallback (this); + mInputWrapper->setWindowEventCallback(this); mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer); mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"), @@ -90,14 +90,14 @@ namespace MWInput mActionManager = new ActionManager(mInputBinder, screenCaptureOperation, viewer, screenCaptureHandler); - mMouseManager = new MouseManager(mInputBinder, mInputManager, window); - mInputManager->setMouseEventCallback (mMouseManager); + mMouseManager = new MouseManager(mInputBinder, mInputWrapper, window); + mInputWrapper->setMouseEventCallback (mMouseManager); - mControllerManager = new ControllerManager(mInputBinder, mInputManager, mActionManager, mMouseManager, userControllerBindingsFile, controllerBindingsFile); - mInputManager->setControllerEventCallback(mControllerManager); + mControllerManager = new ControllerManager(mInputBinder, mInputWrapper, mActionManager, mMouseManager, userControllerBindingsFile, controllerBindingsFile); + mInputWrapper->setControllerEventCallback(mControllerManager); mSensorManager = new SensorManager(); - mInputManager->setSensorEventCallback (mSensorManager); + mInputWrapper->setSensorEventCallback (mSensorManager); } void InputManager::clear() @@ -123,8 +123,7 @@ namespace MWInput delete mInputBinder; - delete mInputManager; - + delete mInputWrapper; delete mVideoWrapper; } @@ -217,15 +216,15 @@ namespace MWInput bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && !MWBase::Environment::get().getWindowManager()->isConsoleMode(); - bool was_relative = mInputManager->getMouseRelative(); + bool was_relative = mInputWrapper->getMouseRelative(); bool is_relative = !MWBase::Environment::get().getWindowManager()->isGuiMode(); // don't keep the pointer away from the window edge in gui mode // stop using raw mouse motions and switch to system cursor movements - mInputManager->setMouseRelative(is_relative); + mInputWrapper->setMouseRelative(is_relative); //we let the mouse escape in the main menu - mInputManager->setGrabPointer(grab && (mGrabCursor || is_relative)); + mInputWrapper->setGrabPointer(grab && (mGrabCursor || is_relative)); //we switched to non-relative mode, move our cursor to where the in-game //cursor is @@ -239,9 +238,9 @@ namespace MWInput { mControlsDisabled = disableControls; - mInputManager->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); + mInputWrapper->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); - mInputManager->capture(disableEvents); + mInputWrapper->capture(disableEvents); if (mControlsDisabled) { @@ -472,7 +471,7 @@ namespace MWInput // HACK: to make Morrowind's default keybinding for the console work without printing an extra "^" upon closing // This assumes that SDL_TextInput events always come *after* the key event // (which is somewhat reasonable, and hopefully true for all SDL platforms) - OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); + OIS::KeyCode kc = mInputWrapper->sdl2OISKeyCode(arg.keysym.sym); if (mInputBinder->getKeyBinding(mInputBinder->getControl(A_Console), ICS::Control::INCREASE) == arg.keysym.scancode && MWBase::Environment::get().getWindowManager()->isConsoleMode()) @@ -506,7 +505,7 @@ namespace MWInput void InputManager::keyReleased(const SDL_KeyboardEvent &arg ) { mControllerManager->setJoystickLastUsed(false); - OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); + OIS::KeyCode kc = mInputWrapper->sdl2OISKeyCode(arg.keysym.sym); if (!mInputBinder->detectingBindingState()) setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 93bcf13ee..e6c084a64 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -155,7 +155,7 @@ namespace MWInput ICS::InputControlSystem* mInputBinder; - SDLUtil::InputWrapper* mInputManager; + SDLUtil::InputWrapper* mInputWrapper; SDLUtil::VideoWrapper* mVideoWrapper; std::string mUserFile; From d3a9f893c839f05b699d6e7782cf01da90e887a5 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 16 Apr 2020 11:03:34 +0400 Subject: [PATCH 052/227] Move keyboard-specific code to the separate file --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwinput/controllermanager.cpp | 19 +-- apps/openmw/mwinput/controllermanager.hpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 118 +++-------------- apps/openmw/mwinput/inputmanagerimp.hpp | 9 +- apps/openmw/mwinput/keyboardmanager.cpp | 146 ++++++++++++++++++++++ apps/openmw/mwinput/keyboardmanager.hpp | 45 +++++++ apps/openmw/mwinput/sensormanager.hpp | 12 +- 8 files changed, 224 insertions(+), 129 deletions(-) create mode 100644 apps/openmw/mwinput/keyboardmanager.cpp create mode 100644 apps/openmw/mwinput/keyboardmanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 405320881..97cc9b035 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,7 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - actions actionmanager controllermanager inputmanagerimp mousemanager sdlmappings sensormanager + actions actionmanager controllermanager inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager ) add_openmw_dir (mwgui diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index ec687ed7b..209800bd6 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -94,7 +94,7 @@ namespace MWInput return (mInputBinder->getChannel (id)->getValue ()==1.0); } - void ControllerManager::update(float dt, bool disableControls, bool gamepadPreviewMode) + bool ControllerManager::update(float dt, bool disableControls, bool gamepadPreviewMode) { mControlsDisabled = disableControls; mGamepadPreviewMode = gamepadPreviewMode; @@ -122,23 +122,21 @@ namespace MWInput } } - MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); - // Disable movement in Gui mode if (MWBase::Environment::get().getWindowManager()->isGuiMode() || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running) { mGamepadZoom = 0; - return; + return false; } - // Configure player movement according to keyboard input. Actual movement will + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + bool triedToMove = false; + + // Configure player movement according to controller input. Actual movement will // be done in the physics system. if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) { - bool triedToMove = false; - - // joystick movement float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue(); float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue(); if (xAxis != .5) @@ -157,7 +155,8 @@ namespace MWInput if (triedToMove) mJoystickLastUsed = true; - if(triedToMove) MWBase::Environment::get().getInputManager()->resetIdleTime(); + if (triedToMove) + MWBase::Environment::get().getInputManager()->resetIdleTime(); static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); if (!isToggleSneak) @@ -205,6 +204,8 @@ namespace MWInput MWBase::Environment::get().getWorld()->setCameraDistance(mGamepadZoom, true, true); } } + + return triedToMove; } void ControllerManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg ) diff --git a/apps/openmw/mwinput/controllermanager.hpp b/apps/openmw/mwinput/controllermanager.hpp index 2343cab39..77b68be54 100644 --- a/apps/openmw/mwinput/controllermanager.hpp +++ b/apps/openmw/mwinput/controllermanager.hpp @@ -31,7 +31,7 @@ namespace MWInput void clear(); - void update(float dt, bool disableControls, bool gamepadPreviewMode); + bool update(float dt, bool disableControls, bool gamepadPreviewMode); virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg); virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ca07f6645..d176ba480 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -32,6 +32,7 @@ #include "actionmanager.hpp" #include "controllermanager.hpp" +#include "keyboardmanager.hpp" #include "mousemanager.hpp" #include "sdlmappings.hpp" #include "sensormanager.hpp" @@ -47,12 +48,9 @@ namespace MWInput const std::string& controllerBindingsFile, bool grab) : mWindow(window) , mWindowVisible(true) - , mInputWrapper(nullptr) - , mVideoWrapper(nullptr) , mUserFile(userFile) , mDragDrop(false) , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) - , mControlsDisabled(false) , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) , mGuiCursorEnabled(true) @@ -62,7 +60,6 @@ namespace MWInput , mFakeDeviceID(1) { mInputWrapper = new SDLUtil::InputWrapper(window, viewer, grab); - mInputWrapper->setKeyboardEventCallback (this); mInputWrapper->setWindowEventCallback(this); mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer); @@ -90,14 +87,17 @@ namespace MWInput mActionManager = new ActionManager(mInputBinder, screenCaptureOperation, viewer, screenCaptureHandler); + mKeyboardManager = new KeyboardManager(mInputBinder, mInputWrapper, mActionManager); + mInputWrapper->setKeyboardEventCallback(mKeyboardManager); + mMouseManager = new MouseManager(mInputBinder, mInputWrapper, window); - mInputWrapper->setMouseEventCallback (mMouseManager); + mInputWrapper->setMouseEventCallback(mMouseManager); mControllerManager = new ControllerManager(mInputBinder, mInputWrapper, mActionManager, mMouseManager, userControllerBindingsFile, controllerBindingsFile); mInputWrapper->setControllerEventCallback(mControllerManager); mSensorManager = new SensorManager(); - mInputWrapper->setSensorEventCallback (mSensorManager); + mInputWrapper->setSensorEventCallback(mSensorManager); } void InputManager::clear() @@ -118,6 +118,7 @@ namespace MWInput delete mActionManager; delete mControllerManager; + delete mKeyboardManager; delete mMouseManager; delete mSensorManager; @@ -236,13 +237,11 @@ namespace MWInput void InputManager::update(float dt, bool disableControls, bool disableEvents) { - mControlsDisabled = disableControls; - mInputWrapper->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); mInputWrapper->capture(disableEvents); - if (mControlsDisabled) + if (disableControls) { updateCursorMode(); return; @@ -253,7 +252,8 @@ namespace MWInput updateCursorMode(); - mControllerManager->update(dt, disableControls, mPreviewPOVDelay == 1.f); + bool controllerMove = mControllerManager->update(dt, disableControls, mPreviewPOVDelay == 1.f); + bool keyboardMove = mKeyboardManager->update(dt, disableControls); if (mMouseManager->update(dt, disableControls)) resetIdleTime(); @@ -265,64 +265,20 @@ namespace MWInput if (!(MWBase::Environment::get().getWindowManager()->isGuiMode() || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)) { - // Configure player movement according to keyboard input. Actual movement will - // be done in the physics system. if (mControlSwitch["playercontrols"]) { MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); - bool triedToMove = false; - bool isRunning = false; - bool alwaysRunAllowed = false; - - // keyboard movement - float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue(); - float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue(); - isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25; - - if (actionIsActive(A_MoveLeft) != actionIsActive(A_MoveRight)) - { - alwaysRunAllowed = true; - triedToMove = true; - player.setLeftRight (actionIsActive(A_MoveRight) ? 1 : -1); - } - - if (actionIsActive(A_MoveForward) != actionIsActive(A_MoveBackward)) - { - alwaysRunAllowed = true; - triedToMove = true; - player.setAutoMove (false); - player.setForwardBackward (actionIsActive(A_MoveForward) ? 1 : -1); - } - - if (player.getAutoMove()) - { - alwaysRunAllowed = true; - triedToMove = true; - player.setForwardBackward (1); - } - - static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); - if (!isToggleSneak) - { - if(!mControllerManager->joystickLastUsed()) - player.setSneak(actionIsActive(A_Sneak)); - } - + bool attemptToJump = false; if (mAttemptJump && mControlSwitch["playerjumping"]) { - player.setUpDown (1); - triedToMove = true; + player.setUpDown(1); + attemptToJump = true; mOverencumberedMessageDelay = 0.f; } - if ((mActionManager->isAlwaysRunActive() && alwaysRunAllowed) || isRunning) - player.setRunState(!actionIsActive(A_Run)); - else - player.setRunState(actionIsActive(A_Run)); - // if player tried to start moving, but can't (due to being overencumbered), display a notification. - if (triedToMove) + if (controllerMove || keyboardMove || attemptToJump) { MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); mOverencumberedMessageDelay -= dt; @@ -466,52 +422,6 @@ namespace MWInput mControlSwitch[sw] = value; } - void InputManager::keyPressed( const SDL_KeyboardEvent &arg ) - { - // HACK: to make Morrowind's default keybinding for the console work without printing an extra "^" upon closing - // This assumes that SDL_TextInput events always come *after* the key event - // (which is somewhat reasonable, and hopefully true for all SDL platforms) - OIS::KeyCode kc = mInputWrapper->sdl2OISKeyCode(arg.keysym.sym); - if (mInputBinder->getKeyBinding(mInputBinder->getControl(A_Console), ICS::Control::INCREASE) - == arg.keysym.scancode - && MWBase::Environment::get().getWindowManager()->isConsoleMode()) - SDL_StopTextInput(); - - bool consumed = false; - if (kc != OIS::KC_UNASSIGNED && !mInputBinder->detectingBindingState()) - { - consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Enum(kc), 0, arg.repeat); - if (SDL_IsTextInputActive() && // Little trick to check if key is printable - ( !(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym))) - consumed = true; - setPlayerControlsEnabled(!consumed); - } - if (arg.repeat) - return; - - if (!mControlsDisabled && !consumed) - mInputBinder->keyPressed (arg); - mControllerManager->setJoystickLastUsed(false); - } - - void InputManager::textInput(const SDL_TextInputEvent &arg) - { - MyGUI::UString ustring(&arg.text[0]); - MyGUI::UString::utf32string utf32string = ustring.asUTF32(); - for (MyGUI::UString::utf32string::const_iterator it = utf32string.begin(); it != utf32string.end(); ++it) - MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it); - } - - void InputManager::keyReleased(const SDL_KeyboardEvent &arg ) - { - mControllerManager->setJoystickLastUsed(false); - OIS::KeyCode kc = mInputWrapper->sdl2OISKeyCode(arg.keysym.sym); - - if (!mInputBinder->detectingBindingState()) - setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); - mInputBinder->keyReleased (arg); - } - void InputManager::windowFocusChange(bool have_focus) { } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index e6c084a64..ff0c43a82 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -23,6 +23,7 @@ namespace MWInput { class ActionManager; class ControllerManager; + class KeyboardManager; class MouseManager; class SensorManager; } @@ -62,7 +63,6 @@ namespace MWInput */ class InputManager : public MWBase::InputManager, - public SDLUtil::KeyListener, public SDLUtil::WindowListener, public ICS::ChannelListener, public ICS::DetectingBindingListener @@ -109,10 +109,6 @@ namespace MWInput virtual void setJoystickLastUsed(bool enabled); virtual bool joystickLastUsed(); - virtual void keyPressed(const SDL_KeyboardEvent &arg ); - virtual void keyReleased( const SDL_KeyboardEvent &arg ); - virtual void textInput (const SDL_TextInputEvent &arg); - virtual void windowVisibilityChange( bool visible ); virtual void windowFocusChange( bool have_focus ); virtual void windowResized (int x, int y); @@ -164,8 +160,6 @@ namespace MWInput bool mGrabCursor; - bool mControlsDisabled; - float mPreviewPOVDelay; float mTimeIdle; @@ -181,6 +175,7 @@ namespace MWInput ActionManager* mActionManager; ControllerManager* mControllerManager; + KeyboardManager* mKeyboardManager; MouseManager* mMouseManager; SensorManager* mSensorManager; diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp new file mode 100644 index 000000000..f56246716 --- /dev/null +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -0,0 +1,146 @@ +#include "keyboardmanager.hpp" + +#include + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" +#include "../mwbase/statemanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" + +#include "actionmanager.hpp" +#include "actions.hpp" + +namespace MWInput +{ + KeyboardManager::KeyboardManager(ICS::InputControlSystem* inputBinder, SDLUtil::InputWrapper* inputWrapper, ActionManager* actionManager) + : mInputBinder(inputBinder) + , mInputWrapper(inputWrapper) + , mActionManager(actionManager) + , mControlsDisabled(false) + { + } + + bool KeyboardManager::update(float dt, bool disableControls) + { + mControlsDisabled = disableControls; + + // Disable movement in Gui mode + if (MWBase::Environment::get().getWindowManager()->isGuiMode() + || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running) + { + return false; + } + + // Configure player movement according to keyboard input. Actual movement will + // be done in the physics system. + if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + { + return false; + } + + bool triedToMove = false; + bool alwaysRunAllowed = false; + + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + + if (actionIsActive(A_MoveLeft) != actionIsActive(A_MoveRight)) + { + alwaysRunAllowed = true; + triedToMove = true; + player.setLeftRight (actionIsActive(A_MoveRight) ? 1 : -1); + } + + if (actionIsActive(A_MoveForward) != actionIsActive(A_MoveBackward)) + { + alwaysRunAllowed = true; + triedToMove = true; + player.setAutoMove (false); + player.setForwardBackward (actionIsActive(A_MoveForward) ? 1 : -1); + } + + if (player.getAutoMove()) + { + alwaysRunAllowed = true; + triedToMove = true; + player.setForwardBackward (1); + } + + if (triedToMove) + MWBase::Environment::get().getInputManager()->resetIdleTime(); + + static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); + if (!isToggleSneak) + { + if(!MWBase::Environment::get().getInputManager()->joystickLastUsed()) + player.setSneak(actionIsActive(A_Sneak)); + } + + float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue(); + float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue(); + bool isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25; + if ((mActionManager->isAlwaysRunActive() && alwaysRunAllowed) || isRunning) + player.setRunState(!actionIsActive(A_Run)); + else + player.setRunState(actionIsActive(A_Run)); + + return triedToMove; + } + + bool KeyboardManager::actionIsActive (int id) + { + return (mInputBinder->getChannel(id)->getValue ()==1.0); + } + + void KeyboardManager::textInput(const SDL_TextInputEvent &arg) + { + MyGUI::UString ustring(&arg.text[0]); + MyGUI::UString::utf32string utf32string = ustring.asUTF32(); + for (MyGUI::UString::utf32string::const_iterator it = utf32string.begin(); it != utf32string.end(); ++it) + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it); + } + + void KeyboardManager::keyPressed(const SDL_KeyboardEvent &arg) + { + // HACK: to make Morrowind's default keybinding for the console work without printing an extra "^" upon closing + // This assumes that SDL_TextInput events always come *after* the key event + // (which is somewhat reasonable, and hopefully true for all SDL platforms) + OIS::KeyCode kc = mInputWrapper->sdl2OISKeyCode(arg.keysym.sym); + if (mInputBinder->getKeyBinding(mInputBinder->getControl(A_Console), ICS::Control::INCREASE) + == arg.keysym.scancode + && MWBase::Environment::get().getWindowManager()->isConsoleMode()) + SDL_StopTextInput(); + + bool consumed = false; + if (kc != OIS::KC_UNASSIGNED && !mInputBinder->detectingBindingState()) + { + consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Enum(kc), 0, arg.repeat); + if (SDL_IsTextInputActive() && // Little trick to check if key is printable + ( !(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym))) + consumed = true; + MWBase::Environment::get().getInputManager()->setJoystickLastUsed(!consumed); + } + if (arg.repeat) + return; + + if (!mControlsDisabled && !consumed) + mInputBinder->keyPressed(arg); + MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); + } + + void KeyboardManager::keyReleased(const SDL_KeyboardEvent &arg) + { + MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); + OIS::KeyCode kc = mInputWrapper->sdl2OISKeyCode(arg.keysym.sym); + + if (!mInputBinder->detectingBindingState()) + MWBase::Environment::get().getInputManager()->setJoystickLastUsed(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); + mInputBinder->keyReleased(arg); + } +} diff --git a/apps/openmw/mwinput/keyboardmanager.hpp b/apps/openmw/mwinput/keyboardmanager.hpp new file mode 100644 index 000000000..590241bc7 --- /dev/null +++ b/apps/openmw/mwinput/keyboardmanager.hpp @@ -0,0 +1,45 @@ +#ifndef MWINPUT_MWKEYBOARDMANAGER_H +#define MWINPUT_MWKEYBOARDMANAGER_H + +#include +#include + +namespace SDLUtil +{ + class InputWrapper; +} + +namespace ICS +{ + class InputControlSystem; +} + +namespace MWInput +{ + class ActionManager; + + class KeyboardManager : public SDLUtil::KeyListener + { + public: + KeyboardManager(ICS::InputControlSystem* inputBinder, SDLUtil::InputWrapper* inputWrapper, ActionManager* actionManager); + + virtual ~KeyboardManager() = default; + + bool update(float dt, bool disableControls); + + virtual void textInput(const SDL_TextInputEvent &arg); + virtual void keyPressed(const SDL_KeyboardEvent &arg); + virtual void keyReleased(const SDL_KeyboardEvent &arg); + + private: + bool actionIsActive(int id); + + ICS::InputControlSystem* mInputBinder; + SDLUtil::InputWrapper* mInputWrapper; + + ActionManager* mActionManager; + + bool mControlsDisabled; + }; +} +#endif diff --git a/apps/openmw/mwinput/sensormanager.hpp b/apps/openmw/mwinput/sensormanager.hpp index d655e9c07..51225e381 100644 --- a/apps/openmw/mwinput/sensormanager.hpp +++ b/apps/openmw/mwinput/sensormanager.hpp @@ -31,7 +31,6 @@ namespace MWInput bool update(float dt, bool isCursorEnabled, bool isTurningEnabled); - public: virtual void sensorUpdated(const SDL_SensorEvent &arg); virtual void displayOrientationChanged(); void processChangedSettings(const Settings::CategorySettingVector& changed); @@ -48,6 +47,11 @@ namespace MWInput Minus_Z = -3 }; + void updateSensors(); + void correctGyroscopeAxes(); + GyroscopeAxis mapGyroscopeAxis(const std::string& axis); + float getGyroAxisSpeed(GyroscopeAxis axis, const SDL_SensorEvent &arg) const; + bool mInvertX; bool mInvertY; @@ -61,12 +65,6 @@ namespace MWInput GyroscopeAxis mGyroVAxis; float mGyroInputThreshold; - private: - - void updateSensors(); - void correctGyroscopeAxes(); - GyroscopeAxis mapGyroscopeAxis(const std::string& axis); - float getGyroAxisSpeed(GyroscopeAxis axis, const SDL_SensorEvent &arg) const; SDL_Sensor* mGyroscope; }; } From 13b7c5b5196ae4e57cec6a587982714b25f3c1a1 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 16 Apr 2020 16:36:32 +0400 Subject: [PATCH 053/227] Rework actions update --- apps/openmw/mwinput/actionmanager.cpp | 154 ++++++++++++++++++++++ apps/openmw/mwinput/actionmanager.hpp | 17 +++ apps/openmw/mwinput/controllermanager.cpp | 10 +- apps/openmw/mwinput/controllermanager.hpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 108 ++------------- apps/openmw/mwinput/inputmanagerimp.hpp | 10 -- apps/openmw/mwinput/keyboardmanager.cpp | 63 +-------- apps/openmw/mwinput/keyboardmanager.hpp | 2 +- apps/openmw/mwinput/mousemanager.cpp | 8 +- apps/openmw/mwinput/mousemanager.hpp | 2 +- apps/openmw/mwinput/sensormanager.cpp | 13 +- apps/openmw/mwinput/sensormanager.hpp | 2 +- 12 files changed, 200 insertions(+), 191 deletions(-) diff --git a/apps/openmw/mwinput/actionmanager.cpp b/apps/openmw/mwinput/actionmanager.cpp index 22c7d96a6..2240e7556 100644 --- a/apps/openmw/mwinput/actionmanager.cpp +++ b/apps/openmw/mwinput/actionmanager.cpp @@ -40,9 +40,163 @@ namespace MWInput , mScreenCaptureOperation(screenCaptureOperation) , mAlwaysRunActive(Settings::Manager::getBool("always run", "Input")) , mSneaking(false) + , mAttemptJump(false) + , mOverencumberedMessageDelay(0.f) + , mPreviewPOVDelay(0.f) + , mTimeIdle(0.f) { } + void ActionManager::update(float dt, bool triedToMove) + { + // Disable movement in Gui mode + if (MWBase::Environment::get().getWindowManager()->isGuiMode() + || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running) + { + mAttemptJump = false; + return; + } + + // Configure player movement according to keyboard input. Actual movement will + // be done in the physics system. + if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + { + bool alwaysRunAllowed = false; + + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + + if (actionIsActive(A_MoveLeft) != actionIsActive(A_MoveRight)) + { + alwaysRunAllowed = true; + triedToMove = true; + player.setLeftRight (actionIsActive(A_MoveRight) ? 1 : -1); + } + + if (actionIsActive(A_MoveForward) != actionIsActive(A_MoveBackward)) + { + alwaysRunAllowed = true; + triedToMove = true; + player.setAutoMove (false); + player.setForwardBackward (actionIsActive(A_MoveForward) ? 1 : -1); + } + + if (player.getAutoMove()) + { + alwaysRunAllowed = true; + triedToMove = true; + player.setForwardBackward (1); + } + + if (mAttemptJump && MWBase::Environment::get().getInputManager()->getControlSwitch("playerjumping")) + { + player.setUpDown(1); + triedToMove = true; + mOverencumberedMessageDelay = 0.f; + } + + // if player tried to start moving, but can't (due to being overencumbered), display a notification. + if (triedToMove) + { + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); + mOverencumberedMessageDelay -= dt; + if (playerPtr.getClass().getEncumbrance(playerPtr) > playerPtr.getClass().getCapacity(playerPtr)) + { + player.setAutoMove (false); + if (mOverencumberedMessageDelay <= 0) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage59}"); + mOverencumberedMessageDelay = 1.0; + } + } + } + + if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch")) + { + if (actionIsActive(A_TogglePOV)) + { + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += dt) > 0.5) + { + mPreviewPOVDelay = 1.f; + MWBase::Environment::get().getWorld()->togglePreviewMode(true); + } + } + else + { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5) + { + MWBase::Environment::get().getWorld()->togglePOV(); + } + mPreviewPOVDelay = 0.f; + } + } + + if (triedToMove) + MWBase::Environment::get().getInputManager()->resetIdleTime(); + + static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); + if (!isToggleSneak) + { + if(!MWBase::Environment::get().getInputManager()->joystickLastUsed()) + player.setSneak(actionIsActive(A_Sneak)); + } + + float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue(); + float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue(); + bool isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25; + if ((mAlwaysRunActive && alwaysRunAllowed) || isRunning) + player.setRunState(!actionIsActive(A_Run)); + else + player.setRunState(actionIsActive(A_Run)); + } + + if (actionIsActive(A_MoveForward) || + actionIsActive(A_MoveBackward) || + actionIsActive(A_MoveLeft) || + actionIsActive(A_MoveRight) || + actionIsActive(A_Jump) || + actionIsActive(A_Sneak) || + actionIsActive(A_TogglePOV) || + actionIsActive(A_ZoomIn) || + actionIsActive(A_ZoomOut)) + { + resetIdleTime(); + } + else + { + updateIdleTime(dt); + } + + mAttemptJump = false; + } + + void ActionManager::resetIdleTime() + { + if (mTimeIdle < 0) + MWBase::Environment::get().getWorld()->toggleVanityMode(false); + mTimeIdle = 0.f; + } + + void ActionManager::updateIdleTime(float dt) + { + static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get() + .find("fVanityDelay")->mValue.getFloat(); + if (mTimeIdle >= 0.f) + mTimeIdle += dt; + if (mTimeIdle > vanityDelay) + { + MWBase::Environment::get().getWorld()->toggleVanityMode(true); + mTimeIdle = -1.f; + } + } + + bool ActionManager::actionIsActive(int id) + { + return (mInputBinder->getChannel(id)->getValue ()==1.0); + } + void ActionManager::executeAction(int action) { // trigger action activated diff --git a/apps/openmw/mwinput/actionmanager.hpp b/apps/openmw/mwinput/actionmanager.hpp index 7c1fe3f70..399cb318c 100644 --- a/apps/openmw/mwinput/actionmanager.hpp +++ b/apps/openmw/mwinput/actionmanager.hpp @@ -28,6 +28,8 @@ namespace MWInput void clear(); + void update(float dt, bool triedToMove); + void executeAction(int action); bool checkAllowedToUseItems() const; @@ -50,12 +52,22 @@ namespace MWInput void quickKey (int index); void showQuickKeysMenu(); + void resetIdleTime(); + bool isAlwaysRunActive() const { return mAlwaysRunActive; }; bool isSneaking() const { return mSneaking; }; + void setAttemptJump(bool enabled) { mAttemptJump = enabled; } + + float getPreviewDelay() const { return mPreviewPOVDelay; }; + private: void handleGuiArrowKey(int action); + bool actionIsActive(int id); + + void updateIdleTime(float dt); + ICS::InputControlSystem* mInputBinder; osg::ref_ptr mViewer; osg::ref_ptr mScreenCaptureHandler; @@ -63,6 +75,11 @@ namespace MWInput bool mAlwaysRunActive; bool mSneaking; + bool mAttemptJump; + + float mOverencumberedMessageDelay; + float mPreviewPOVDelay; + float mTimeIdle; }; } #endif diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 209800bd6..697d3ab91 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -94,10 +94,10 @@ namespace MWInput return (mInputBinder->getChannel (id)->getValue ()==1.0); } - bool ControllerManager::update(float dt, bool disableControls, bool gamepadPreviewMode) + bool ControllerManager::update(float dt, bool disableControls) { mControlsDisabled = disableControls; - mGamepadPreviewMode = gamepadPreviewMode; + mGamepadPreviewMode = mActionManager->getPreviewDelay() == 1.f; if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled)) { @@ -153,10 +153,10 @@ namespace MWInput } if (triedToMove) + { mJoystickLastUsed = true; - - if (triedToMove) MWBase::Environment::get().getInputManager()->resetIdleTime(); + } static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); if (!isToggleSneak) @@ -208,7 +208,7 @@ namespace MWInput return triedToMove; } - void ControllerManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg ) + void ControllerManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg) { if (!mJoystickEnabled || mInputBinder->detectingBindingState()) return; diff --git a/apps/openmw/mwinput/controllermanager.hpp b/apps/openmw/mwinput/controllermanager.hpp index 77b68be54..1ab6ea76d 100644 --- a/apps/openmw/mwinput/controllermanager.hpp +++ b/apps/openmw/mwinput/controllermanager.hpp @@ -31,7 +31,7 @@ namespace MWInput void clear(); - bool update(float dt, bool disableControls, bool gamepadPreviewMode); + bool update(float dt, bool disableControls); virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg); virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index d176ba480..815283ba8 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -46,17 +46,12 @@ namespace MWInput osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation, const std::string& userFile, bool userFileExists, const std::string& userControllerBindingsFile, const std::string& controllerBindingsFile, bool grab) - : mWindow(window) - , mWindowVisible(true) + : mWindowVisible(true) , mUserFile(userFile) , mDragDrop(false) - , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) - , mPreviewPOVDelay(0.f) - , mTimeIdle(0.f) + , mGrabCursor(Settings::Manager::getBool("grab cursor", "Input")) , mGuiCursorEnabled(true) , mDetectingKeyboard(false) - , mOverencumberedMessageDelay(0.f) - , mAttemptJump(false) , mFakeDeviceID(1) { mInputWrapper = new SDLUtil::InputWrapper(window, viewer, grab); @@ -204,7 +199,7 @@ namespace MWInput action = A_CycleSpellLeft; else - mAttemptJump = (currentValue == 1.0 && previousValue == 0.0); + mActionManager->setAttemptJump(currentValue == 1.0 && previousValue == 0.0); } } @@ -252,82 +247,11 @@ namespace MWInput updateCursorMode(); - bool controllerMove = mControllerManager->update(dt, disableControls, mPreviewPOVDelay == 1.f); - bool keyboardMove = mKeyboardManager->update(dt, disableControls); - - if (mMouseManager->update(dt, disableControls)) - resetIdleTime(); - - if (mSensorManager->update(dt, mGuiCursorEnabled, mControlSwitch["playerlooking"])) - resetIdleTime(); - - // Disable movement in Gui mode - if (!(MWBase::Environment::get().getWindowManager()->isGuiMode() - || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)) - { - if (mControlSwitch["playercontrols"]) - { - MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); - - bool attemptToJump = false; - if (mAttemptJump && mControlSwitch["playerjumping"]) - { - player.setUpDown(1); - attemptToJump = true; - mOverencumberedMessageDelay = 0.f; - } - - // if player tried to start moving, but can't (due to being overencumbered), display a notification. - if (controllerMove || keyboardMove || attemptToJump) - { - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - mOverencumberedMessageDelay -= dt; - if (playerPtr.getClass().getEncumbrance(playerPtr) > playerPtr.getClass().getCapacity(playerPtr)) - { - player.setAutoMove (false); - if (mOverencumberedMessageDelay <= 0) - { - MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage59}"); - mOverencumberedMessageDelay = 1.0; - } - } - } - - if (mControlSwitch["playerviewswitch"]) { - - if (actionIsActive(A_TogglePOV)) { - if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += dt) > 0.5) - { - mPreviewPOVDelay = 1.f; - MWBase::Environment::get().getWorld()->togglePreviewMode(true); - } - } else { - //disable preview mode - MWBase::Environment::get().getWorld()->togglePreviewMode(false); - if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5) { - MWBase::Environment::get().getWorld()->togglePOV(); - } - mPreviewPOVDelay = 0.f; - } - } - } - if (actionIsActive(A_MoveForward) || - actionIsActive(A_MoveBackward) || - actionIsActive(A_MoveLeft) || - actionIsActive(A_MoveRight) || - actionIsActive(A_Jump) || - actionIsActive(A_Sneak) || - actionIsActive(A_TogglePOV) || - actionIsActive(A_ZoomIn) || - actionIsActive(A_ZoomOut) ) - { - resetIdleTime(); - } else { - updateIdleTime(dt); - } - } - mAttemptJump = false; // Can only jump on first frame input is on + bool controllerMove = mControllerManager->update(dt, disableControls); + mKeyboardManager->update(dt, disableControls); + mMouseManager->update(dt, disableControls); + mSensorManager->update(dt, mGuiCursorEnabled); + mActionManager->update(dt, controllerMove); } void InputManager::setDragDrop(bool dragDrop) @@ -453,21 +377,7 @@ namespace MWInput void InputManager::resetIdleTime() { - if (mTimeIdle < 0) - MWBase::Environment::get().getWorld()->toggleVanityMode(false); - mTimeIdle = 0.f; - } - - void InputManager::updateIdleTime(float dt) - { - static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get() - .find("fVanityDelay")->mValue.getFloat(); - if (mTimeIdle >= 0.f) - mTimeIdle += dt; - if (mTimeIdle > vanityDelay) { - MWBase::Environment::get().getWorld()->toggleVanityMode(true); - mTimeIdle = -1.f; - } + mActionManager->resetIdleTime(); } bool InputManager::actionIsActive (int id) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index ff0c43a82..fc01fecab 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -146,7 +146,6 @@ namespace MWInput virtual void resetIdleTime(); private: - SDL_Window* mWindow; bool mWindowVisible; ICS::InputControlSystem* mInputBinder; @@ -160,17 +159,10 @@ namespace MWInput bool mGrabCursor; - float mPreviewPOVDelay; - float mTimeIdle; - bool mGuiCursorEnabled; bool mDetectingKeyboard; - float mOverencumberedMessageDelay; - - bool mAttemptJump; - std::map mControlSwitch; ActionManager* mActionManager; @@ -181,8 +173,6 @@ namespace MWInput void convertMousePosForMyGUI(int& x, int& y); - void updateIdleTime(float dt); - void handleGuiArrowKey(int action); void updateCursorMode(); diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index f56246716..d043f8137 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -27,70 +27,9 @@ namespace MWInput { } - bool KeyboardManager::update(float dt, bool disableControls) + void KeyboardManager::update(float dt, bool disableControls) { mControlsDisabled = disableControls; - - // Disable movement in Gui mode - if (MWBase::Environment::get().getWindowManager()->isGuiMode() - || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running) - { - return false; - } - - // Configure player movement according to keyboard input. Actual movement will - // be done in the physics system. - if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) - { - return false; - } - - bool triedToMove = false; - bool alwaysRunAllowed = false; - - MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); - - if (actionIsActive(A_MoveLeft) != actionIsActive(A_MoveRight)) - { - alwaysRunAllowed = true; - triedToMove = true; - player.setLeftRight (actionIsActive(A_MoveRight) ? 1 : -1); - } - - if (actionIsActive(A_MoveForward) != actionIsActive(A_MoveBackward)) - { - alwaysRunAllowed = true; - triedToMove = true; - player.setAutoMove (false); - player.setForwardBackward (actionIsActive(A_MoveForward) ? 1 : -1); - } - - if (player.getAutoMove()) - { - alwaysRunAllowed = true; - triedToMove = true; - player.setForwardBackward (1); - } - - if (triedToMove) - MWBase::Environment::get().getInputManager()->resetIdleTime(); - - static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); - if (!isToggleSneak) - { - if(!MWBase::Environment::get().getInputManager()->joystickLastUsed()) - player.setSneak(actionIsActive(A_Sneak)); - } - - float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue(); - float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue(); - bool isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25; - if ((mActionManager->isAlwaysRunActive() && alwaysRunAllowed) || isRunning) - player.setRunState(!actionIsActive(A_Run)); - else - player.setRunState(actionIsActive(A_Run)); - - return triedToMove; } bool KeyboardManager::actionIsActive (int id) diff --git a/apps/openmw/mwinput/keyboardmanager.hpp b/apps/openmw/mwinput/keyboardmanager.hpp index 590241bc7..55b91bac4 100644 --- a/apps/openmw/mwinput/keyboardmanager.hpp +++ b/apps/openmw/mwinput/keyboardmanager.hpp @@ -25,7 +25,7 @@ namespace MWInput virtual ~KeyboardManager() = default; - bool update(float dt, bool disableControls); + void update(float dt, bool disableControls); virtual void textInput(const SDL_TextInputEvent &arg); virtual void keyPressed(const SDL_KeyboardEvent &arg); diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index b34124773..3fbfc7813 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -173,17 +173,17 @@ namespace MWInput mInputBinder->mousePressed (arg, id); } - bool MouseManager::update(float dt, bool disableControls) + void MouseManager::update(float dt, bool disableControls) { mControlsDisabled = disableControls; if (!mMouseLookEnabled) - return false; + return; float xAxis = mInputBinder->getChannel(A_LookLeftRight)->getValue()*2.0f-1.0f; float yAxis = mInputBinder->getChannel(A_LookUpDown)->getValue()*2.0f-1.0f; if (xAxis == 0 && yAxis == 0) - return false; + return; float rot[3]; rot[0] = yAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; @@ -198,7 +198,7 @@ namespace MWInput player.pitch(rot[0]); } - return true; + MWBase::Environment::get().getInputManager()->resetIdleTime(); } bool MouseManager::injectMouseButtonPress(Uint8 button) diff --git a/apps/openmw/mwinput/mousemanager.hpp b/apps/openmw/mwinput/mousemanager.hpp index f4517da47..6e59a639b 100644 --- a/apps/openmw/mwinput/mousemanager.hpp +++ b/apps/openmw/mwinput/mousemanager.hpp @@ -30,7 +30,7 @@ namespace MWInput void clear(); - bool update(float dt, bool disableControls); + void update(float dt, bool disableControls); virtual void mouseMoved(const SDLUtil::MouseMotionEvent &arg); virtual void mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id); diff --git a/apps/openmw/mwinput/sensormanager.cpp b/apps/openmw/mwinput/sensormanager.cpp index 55a0882f5..27879d214 100644 --- a/apps/openmw/mwinput/sensormanager.cpp +++ b/apps/openmw/mwinput/sensormanager.cpp @@ -3,6 +3,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" @@ -234,10 +235,10 @@ namespace MWInput } } - bool SensorManager::update(float dt, bool isCursorEnabled, bool isTurningEnabled) + void SensorManager::update(float dt, bool isCursorEnabled) { if (mGyroXSpeed == 0.f && mGyroYSpeed == 0.f) - return false; + return; if (mGyroUpdateTimer > 0.5f) { @@ -246,7 +247,7 @@ namespace MWInput // Reset current rotation speed and wait for update. clear(); mGyroUpdateTimer = 0.f; - return false; + return; } mGyroUpdateTimer += dt; @@ -259,16 +260,14 @@ namespace MWInput rot[2] = mGyroXSpeed * dt * mGyroHSensitivity * 4 * (mInvertX ? -1 : 1); // Only actually turn player when we're not in vanity mode - if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && isTurningEnabled) + if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && MWBase::Environment::get().getInputManager()->getControlSwitch("playerlooking")) { MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); player.yaw(rot[2]); player.pitch(rot[0]); } - return true; + MWBase::Environment::get().getInputManager()->resetIdleTime(); } - - return false; } } diff --git a/apps/openmw/mwinput/sensormanager.hpp b/apps/openmw/mwinput/sensormanager.hpp index 51225e381..283a53c1f 100644 --- a/apps/openmw/mwinput/sensormanager.hpp +++ b/apps/openmw/mwinput/sensormanager.hpp @@ -29,7 +29,7 @@ namespace MWInput void clear(); - bool update(float dt, bool isCursorEnabled, bool isTurningEnabled); + void update(float dt, bool isCursorEnabled); virtual void sensorUpdated(const SDL_SensorEvent &arg); virtual void displayOrientationChanged(); From f990150c497483a099adfcf44192076e31d72d37 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 16 Apr 2020 17:31:20 +0400 Subject: [PATCH 054/227] Move video wrapper to the WindowsManager --- apps/openmw/engine.cpp | 10 ++-- apps/openmw/mwbase/inputmanager.hpp | 2 - apps/openmw/mwbase/windowmanager.hpp | 11 ++-- apps/openmw/mwgui/windowmanagerimp.cpp | 59 ++++++++++++++++++-- apps/openmw/mwgui/windowmanagerimp.hpp | 14 ++++- apps/openmw/mwinput/inputmanagerimp.cpp | 72 ++----------------------- apps/openmw/mwinput/inputmanagerimp.hpp | 12 ----- components/sdlutil/events.hpp | 3 -- components/sdlutil/sdlinputwrapper.cpp | 5 -- 9 files changed, 84 insertions(+), 104 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index efb3c01a3..0fd6a1cb5 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -96,7 +96,7 @@ bool OMW::Engine::frame(float frametime) // When the window is minimized, pause the game. Currently this *has* to be here to work around a MyGUI bug. // If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures (fixed in MyGUI 3.3.2), // and destroyed widgets will not be deleted (not fixed yet, https://github.com/MyGUI/mygui/issues/21) - if (!mEnvironment.getInputManager()->isWindowVisible()) + if (!mEnvironment.getWindowManager()->isWindowVisible()) { mEnvironment.getSoundManager()->pausePlayback(); return false; @@ -532,20 +532,20 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) else gameControllerdb = ""; //if it doesn't exist, pass in an empty string - MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab); - mEnvironment.setInputManager (input); - std::string myguiResources = (mResDir / "mygui").string(); osg::ref_ptr guiRoot = new osg::Group; guiRoot->setName("GUI Root"); guiRoot->setNodeMask(MWRender::Mask_GUI); rootNode->addChild(guiRoot); - MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(), + MWGui::WindowManager* window = new MWGui::WindowManager(mWindow, mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(), mCfgMgr.getLogPath().string() + std::string("/"), myguiResources, mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string()); mEnvironment.setWindowManager (window); + MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab); + mEnvironment.setInputManager (input); + // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound)); diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 29c876e2b..601dd90e5 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -38,8 +38,6 @@ namespace MWBase virtual ~InputManager() {} - virtual bool isWindowVisible() = 0; - virtual void update(float dt, bool disableControls, bool disableEvents=false) = 0; virtual void changeInputMode(bool guiMode) = 0; diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index e8ae61fe5..2639fe590 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -11,6 +11,8 @@ #include "../mwgui/mode.hpp" +#include + namespace Loading { class Listener; @@ -86,7 +88,7 @@ namespace SFO namespace MWBase { /// \brief Interface for widnow manager (implemented in MWGui) - class WindowManager + class WindowManager : public SDLUtil::WindowListener { WindowManager (const WindowManager&); ///< not implemented @@ -268,8 +270,6 @@ namespace MWBase virtual void processChangedSettings(const std::set< std::pair >& changed) = 0; - virtual void windowResized(int x, int y) = 0; - virtual void executeInConsole (const std::string& path) = 0; virtual void enableRest() = 0; @@ -360,6 +360,11 @@ namespace MWBase virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) = 0; virtual bool injectKeyRelease(MyGUI::KeyCode key) = 0; + + virtual void windowVisibilityChange(bool visible) = 0; + virtual void windowResized(int x, int y) = 0; + virtual void windowClosed() = 0; + virtual bool isWindowVisible() = 0; }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 28521ac6f..21e84613c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -127,7 +128,7 @@ namespace MWGui { WindowManager::WindowManager( - osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, + SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, const std::string& userDataPath) : mOldUpdateMask(0) @@ -196,6 +197,7 @@ namespace MWGui , mEncoding(encoding) , mFontHeight(16) , mVersionDescription(versionDescription) + , mWindowVisible(true) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale); @@ -288,6 +290,10 @@ namespace MWGui MyGUI::ClipboardManager::getInstance().eventClipboardRequested += MyGUI::newDelegate(this, &WindowManager::onClipboardRequested); mShowOwned = Settings::Manager::getInt("show owned", "Game"); + + mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer); + mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"), + Settings::Manager::getFloat("contrast", "Video")); } void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) @@ -653,6 +659,7 @@ namespace MWGui mGuiPlatform->shutdown(); delete mGuiPlatform; + delete mVideoWrapper; } catch(const MyGUI::Exception& e) { @@ -916,7 +923,7 @@ namespace MWGui mMessageBoxManager->onFrame(dt); MWBase::Environment::get().getInputManager()->update(dt, true, false); - if (!MWBase::Environment::get().getInputManager()->isWindowVisible()) + if (!mWindowVisible) OpenThreads::Thread::microSleep(5000); else { @@ -1241,6 +1248,7 @@ namespace MWGui { mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); + bool changeRes = false; for (const auto& setting : changed) { if (setting.first == "HUD" && setting.second == "crosshair") @@ -1249,11 +1257,38 @@ namespace MWGui mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); else if (setting.first == "GUI" && setting.second == "menu transparency") setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); + else if (setting.first == "Video" && ( + setting.second == "resolution x" + || setting.second == "resolution y" + || setting.second == "fullscreen" + || setting.second == "window border")) + changeRes = true; + + else if (setting.first == "Video" && setting.second == "vsync") + mVideoWrapper->setSyncToVBlank(Settings::Manager::getBool("vsync", "Video")); + else if (setting.first == "Video" && (setting.second == "gamma" || setting.second == "contrast")) + mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"), + Settings::Manager::getFloat("contrast", "Video")); + } + + if (changeRes) + { + mVideoWrapper->setVideoMode(Settings::Manager::getInt("resolution x", "Video"), + Settings::Manager::getInt("resolution y", "Video"), + Settings::Manager::getBool("fullscreen", "Video"), + Settings::Manager::getBool("window border", "Video")); } } void WindowManager::windowResized(int x, int y) { + // Note: this is a side effect of resolution change or window resize. + // There is no need to track these changes. + Settings::Manager::setInt("resolution x", "Video", x); + Settings::Manager::setInt("resolution y", "Video", y); + Settings::Manager::resetPendingChange("resolution x", "Video"); + Settings::Manager::resetPendingChange("resolution y", "Video"); + mGuiPlatform->getRenderManagerPtr()->setViewSize(x, y); // scaled size @@ -1283,9 +1318,27 @@ namespace MWGui for (WindowBase* window : mWindows) window->onResChange(x, y); + // We should reload TrueType fonts to fit new resolution + loadUserFonts(); + // TODO: check if any windows are now off-screen and move them back if so } + bool WindowManager::isWindowVisible() + { + return mWindowVisible; + } + + void WindowManager::windowVisibilityChange(bool visible) + { + mWindowVisible = visible; + } + + void WindowManager::windowClosed() + { + MWBase::Environment::get().getStateManager()->requestQuit(); + } + void WindowManager::onCursorChange(const std::string &name) { mCursorManager->cursorChanged(name); @@ -1925,7 +1978,7 @@ namespace MWGui MWBase::Environment::get().getInputManager()->update(dt, true, false); - if (!MWBase::Environment::get().getInputManager()->isWindowVisible()) + if (!mWindowVisible) { mVideoWidget->pause(); OpenThreads::Thread::microSleep(5000); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 4e90f2e93..8b16cf25f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -15,6 +15,7 @@ #include "../mwworld/ptr.hpp" +#include #include #include @@ -70,6 +71,7 @@ namespace SceneUtil namespace SDLUtil { class SDLCursorManager; + class VideoWrapper; } namespace osgMyGUI @@ -124,13 +126,14 @@ namespace MWGui class JailScreen; class KeyboardNavigation; - class WindowManager : public MWBase::WindowManager + class WindowManager : + public MWBase::WindowManager { public: typedef std::pair Faction; typedef std::vector FactionList; - WindowManager(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, + WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, const std::string& localPath); virtual ~WindowManager(); @@ -296,7 +299,10 @@ namespace MWGui virtual void processChangedSettings(const Settings::CategorySettingVector& changed); + virtual void windowVisibilityChange(bool visible); virtual void windowResized(int x, int y); + virtual void windowClosed(); + virtual bool isWindowVisible(); virtual void executeInConsole (const std::string& path); @@ -529,10 +535,14 @@ namespace MWGui std::string mVersionDescription; + bool mWindowVisible; + MWGui::TextColours mTextColours; std::unique_ptr mKeyboardNavigation; + SDLUtil::VideoWrapper* mVideoWrapper; + /** * Called when MyGUI tries to retrieve a tag's value. Tags must be denoted in #{tag} notation and will be replaced upon setting a user visible text/property. * Supported syntax: diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 815283ba8..96806845e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -46,8 +45,7 @@ namespace MWInput osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation, const std::string& userFile, bool userFileExists, const std::string& userControllerBindingsFile, const std::string& controllerBindingsFile, bool grab) - : mWindowVisible(true) - , mUserFile(userFile) + : mUserFile(userFile) , mDragDrop(false) , mGrabCursor(Settings::Manager::getBool("grab cursor", "Input")) , mGuiCursorEnabled(true) @@ -55,11 +53,7 @@ namespace MWInput , mFakeDeviceID(1) { mInputWrapper = new SDLUtil::InputWrapper(window, viewer, grab); - mInputWrapper->setWindowEventCallback(this); - - mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer); - mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"), - Settings::Manager::getFloat("contrast", "Video")); + mInputWrapper->setWindowEventCallback(MWBase::Environment::get().getWindowManager()); std::string file = userFileExists ? userFile : ""; mInputBinder = new ICS::InputControlSystem(file, true, this, nullptr, A_Last); @@ -109,23 +103,16 @@ namespace MWInput InputManager::~InputManager() { - mInputBinder->save (mUserFile); - delete mActionManager; delete mControllerManager; delete mKeyboardManager; delete mMouseManager; delete mSensorManager; + mInputBinder->save(mUserFile); delete mInputBinder; delete mInputWrapper; - delete mVideoWrapper; - } - - bool InputManager::isWindowVisible() - { - return mWindowVisible; } void InputManager::setPlayerControlsEnabled(bool enabled) @@ -278,35 +265,11 @@ namespace MWInput void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) { - bool changeRes = false; - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) { if (it->first == "Input" && it->second == "grab cursor") mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); - - if (it->first == "Video" && ( - it->second == "resolution x" - || it->second == "resolution y" - || it->second == "fullscreen" - || it->second == "window border")) - changeRes = true; - - if (it->first == "Video" && it->second == "vsync") - mVideoWrapper->setSyncToVBlank(Settings::Manager::getBool("vsync", "Video")); - - if (it->first == "Video" && (it->second == "gamma" || it->second == "contrast")) - mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"), - Settings::Manager::getFloat("contrast", "Video")); - } - - if (changeRes) - { - mVideoWrapper->setVideoMode(Settings::Manager::getInt("resolution x", "Video"), - Settings::Manager::getInt("resolution y", "Video"), - Settings::Manager::getBool("fullscreen", "Video"), - Settings::Manager::getBool("window border", "Video")); } mMouseManager->processChangedSettings(changed); @@ -346,35 +309,6 @@ namespace MWInput mControlSwitch[sw] = value; } - void InputManager::windowFocusChange(bool have_focus) - { - } - - void InputManager::windowVisibilityChange(bool visible) - { - mWindowVisible = visible; - } - - void InputManager::windowResized(int x, int y) - { - // Note: this is a side effect of resolution change or window resize. - // There is no need to track these changes. - Settings::Manager::setInt("resolution x", "Video", x); - Settings::Manager::setInt("resolution y", "Video", y); - Settings::Manager::resetPendingChange("resolution x", "Video"); - Settings::Manager::resetPendingChange("resolution y", "Video"); - - MWBase::Environment::get().getWindowManager()->windowResized(x, y); - - // We should reload TrueType fonts to fit new resolution - MWBase::Environment::get().getWindowManager()->loadUserFonts(); - } - - void InputManager::windowClosed() - { - MWBase::Environment::get().getStateManager()->requestQuit(); - } - void InputManager::resetIdleTime() { mActionManager->resetIdleTime(); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index fc01fecab..51561feac 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -51,7 +51,6 @@ namespace Files namespace SDLUtil { class InputWrapper; - class VideoWrapper; } struct SDL_Window; @@ -63,7 +62,6 @@ namespace MWInput */ class InputManager : public MWBase::InputManager, - public SDLUtil::WindowListener, public ICS::ChannelListener, public ICS::DetectingBindingListener { @@ -79,8 +77,6 @@ namespace MWInput virtual ~InputManager(); - virtual bool isWindowVisible(); - /// Clear all savegame-specific data virtual void clear(); @@ -109,11 +105,6 @@ namespace MWInput virtual void setJoystickLastUsed(bool enabled); virtual bool joystickLastUsed(); - virtual void windowVisibilityChange( bool visible ); - virtual void windowFocusChange( bool have_focus ); - virtual void windowResized (int x, int y); - virtual void windowClosed (); - virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control @@ -146,12 +137,9 @@ namespace MWInput virtual void resetIdleTime(); private: - bool mWindowVisible; - ICS::InputControlSystem* mInputBinder; SDLUtil::InputWrapper* mInputWrapper; - SDLUtil::VideoWrapper* mVideoWrapper; std::string mUserFile; diff --git a/components/sdlutil/events.hpp b/components/sdlutil/events.hpp index 4d400e5b8..a0dd11ace 100644 --- a/components/sdlutil/events.hpp +++ b/components/sdlutil/events.hpp @@ -79,9 +79,6 @@ public: /** @remarks The window's visibility changed */ virtual void windowVisibilityChange( bool visible ) {} - /** @remarks The window got / lost input focus */ - virtual void windowFocusChange( bool have_focus ) {} - virtual void windowClosed () {} virtual void windowResized (int x, int y) {} diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index 60997d281..3ea11c6d0 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -231,15 +231,10 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v case SDL_WINDOWEVENT_FOCUS_GAINED: mWindowHasFocus = true; updateMouseSettings(); - if (mWindowListener) - mWindowListener->windowFocusChange(true); - break; case SDL_WINDOWEVENT_FOCUS_LOST: mWindowHasFocus = false; updateMouseSettings(); - if (mWindowListener) - mWindowListener->windowFocusChange(false); break; case SDL_WINDOWEVENT_CLOSE: break; From 8512133bb1df6388b730c6f9ee518a67d9cafc8f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 16 Apr 2020 18:08:55 +0400 Subject: [PATCH 055/227] Move control switches to the separate file --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwinput/controlswitch.cpp | 58 +++++++++++++++++++++++++ apps/openmw/mwinput/controlswitch.hpp | 21 +++++++++ apps/openmw/mwinput/inputmanagerimp.cpp | 43 ++++-------------- apps/openmw/mwinput/inputmanagerimp.hpp | 3 +- 5 files changed, 90 insertions(+), 37 deletions(-) create mode 100644 apps/openmw/mwinput/controlswitch.cpp create mode 100644 apps/openmw/mwinput/controlswitch.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 97cc9b035..d907091bd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,7 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - actions actionmanager controllermanager inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager + actions actionmanager controllermanager controlswitch inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager ) add_openmw_dir (mwgui diff --git a/apps/openmw/mwinput/controlswitch.cpp b/apps/openmw/mwinput/controlswitch.cpp new file mode 100644 index 000000000..6ea51064f --- /dev/null +++ b/apps/openmw/mwinput/controlswitch.cpp @@ -0,0 +1,58 @@ +#include "controlswitch.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" + +namespace MWInput +{ + ControlSwitch::ControlSwitch() + { + clear(); + } + + void ControlSwitch::clear() + { + mSwitches["playercontrols"] = true; + mSwitches["playerfighting"] = true; + mSwitches["playerjumping"] = true; + mSwitches["playerlooking"] = true; + mSwitches["playermagic"] = true; + mSwitches["playerviewswitch"] = true; + mSwitches["vanitymode"] = true; + } + + bool ControlSwitch::get(const std::string& key) + { + return mSwitches[key]; + } + + void ControlSwitch::set(const std::string& key, bool value) + { + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + + /// \note 7 switches at all, if-else is relevant + if (key == "playercontrols" && !value) + { + player.setLeftRight(0); + player.setForwardBackward(0); + player.setAutoMove(false); + player.setUpDown(0); + } + else if (key == "playerjumping" && !value) + { + /// \fixme maybe crouching at this time + player.setUpDown(0); + } + else if (key == "vanitymode") + { + MWBase::Environment::get().getWorld()->allowVanityMode(value); + } + else if (key == "playerlooking" && !value) + { + MWBase::Environment::get().getWorld()->rotateObject(player.getPlayer(), 0.f, 0.f, 0.f); + } + mSwitches[key] = value; + } +} diff --git a/apps/openmw/mwinput/controlswitch.hpp b/apps/openmw/mwinput/controlswitch.hpp new file mode 100644 index 000000000..5fa475e77 --- /dev/null +++ b/apps/openmw/mwinput/controlswitch.hpp @@ -0,0 +1,21 @@ +#ifndef MWINPUT_CONTROLSWITCH_H +#define MWINPUT_CONTROLSWITCH_H + +#include + +namespace MWInput +{ + class ControlSwitch + { + public: + ControlSwitch(); + + bool get(const std::string& key); + void set(const std::string& key, bool value); + void clear(); + + private: + std::map mSwitches; + }; +} +#endif diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 96806845e..b9b9ecf62 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -31,6 +31,7 @@ #include "actionmanager.hpp" #include "controllermanager.hpp" +#include "controlswitch.hpp" #include "keyboardmanager.hpp" #include "mousemanager.hpp" #include "sdlmappings.hpp" @@ -66,13 +67,7 @@ namespace MWInput mInputBinder->getChannel (i)->addListener (this); } - mControlSwitch["playercontrols"] = true; - mControlSwitch["playerfighting"] = true; - mControlSwitch["playerjumping"] = true; - mControlSwitch["playerlooking"] = true; - mControlSwitch["playermagic"] = true; - mControlSwitch["playerviewswitch"] = true; - mControlSwitch["vanitymode"] = true; + mControlSwitch = new ControlSwitch(); mActionManager = new ActionManager(mInputBinder, screenCaptureOperation, viewer, screenCaptureHandler); @@ -92,8 +87,7 @@ namespace MWInput void InputManager::clear() { // Enable all controls - for (std::map::iterator it = mControlSwitch.begin(); it != mControlSwitch.end(); ++it) - it->second = true; + mControlSwitch->clear(); mActionManager->clear(); mControllerManager->clear(); @@ -109,6 +103,8 @@ namespace MWInput delete mMouseManager; delete mSensorManager; + delete mControlSwitch; + mInputBinder->save(mUserFile); delete mInputBinder; @@ -159,7 +155,7 @@ namespace MWInput return; } - if (mControlSwitch["playercontrols"]) + if (mControlSwitch->get("playercontrols")) { bool joystickUsed = mControllerManager->joystickLastUsed(); if (action == A_Use) @@ -278,35 +274,12 @@ namespace MWInput bool InputManager::getControlSwitch (const std::string& sw) { - return mControlSwitch[sw]; + return mControlSwitch->get(sw); } void InputManager::toggleControlSwitch (const std::string& sw, bool value) { - MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); - - /// \note 7 switches at all, if-else is relevant - if (sw == "playercontrols" && !value) - { - player.setLeftRight(0); - player.setForwardBackward(0); - player.setAutoMove(false); - player.setUpDown(0); - } - else if (sw == "playerjumping" && !value) - { - /// \fixme maybe crouching at this time - player.setUpDown(0); - } - else if (sw == "vanitymode") - { - MWBase::Environment::get().getWorld()->allowVanityMode(value); - } - else if (sw == "playerlooking" && !value) - { - MWBase::Environment::get().getWorld()->rotateObject(player.getPlayer(), 0.f, 0.f, 0.f); - } - mControlSwitch[sw] = value; + mControlSwitch->set(sw, value); } void InputManager::resetIdleTime() diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 51561feac..ac056809e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -21,6 +21,7 @@ namespace MWInput { + class ControlSwitch; class ActionManager; class ControllerManager; class KeyboardManager; @@ -151,7 +152,7 @@ namespace MWInput bool mDetectingKeyboard; - std::map mControlSwitch; + ControlSwitch* mControlSwitch; ActionManager* mActionManager; ControllerManager* mControllerManager; From 2f2b3173e35b190c1696856ec4ce685adf5e7444 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 16 Apr 2020 18:59:48 +0400 Subject: [PATCH 056/227] Fix copy-paste error --- apps/openmw/mwinput/keyboardmanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index d043f8137..9cca00441 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -63,7 +63,7 @@ namespace MWInput if (SDL_IsTextInputActive() && // Little trick to check if key is printable ( !(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym))) consumed = true; - MWBase::Environment::get().getInputManager()->setJoystickLastUsed(!consumed); + MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!consumed); } if (arg.repeat) return; @@ -79,7 +79,7 @@ namespace MWInput OIS::KeyCode kc = mInputWrapper->sdl2OISKeyCode(arg.keysym.sym); if (!mInputBinder->detectingBindingState()) - MWBase::Environment::get().getInputManager()->setJoystickLastUsed(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); + MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); mInputBinder->keyReleased(arg); } } From 0eb24da2e78b68d174ec7e51d1058f2ae6762f91 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 16 Apr 2020 19:41:55 +0400 Subject: [PATCH 057/227] Fix controls disabling --- apps/openmw/mwinput/inputmanagerimp.cpp | 3 +-- apps/openmw/mwinput/keyboardmanager.cpp | 5 ----- apps/openmw/mwinput/keyboardmanager.hpp | 4 ++-- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b9b9ecf62..f7f32b9a9 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -216,9 +216,9 @@ namespace MWInput void InputManager::update(float dt, bool disableControls, bool disableEvents) { mInputWrapper->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); - mInputWrapper->capture(disableEvents); + mKeyboardManager->setControlsDisabled(disableControls); if (disableControls) { updateCursorMode(); @@ -231,7 +231,6 @@ namespace MWInput updateCursorMode(); bool controllerMove = mControllerManager->update(dt, disableControls); - mKeyboardManager->update(dt, disableControls); mMouseManager->update(dt, disableControls); mSensorManager->update(dt, mGuiCursorEnabled); mActionManager->update(dt, controllerMove); diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index 9cca00441..773ea4028 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -27,11 +27,6 @@ namespace MWInput { } - void KeyboardManager::update(float dt, bool disableControls) - { - mControlsDisabled = disableControls; - } - bool KeyboardManager::actionIsActive (int id) { return (mInputBinder->getChannel(id)->getValue ()==1.0); diff --git a/apps/openmw/mwinput/keyboardmanager.hpp b/apps/openmw/mwinput/keyboardmanager.hpp index 55b91bac4..9d1c0b4fd 100644 --- a/apps/openmw/mwinput/keyboardmanager.hpp +++ b/apps/openmw/mwinput/keyboardmanager.hpp @@ -25,12 +25,12 @@ namespace MWInput virtual ~KeyboardManager() = default; - void update(float dt, bool disableControls); - virtual void textInput(const SDL_TextInputEvent &arg); virtual void keyPressed(const SDL_KeyboardEvent &arg); virtual void keyReleased(const SDL_KeyboardEvent &arg); + void setControlsDisabled(bool disabled) { mControlsDisabled = disabled; } + private: bool actionIsActive(int id); From b33c4c920cd92fe105786d91f02e6569c7ae1296 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 17 Apr 2020 15:21:23 +0400 Subject: [PATCH 058/227] Move all OICS handling to the separate file --- apps/openmw/CMakeLists.txt | 3 +- apps/openmw/mwbase/inputmanager.hpp | 5 +- apps/openmw/mwinput/actionmanager.cpp | 66 +- apps/openmw/mwinput/actionmanager.hpp | 13 +- apps/openmw/mwinput/bindingsmanager.cpp | 738 ++++++++++++++++++++++ apps/openmw/mwinput/bindingsmanager.hpp | 73 +++ apps/openmw/mwinput/controllermanager.cpp | 80 ++- apps/openmw/mwinput/controllermanager.hpp | 15 +- apps/openmw/mwinput/inputmanagerimp.cpp | 606 +----------------- apps/openmw/mwinput/inputmanagerimp.hpp | 56 +- apps/openmw/mwinput/keyboardmanager.cpp | 41 +- apps/openmw/mwinput/keyboardmanager.hpp | 13 +- apps/openmw/mwinput/mousemanager.cpp | 39 +- apps/openmw/mwinput/mousemanager.hpp | 16 +- apps/openmw/mwinput/sdlmappings.cpp | 140 ++++ apps/openmw/mwinput/sdlmappings.hpp | 6 +- apps/openmw/mwinput/sensormanager.cpp | 18 +- components/CMakeLists.txt | 2 +- components/sdlutil/OISCompat.hpp | 159 ----- components/sdlutil/sdlinputwrapper.cpp | 137 ---- components/sdlutil/sdlinputwrapper.hpp | 8 - 21 files changed, 1121 insertions(+), 1113 deletions(-) create mode 100644 apps/openmw/mwinput/bindingsmanager.cpp create mode 100644 apps/openmw/mwinput/bindingsmanager.hpp delete mode 100644 components/sdlutil/OISCompat.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index d907091bd..34824ea33 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,8 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - actions actionmanager controllermanager controlswitch inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager + actions actionmanager bindingsmanager controllermanager controlswitch + inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager ) add_openmw_dir (mwgui diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 601dd90e5..932463e97 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -46,6 +46,7 @@ namespace MWBase virtual void setDragDrop(bool dragDrop) = 0; virtual void setGamepadGuiCursorEnabled(bool enabled) = 0; + virtual void setAttemptJump(bool jumping) = 0; virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; virtual bool getControlSwitch (const std::string& sw) = 0; @@ -72,9 +73,9 @@ namespace MWBase virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) = 0; virtual void readRecord(ESM::ESMReader& reader, uint32_t type) = 0; - virtual void setPlayerControlsEnabled(bool enabled) = 0; - virtual void resetIdleTime() = 0; + + virtual void executeAction(int action) = 0; }; } diff --git a/apps/openmw/mwinput/actionmanager.cpp b/apps/openmw/mwinput/actionmanager.cpp index 2240e7556..e90a4ddaf 100644 --- a/apps/openmw/mwinput/actionmanager.cpp +++ b/apps/openmw/mwinput/actionmanager.cpp @@ -4,8 +4,6 @@ #include -#include - #include #include "../mwbase/inputmanager.hpp" @@ -24,17 +22,17 @@ #include "../mwmechanics/actorutil.hpp" #include "actions.hpp" +#include "bindingsmanager.hpp" namespace MWInput { const float ZOOM_SCALE = 120.f; /// Used for scrolling camera in and out - const int fakeDeviceID = 1; - ActionManager::ActionManager(ICS::InputControlSystem* inputBinder, + ActionManager::ActionManager(BindingsManager* bindingsManager, osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation, osg::ref_ptr viewer, osg::ref_ptr screenCaptureHandler) - : mInputBinder(inputBinder) + : mBindingsManager(bindingsManager) , mViewer(viewer) , mScreenCaptureHandler(screenCaptureHandler) , mScreenCaptureOperation(screenCaptureOperation) @@ -65,19 +63,19 @@ namespace MWInput MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); - if (actionIsActive(A_MoveLeft) != actionIsActive(A_MoveRight)) + if (mBindingsManager->actionIsActive(A_MoveLeft) != mBindingsManager->actionIsActive(A_MoveRight)) { alwaysRunAllowed = true; triedToMove = true; - player.setLeftRight (actionIsActive(A_MoveRight) ? 1 : -1); + player.setLeftRight(mBindingsManager->actionIsActive(A_MoveRight) ? 1 : -1); } - if (actionIsActive(A_MoveForward) != actionIsActive(A_MoveBackward)) + if (mBindingsManager->actionIsActive(A_MoveForward) != mBindingsManager->actionIsActive(A_MoveBackward)) { alwaysRunAllowed = true; triedToMove = true; player.setAutoMove (false); - player.setForwardBackward (actionIsActive(A_MoveForward) ? 1 : -1); + player.setForwardBackward(mBindingsManager->actionIsActive(A_MoveForward) ? 1 : -1); } if (player.getAutoMove()) @@ -112,7 +110,7 @@ namespace MWInput if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch")) { - if (actionIsActive(A_TogglePOV)) + if (mBindingsManager->actionIsActive(A_TogglePOV)) { if (mPreviewPOVDelay <= 0.5 && (mPreviewPOVDelay += dt) > 0.5) @@ -140,27 +138,27 @@ namespace MWInput if (!isToggleSneak) { if(!MWBase::Environment::get().getInputManager()->joystickLastUsed()) - player.setSneak(actionIsActive(A_Sneak)); + player.setSneak(mBindingsManager->actionIsActive(A_Sneak)); } - float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue(); - float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue(); + float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight); + float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward); bool isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25; if ((mAlwaysRunActive && alwaysRunAllowed) || isRunning) - player.setRunState(!actionIsActive(A_Run)); + player.setRunState(!mBindingsManager->actionIsActive(A_Run)); else - player.setRunState(actionIsActive(A_Run)); + player.setRunState(mBindingsManager->actionIsActive(A_Run)); } - if (actionIsActive(A_MoveForward) || - actionIsActive(A_MoveBackward) || - actionIsActive(A_MoveLeft) || - actionIsActive(A_MoveRight) || - actionIsActive(A_Jump) || - actionIsActive(A_Sneak) || - actionIsActive(A_TogglePOV) || - actionIsActive(A_ZoomIn) || - actionIsActive(A_ZoomOut)) + if (mBindingsManager->actionIsActive(A_MoveForward) || + mBindingsManager->actionIsActive(A_MoveBackward) || + mBindingsManager->actionIsActive(A_MoveLeft) || + mBindingsManager->actionIsActive(A_MoveRight) || + mBindingsManager->actionIsActive(A_Jump) || + mBindingsManager->actionIsActive(A_Sneak) || + mBindingsManager->actionIsActive(A_TogglePOV) || + mBindingsManager->actionIsActive(A_ZoomIn) || + mBindingsManager->actionIsActive(A_ZoomOut)) { resetIdleTime(); } @@ -192,11 +190,6 @@ namespace MWInput } } - bool ActionManager::actionIsActive(int id) - { - return (mInputBinder->getChannel(id)->getValue ()==1.0); - } - void ActionManager::executeAction(int action) { // trigger action activated @@ -321,17 +314,6 @@ namespace MWInput } } - bool isLeftOrRightButton(int action, ICS::InputControlSystem* ics, int deviceId, bool joystick) - { - int mouseBinding = ics->getMouseButtonBinding(ics->getControl(action), ICS::Control::INCREASE); - if (mouseBinding != ICS_MAX_DEVICE_BUTTONS) - return true; - int buttonBinding = ics->getJoystickButtonBinding(ics->getControl(action), deviceId, ICS::Control::INCREASE); - if (joystick && (buttonBinding == 0 || buttonBinding == 1)) - return true; - return false; - } - bool ActionManager::checkAllowedToUseItems() const { MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -559,7 +541,7 @@ namespace MWInput if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed(); - if (!SDL_IsTextInputActive() && !isLeftOrRightButton(A_Activate, mInputBinder, fakeDeviceID, joystickUsed)) + if (!SDL_IsTextInputActive() && !mBindingsManager->isLeftOrRightButton(A_Activate, joystickUsed)) MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0, false); } else if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) @@ -612,7 +594,7 @@ namespace MWInput if (SDL_IsTextInputActive()) return; - if (isLeftOrRightButton(action, mInputBinder, fakeDeviceID, joystickUsed)) + if (mBindingsManager->isLeftOrRightButton(action, joystickUsed)) return; MyGUI::KeyCode key; diff --git a/apps/openmw/mwinput/actionmanager.hpp b/apps/openmw/mwinput/actionmanager.hpp index 399cb318c..31fd993c9 100644 --- a/apps/openmw/mwinput/actionmanager.hpp +++ b/apps/openmw/mwinput/actionmanager.hpp @@ -4,11 +4,6 @@ #include #include -namespace ICS -{ - class InputControlSystem; -} - namespace osgViewer { class Viewer; @@ -17,11 +12,13 @@ namespace osgViewer namespace MWInput { + class BindingsManager; + class ActionManager { public: - ActionManager(ICS::InputControlSystem* inputBinder, + ActionManager(BindingsManager* bindingsManager, osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation, osg::ref_ptr viewer, osg::ref_ptr screenCaptureHandler); @@ -64,11 +61,9 @@ namespace MWInput private: void handleGuiArrowKey(int action); - bool actionIsActive(int id); - void updateIdleTime(float dt); - ICS::InputControlSystem* mInputBinder; + BindingsManager* mBindingsManager; osg::ref_ptr mViewer; osg::ref_ptr mScreenCaptureHandler; osgViewer::ScreenCaptureHandler::CaptureOperation* mScreenCaptureOperation; diff --git a/apps/openmw/mwinput/bindingsmanager.cpp b/apps/openmw/mwinput/bindingsmanager.cpp new file mode 100644 index 000000000..f871f0c51 --- /dev/null +++ b/apps/openmw/mwinput/bindingsmanager.cpp @@ -0,0 +1,738 @@ +#include "bindingsmanager.hpp" + +#include + +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" + +#include "actions.hpp" +#include "sdlmappings.hpp" + +namespace MWInput +{ + static const int sFakeDeviceId = 1; //As we only support one controller at a time, use a fake deviceID so we don't lose bindings when switching controllers + + void clearAllKeyBindings(ICS::InputControlSystem* inputBinder, ICS::Control* control) + { + // right now we don't really need multiple bindings for the same action, so remove all others first + if (inputBinder->getKeyBinding(control, ICS::Control::INCREASE) != SDL_SCANCODE_UNKNOWN) + inputBinder->removeKeyBinding(inputBinder->getKeyBinding(control, ICS::Control::INCREASE)); + if (inputBinder->getMouseButtonBinding(control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + inputBinder->removeMouseButtonBinding(inputBinder->getMouseButtonBinding(control, ICS::Control::INCREASE)); + if (inputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE) != ICS::InputControlSystem::MouseWheelClick::UNASSIGNED) + inputBinder->removeMouseWheelBinding(inputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE)); + } + + void clearAllControllerBindings(ICS::InputControlSystem* inputBinder, ICS::Control* control) + { + // right now we don't really need multiple bindings for the same action, so remove all others first + if (inputBinder->getJoystickAxisBinding(control, sFakeDeviceId, ICS::Control::INCREASE) != SDL_SCANCODE_UNKNOWN) + inputBinder->removeJoystickAxisBinding(sFakeDeviceId, inputBinder->getJoystickAxisBinding(control, sFakeDeviceId, ICS::Control::INCREASE)); + if (inputBinder->getJoystickButtonBinding(control, sFakeDeviceId, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + inputBinder->removeJoystickButtonBinding(sFakeDeviceId, inputBinder->getJoystickButtonBinding(control, sFakeDeviceId, ICS::Control::INCREASE)); + } + + class InputControlSystem : public ICS::InputControlSystem + { + public: + InputControlSystem(const std::string& bindingsFile) + : ICS::InputControlSystem(bindingsFile, true, nullptr, nullptr, A_Last) + { + } + }; + + class BindingsListener : + public ICS::ChannelListener, + public ICS::DetectingBindingListener + { + public: + BindingsListener(ICS::InputControlSystem* inputBinder, BindingsManager* bindingsManager) + : mInputBinder(inputBinder) + , mBindingsManager(bindingsManager) + , mDetectingKeyboard(false) + { + } + + virtual ~BindingsListener() = default; + + virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue) + { + int action = channel->getNumber(); + mBindingsManager->actionValueChanged(action, currentValue, previousValue); + } + + virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , SDL_Scancode key, ICS::Control::ControlChangingDirection direction) + { + //Disallow binding escape key + if(key==SDL_SCANCODE_ESCAPE) + { + //Stop binding if esc pressed + mInputBinder->cancelDetectingBindingState(); + MWBase::Environment::get().getWindowManager()->notifyInputActionBound(); + return; + } + + // Disallow binding reserved keys + if (key == SDL_SCANCODE_F3 || key == SDL_SCANCODE_F4 || key == SDL_SCANCODE_F10) + return; + + #ifndef __APPLE__ + // Disallow binding Windows/Meta keys + if (key == SDL_SCANCODE_LGUI || key == SDL_SCANCODE_RGUI) + return; + #endif + + if(!mDetectingKeyboard) + return; + + clearAllKeyBindings(mInputBinder, control); + control->setInitialValue(0.0f); + ICS::DetectingBindingListener::keyBindingDetected(ICS, control, key, direction); + MWBase::Environment::get().getWindowManager()->notifyInputActionBound(); + } + + virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction) + { + // we don't want mouse movement bindings + return; + } + + virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , unsigned int button, ICS::Control::ControlChangingDirection direction) + { + if(!mDetectingKeyboard) + return; + clearAllKeyBindings(mInputBinder, control); + control->setInitialValue(0.0f); + ICS::DetectingBindingListener::mouseButtonBindingDetected(ICS, control, button, direction); + MWBase::Environment::get().getWindowManager()->notifyInputActionBound(); + } + + virtual void mouseWheelBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , ICS::InputControlSystem::MouseWheelClick click, ICS::Control::ControlChangingDirection direction) + { + if(!mDetectingKeyboard) + return; + clearAllKeyBindings(mInputBinder, control); + control->setInitialValue(0.0f); + ICS::DetectingBindingListener::mouseWheelBindingDetected(ICS, control, click, direction); + MWBase::Environment::get().getWindowManager()->notifyInputActionBound(); + } + + virtual void joystickAxisBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control + , int axis, ICS::Control::ControlChangingDirection direction) + { + //only allow binding to the trigers + if(axis != SDL_CONTROLLER_AXIS_TRIGGERLEFT && axis != SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + return; + if(mDetectingKeyboard) + return; + + clearAllControllerBindings(mInputBinder, control); + control->setValue(0.5f); //axis bindings must start at 0.5 + control->setInitialValue(0.5f); + ICS::DetectingBindingListener::joystickAxisBindingDetected(ICS, deviceID, control, axis, direction); + MWBase::Environment::get().getWindowManager()->notifyInputActionBound(); + } + + virtual void joystickButtonBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control + , unsigned int button, ICS::Control::ControlChangingDirection direction) + { + if(mDetectingKeyboard) + return; + clearAllControllerBindings(mInputBinder,control); + control->setInitialValue(0.0f); + ICS::DetectingBindingListener::joystickButtonBindingDetected (ICS, deviceID, control, button, direction); + MWBase::Environment::get().getWindowManager()->notifyInputActionBound(); + } + + void setDetectingKeyboard(bool detecting) + { + mDetectingKeyboard = detecting; + } + + private: + ICS::InputControlSystem* mInputBinder; + BindingsManager* mBindingsManager; + bool mDetectingKeyboard; + }; + + BindingsManager::BindingsManager(const std::string& userFile, bool userFileExists) + : mUserFile(userFile) + , mDragDrop(false) + { + std::string file = userFileExists ? userFile : ""; + mInputBinder = new InputControlSystem(file); + mListener = new BindingsListener(mInputBinder, this); + mInputBinder->setDetectingBindingListener(mListener); + + loadKeyDefaults(); + loadControllerDefaults(); + + for (int i = 0; i < A_Last; ++i) + { + mInputBinder->getChannel(i)->addListener(mListener); + } + } + + void BindingsManager::setDragDrop(bool dragDrop) + { + mDragDrop = dragDrop; + } + + BindingsManager::~BindingsManager() + { + mInputBinder->save(mUserFile); + delete mInputBinder; + } + + void BindingsManager::update(float dt) + { + // update values of channels (as a result of pressed keys) + mInputBinder->update(dt); + } + + bool BindingsManager::isLeftOrRightButton(int action, bool joystick) const + { + int mouseBinding = mInputBinder->getMouseButtonBinding(mInputBinder->getControl(action), ICS::Control::INCREASE); + if (mouseBinding != ICS_MAX_DEVICE_BUTTONS) + return true; + int buttonBinding = mInputBinder->getJoystickButtonBinding(mInputBinder->getControl(action), sFakeDeviceId, ICS::Control::INCREASE); + if (joystick && (buttonBinding == 0 || buttonBinding == 1)) + return true; + return false; + } + + void BindingsManager::setPlayerControlsEnabled(bool enabled) + { + int playerChannels[] = {A_AutoMove, A_AlwaysRun, A_ToggleWeapon, + A_ToggleSpell, A_Rest, A_QuickKey1, A_QuickKey2, + A_QuickKey3, A_QuickKey4, A_QuickKey5, A_QuickKey6, + A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10, + A_Use, A_Journal}; + + for(size_t i = 0; i < sizeof(playerChannels)/sizeof(playerChannels[0]); i++) { + int pc = playerChannels[i]; + mInputBinder->getChannel(pc)->setEnabled(enabled); + } + } + + float BindingsManager::getActionValue (int id) const + { + return mInputBinder->getChannel(id)->getValue(); + } + + bool BindingsManager::actionIsActive (int id) const + { + return getActionValue(id) == 1.0; + } + + void BindingsManager::loadKeyDefaults (bool force) + { + // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid + // across different versions of OpenMW (in the case where another input action is added) + std::map defaultKeyBindings; + + //Gets the Keyvalue from the Scancode; gives the button in the same place reguardless of keyboard format + defaultKeyBindings[A_Activate] = SDL_SCANCODE_SPACE; + defaultKeyBindings[A_MoveBackward] = SDL_SCANCODE_S; + defaultKeyBindings[A_MoveForward] = SDL_SCANCODE_W; + defaultKeyBindings[A_MoveLeft] = SDL_SCANCODE_A; + defaultKeyBindings[A_MoveRight] = SDL_SCANCODE_D; + defaultKeyBindings[A_ToggleWeapon] = SDL_SCANCODE_F; + defaultKeyBindings[A_ToggleSpell] = SDL_SCANCODE_R; + defaultKeyBindings[A_CycleSpellLeft] = SDL_SCANCODE_MINUS; + defaultKeyBindings[A_CycleSpellRight] = SDL_SCANCODE_EQUALS; + defaultKeyBindings[A_CycleWeaponLeft] = SDL_SCANCODE_LEFTBRACKET; + defaultKeyBindings[A_CycleWeaponRight] = SDL_SCANCODE_RIGHTBRACKET; + + defaultKeyBindings[A_QuickKeysMenu] = SDL_SCANCODE_F1; + defaultKeyBindings[A_Console] = SDL_SCANCODE_GRAVE; + defaultKeyBindings[A_Run] = SDL_SCANCODE_LSHIFT; + defaultKeyBindings[A_Sneak] = SDL_SCANCODE_LCTRL; + defaultKeyBindings[A_AutoMove] = SDL_SCANCODE_Q; + defaultKeyBindings[A_Jump] = SDL_SCANCODE_E; + defaultKeyBindings[A_Journal] = SDL_SCANCODE_J; + defaultKeyBindings[A_Rest] = SDL_SCANCODE_T; + defaultKeyBindings[A_GameMenu] = SDL_SCANCODE_ESCAPE; + defaultKeyBindings[A_TogglePOV] = SDL_SCANCODE_TAB; + defaultKeyBindings[A_QuickKey1] = SDL_SCANCODE_1; + defaultKeyBindings[A_QuickKey2] = SDL_SCANCODE_2; + defaultKeyBindings[A_QuickKey3] = SDL_SCANCODE_3; + defaultKeyBindings[A_QuickKey4] = SDL_SCANCODE_4; + defaultKeyBindings[A_QuickKey5] = SDL_SCANCODE_5; + defaultKeyBindings[A_QuickKey6] = SDL_SCANCODE_6; + defaultKeyBindings[A_QuickKey7] = SDL_SCANCODE_7; + defaultKeyBindings[A_QuickKey8] = SDL_SCANCODE_8; + defaultKeyBindings[A_QuickKey9] = SDL_SCANCODE_9; + defaultKeyBindings[A_QuickKey10] = SDL_SCANCODE_0; + defaultKeyBindings[A_Screenshot] = SDL_SCANCODE_F12; + defaultKeyBindings[A_ToggleHUD] = SDL_SCANCODE_F11; + defaultKeyBindings[A_ToggleDebug] = SDL_SCANCODE_F10; + defaultKeyBindings[A_AlwaysRun] = SDL_SCANCODE_CAPSLOCK; + defaultKeyBindings[A_QuickSave] = SDL_SCANCODE_F5; + defaultKeyBindings[A_QuickLoad] = SDL_SCANCODE_F9; + + std::map defaultMouseButtonBindings; + defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT; + defaultMouseButtonBindings[A_Use] = SDL_BUTTON_LEFT; + + std::map defaultMouseWheelBindings; + defaultMouseWheelBindings[A_ZoomIn] = ICS::InputControlSystem::MouseWheelClick::UP; + defaultMouseWheelBindings[A_ZoomOut] = ICS::InputControlSystem::MouseWheelClick::DOWN; + + for (int i = 0; i < A_Last; ++i) + { + ICS::Control* control; + bool controlExists = mInputBinder->getChannel(i)->getControlsCount () != 0; + if (!controlExists) + { + control = new ICS::Control(std::to_string(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); + mInputBinder->addControl(control); + control->attachChannel(mInputBinder->getChannel(i), ICS::Channel::DIRECT); + } + else + { + control = mInputBinder->getChannel(i)->getAttachedControls ().front().control; + } + + if (!controlExists || force || + ( mInputBinder->getKeyBinding (control, ICS::Control::INCREASE) == SDL_SCANCODE_UNKNOWN + && mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS + && mInputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE) == ICS::InputControlSystem::MouseWheelClick::UNASSIGNED + )) + { + clearAllKeyBindings(mInputBinder, control); + + if (defaultKeyBindings.find(i) != defaultKeyBindings.end() + && (force || !mInputBinder->isKeyBound(defaultKeyBindings[i]))) + { + control->setInitialValue(0.0f); + mInputBinder->addKeyBinding(control, defaultKeyBindings[i], ICS::Control::INCREASE); + } + else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end() + && (force || !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i]))) + { + control->setInitialValue(0.0f); + mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); + } + else if (defaultMouseWheelBindings.find(i) != defaultMouseWheelBindings.end() + && (force || !mInputBinder->isMouseWheelBound(defaultMouseWheelBindings[i]))) + { + control->setInitialValue(0.f); + mInputBinder->addMouseWheelBinding(control, defaultMouseWheelBindings[i], ICS::Control::INCREASE); + } + + if (i == A_LookLeftRight && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_4) && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_6)) + { + mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_6, ICS::Control::INCREASE); + mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_4, ICS::Control::DECREASE); + } + if (i == A_LookUpDown && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_8) && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_2)) + { + mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_2, ICS::Control::INCREASE); + mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_8, ICS::Control::DECREASE); + } + } + } + } + + void BindingsManager::loadControllerDefaults(bool force) + { + // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid + // across different versions of OpenMW (in the case where another input action is added) + std::map defaultButtonBindings; + + defaultButtonBindings[A_Activate] = SDL_CONTROLLER_BUTTON_A; + defaultButtonBindings[A_ToggleWeapon] = SDL_CONTROLLER_BUTTON_X; + defaultButtonBindings[A_ToggleSpell] = SDL_CONTROLLER_BUTTON_Y; + //defaultButtonBindings[A_QuickButtonsMenu] = SDL_GetButtonFromScancode(SDL_SCANCODE_F1); // Need to implement, should be ToggleSpell(5) AND Wait(9) + defaultButtonBindings[A_Sneak] = SDL_CONTROLLER_BUTTON_LEFTSTICK; + defaultButtonBindings[A_Journal] = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; + defaultButtonBindings[A_Rest] = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; + defaultButtonBindings[A_TogglePOV] = SDL_CONTROLLER_BUTTON_RIGHTSTICK; + defaultButtonBindings[A_Inventory] = SDL_CONTROLLER_BUTTON_B; + defaultButtonBindings[A_GameMenu] = SDL_CONTROLLER_BUTTON_START; + defaultButtonBindings[A_QuickSave] = SDL_CONTROLLER_BUTTON_GUIDE; + defaultButtonBindings[A_MoveForward] = SDL_CONTROLLER_BUTTON_DPAD_UP; + defaultButtonBindings[A_MoveLeft] = SDL_CONTROLLER_BUTTON_DPAD_LEFT; + defaultButtonBindings[A_MoveBackward] = SDL_CONTROLLER_BUTTON_DPAD_DOWN; + defaultButtonBindings[A_MoveRight] = SDL_CONTROLLER_BUTTON_DPAD_RIGHT; + + std::map defaultAxisBindings; + defaultAxisBindings[A_MoveForwardBackward] = SDL_CONTROLLER_AXIS_LEFTY; + defaultAxisBindings[A_MoveLeftRight] = SDL_CONTROLLER_AXIS_LEFTX; + defaultAxisBindings[A_LookUpDown] = SDL_CONTROLLER_AXIS_RIGHTY; + defaultAxisBindings[A_LookLeftRight] = SDL_CONTROLLER_AXIS_RIGHTX; + defaultAxisBindings[A_Use] = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; + defaultAxisBindings[A_Jump] = SDL_CONTROLLER_AXIS_TRIGGERLEFT; + + for (int i = 0; i < A_Last; i++) + { + ICS::Control* control; + bool controlExists = mInputBinder->getChannel(i)->getControlsCount () != 0; + if (!controlExists) + { + float initial; + if (defaultAxisBindings.find(i) == defaultAxisBindings.end()) + initial = 0.0f; + else initial = 0.5f; + control = new ICS::Control(std::to_string(i), false, true, initial, ICS::ICS_MAX, ICS::ICS_MAX); + mInputBinder->addControl(control); + control->attachChannel(mInputBinder->getChannel(i), ICS::Channel::DIRECT); + } + else + { + control = mInputBinder->getChannel(i)->getAttachedControls ().front().control; + } + + if (!controlExists || force || ( mInputBinder->getJoystickAxisBinding (control, sFakeDeviceId, ICS::Control::INCREASE) == ICS::InputControlSystem::UNASSIGNED && mInputBinder->getJoystickButtonBinding (control, sFakeDeviceId, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS )) + { + clearAllControllerBindings(mInputBinder, control); + + if (defaultButtonBindings.find(i) != defaultButtonBindings.end() + && (force || !mInputBinder->isJoystickButtonBound(sFakeDeviceId, defaultButtonBindings[i]))) + { + control->setInitialValue(0.0f); + mInputBinder->addJoystickButtonBinding(control, sFakeDeviceId, defaultButtonBindings[i], ICS::Control::INCREASE); + } + else if (defaultAxisBindings.find(i) != defaultAxisBindings.end() && (force || !mInputBinder->isJoystickAxisBound(sFakeDeviceId, defaultAxisBindings[i]))) + { + control->setValue(0.5f); + control->setInitialValue(0.5f); + mInputBinder->addJoystickAxisBinding(control, sFakeDeviceId, defaultAxisBindings[i], ICS::Control::INCREASE); + } + } + } + } + + std::string BindingsManager::getActionDescription (int action) + { + std::map descriptions; + + if (action == A_Screenshot) + return "Screenshot"; + else if (action == A_ZoomIn) + return "Zoom In"; + else if (action == A_ZoomOut) + return "Zoom Out"; + else if (action == A_ToggleHUD) + return "Toggle HUD"; + + descriptions[A_Use] = "sUse"; + descriptions[A_Activate] = "sActivate"; + descriptions[A_MoveBackward] = "sBack"; + descriptions[A_MoveForward] = "sForward"; + descriptions[A_MoveLeft] = "sLeft"; + descriptions[A_MoveRight] = "sRight"; + descriptions[A_ToggleWeapon] = "sReady_Weapon"; + descriptions[A_ToggleSpell] = "sReady_Magic"; + descriptions[A_CycleSpellLeft] = "sPrevSpell"; + descriptions[A_CycleSpellRight] = "sNextSpell"; + descriptions[A_CycleWeaponLeft] = "sPrevWeapon"; + descriptions[A_CycleWeaponRight] = "sNextWeapon"; + descriptions[A_Console] = "sConsoleTitle"; + descriptions[A_Run] = "sRun"; + descriptions[A_Sneak] = "sCrouch_Sneak"; + descriptions[A_AutoMove] = "sAuto_Run"; + descriptions[A_Jump] = "sJump"; + descriptions[A_Journal] = "sJournal"; + descriptions[A_Rest] = "sRestKey"; + descriptions[A_Inventory] = "sInventory"; + descriptions[A_TogglePOV] = "sTogglePOVCmd"; + descriptions[A_QuickKeysMenu] = "sQuickMenu"; + descriptions[A_QuickKey1] = "sQuick1Cmd"; + descriptions[A_QuickKey2] = "sQuick2Cmd"; + descriptions[A_QuickKey3] = "sQuick3Cmd"; + descriptions[A_QuickKey4] = "sQuick4Cmd"; + descriptions[A_QuickKey5] = "sQuick5Cmd"; + descriptions[A_QuickKey6] = "sQuick6Cmd"; + descriptions[A_QuickKey7] = "sQuick7Cmd"; + descriptions[A_QuickKey8] = "sQuick8Cmd"; + descriptions[A_QuickKey9] = "sQuick9Cmd"; + descriptions[A_QuickKey10] = "sQuick10Cmd"; + descriptions[A_AlwaysRun] = "sAlways_Run"; + descriptions[A_QuickSave] = "sQuickSaveCmd"; + descriptions[A_QuickLoad] = "sQuickLoadCmd"; + + if (descriptions[action] == "") + return ""; // not configurable + + return "#{" + descriptions[action] + "}"; + } + + std::string BindingsManager::getActionKeyBindingName (int action) + { + if (mInputBinder->getChannel (action)->getControlsCount () == 0) + return "#{sNone}"; + + ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; + + SDL_Scancode key = mInputBinder->getKeyBinding (c, ICS::Control::INCREASE); + unsigned int mouse = mInputBinder->getMouseButtonBinding (c, ICS::Control::INCREASE); + ICS::InputControlSystem::MouseWheelClick wheel = mInputBinder->getMouseWheelBinding(c, ICS::Control::INCREASE); + if (key != SDL_SCANCODE_UNKNOWN) + return MyGUI::TextIterator::toTagsString(mInputBinder->scancodeToString (key)); + else if (mouse != ICS_MAX_DEVICE_BUTTONS) + return "#{sMouse} " + std::to_string(mouse); + else if (wheel != ICS::InputControlSystem::MouseWheelClick::UNASSIGNED) + switch (wheel) + { + case ICS::InputControlSystem::MouseWheelClick::UP: + return "Mouse Wheel Up"; + case ICS::InputControlSystem::MouseWheelClick::DOWN: + return "Mouse Wheel Down"; + case ICS::InputControlSystem::MouseWheelClick::RIGHT: + return "Mouse Wheel Right"; + case ICS::InputControlSystem::MouseWheelClick::LEFT: + return "Mouse Wheel Left"; + default: + return "#{sNone}"; + } + else + return "#{sNone}"; + } + + std::string BindingsManager::getActionControllerBindingName (int action) + { + if (mInputBinder->getChannel (action)->getControlsCount () == 0) + return "#{sNone}"; + + ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; + + if (mInputBinder->getJoystickAxisBinding (c, sFakeDeviceId, ICS::Control::INCREASE) != ICS::InputControlSystem::UNASSIGNED) + return sdlControllerAxisToString(mInputBinder->getJoystickAxisBinding (c, sFakeDeviceId, ICS::Control::INCREASE)); + else if (mInputBinder->getJoystickButtonBinding (c, sFakeDeviceId, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS ) + return sdlControllerButtonToString(mInputBinder->getJoystickButtonBinding (c, sFakeDeviceId, ICS::Control::INCREASE)); + else + return "#{sNone}"; + } + + std::vector BindingsManager::getActionKeySorting() + { + std::vector ret; + ret.push_back(A_MoveForward); + ret.push_back(A_MoveBackward); + ret.push_back(A_MoveLeft); + ret.push_back(A_MoveRight); + ret.push_back(A_TogglePOV); + ret.push_back(A_ZoomIn); + ret.push_back(A_ZoomOut); + ret.push_back(A_Run); + ret.push_back(A_AlwaysRun); + ret.push_back(A_Sneak); + ret.push_back(A_Activate); + ret.push_back(A_Use); + ret.push_back(A_ToggleWeapon); + ret.push_back(A_ToggleSpell); + ret.push_back(A_CycleSpellLeft); + ret.push_back(A_CycleSpellRight); + ret.push_back(A_CycleWeaponLeft); + ret.push_back(A_CycleWeaponRight); + ret.push_back(A_AutoMove); + ret.push_back(A_Jump); + ret.push_back(A_Inventory); + ret.push_back(A_Journal); + ret.push_back(A_Rest); + ret.push_back(A_Console); + ret.push_back(A_QuickSave); + ret.push_back(A_QuickLoad); + ret.push_back(A_ToggleHUD); + ret.push_back(A_Screenshot); + ret.push_back(A_QuickKeysMenu); + ret.push_back(A_QuickKey1); + ret.push_back(A_QuickKey2); + ret.push_back(A_QuickKey3); + ret.push_back(A_QuickKey4); + ret.push_back(A_QuickKey5); + ret.push_back(A_QuickKey6); + ret.push_back(A_QuickKey7); + ret.push_back(A_QuickKey8); + ret.push_back(A_QuickKey9); + ret.push_back(A_QuickKey10); + + return ret; + } + std::vector BindingsManager::getActionControllerSorting() + { + std::vector ret; + ret.push_back(A_TogglePOV); + ret.push_back(A_ZoomIn); + ret.push_back(A_ZoomOut); + ret.push_back(A_Sneak); + ret.push_back(A_Activate); + ret.push_back(A_Use); + ret.push_back(A_ToggleWeapon); + ret.push_back(A_ToggleSpell); + ret.push_back(A_AutoMove); + ret.push_back(A_Jump); + ret.push_back(A_Inventory); + ret.push_back(A_Journal); + ret.push_back(A_Rest); + ret.push_back(A_QuickSave); + ret.push_back(A_QuickLoad); + ret.push_back(A_ToggleHUD); + ret.push_back(A_Screenshot); + ret.push_back(A_QuickKeysMenu); + ret.push_back(A_QuickKey1); + ret.push_back(A_QuickKey2); + ret.push_back(A_QuickKey3); + ret.push_back(A_QuickKey4); + ret.push_back(A_QuickKey5); + ret.push_back(A_QuickKey6); + ret.push_back(A_QuickKey7); + ret.push_back(A_QuickKey8); + ret.push_back(A_QuickKey9); + ret.push_back(A_QuickKey10); + ret.push_back(A_CycleSpellLeft); + ret.push_back(A_CycleSpellRight); + ret.push_back(A_CycleWeaponLeft); + ret.push_back(A_CycleWeaponRight); + + return ret; + } + + void BindingsManager::enableDetectingBindingMode(int action, bool keyboard) + { + mListener->setDetectingKeyboard(keyboard); + ICS::Control* c = mInputBinder->getChannel(action)->getAttachedControls().front().control; + mInputBinder->enableDetectingBindingState(c, ICS::Control::INCREASE); + } + + bool BindingsManager::isDetectingBindingState() const + { + return mInputBinder->detectingBindingState(); + } + + void BindingsManager::mousePressed(const SDL_MouseButtonEvent &arg, int deviceID) + { + mInputBinder->mousePressed(arg, deviceID); + } + + void BindingsManager::mouseReleased(const SDL_MouseButtonEvent &arg, int deviceID) + { + mInputBinder->mouseReleased(arg, deviceID); + } + + void BindingsManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg) + { + mInputBinder->mouseMoved(arg); + } + + void BindingsManager::mouseWheelMoved(const SDL_MouseWheelEvent &arg) + { + mInputBinder->mouseWheelMoved(arg); + } + + void BindingsManager::keyPressed(const SDL_KeyboardEvent &arg) + { + mInputBinder->keyPressed(arg); + } + + void BindingsManager::keyReleased(const SDL_KeyboardEvent &arg) + { + mInputBinder->keyReleased(arg); + } + + void BindingsManager::controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg) + { + mInputBinder->controllerAdded(deviceID, arg); + } + + void BindingsManager::controllerRemoved(const SDL_ControllerDeviceEvent &arg) + { + mInputBinder->controllerRemoved(arg); + } + + void BindingsManager::controllerButtonPressed(int deviceID, const SDL_ControllerButtonEvent &arg) + { + mInputBinder->buttonPressed(deviceID, arg); + } + + void BindingsManager::controllerButtonReleased(int deviceID, const SDL_ControllerButtonEvent &arg) + { + mInputBinder->buttonReleased(deviceID, arg); + } + + void BindingsManager::controllerAxisMoved(int deviceID, const SDL_ControllerAxisEvent &arg) + { + mInputBinder->axisMoved(deviceID, arg); + } + + SDL_Scancode BindingsManager::getKeyBinding(int actionId) + { + return mInputBinder->getKeyBinding(mInputBinder->getControl(actionId), ICS::Control::INCREASE); + } + + void BindingsManager::actionValueChanged(int action, float currentValue, float previousValue) + { + MWBase::Environment::get().getInputManager()->resetIdleTime(); + + if (mDragDrop && action != A_GameMenu && action != A_Inventory) + return; + + if((previousValue == 1 || previousValue == 0) && (currentValue==1 || currentValue==0)) + { + //Is a normal button press, so don't change it at all + } + //Otherwise only trigger button presses as they go through specific points + else if(previousValue >= .8 && currentValue < .8) + { + currentValue = 0.0; + previousValue = 1.0; + } + else if(previousValue <= .6 && currentValue > .6) + { + currentValue = 1.0; + previousValue = 0.0; + } + else + { + //If it's not switching between those values, ignore the channel change. + return; + } + + if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + { + bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed(); + if (action == A_Use) + { + if(joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) + action = A_CycleWeaponRight; + + else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell)) + action = A_CycleSpellRight; + + else + { + MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); + MWMechanics::DrawState_ state = player.getDrawState(); + player.setAttackingOrSpell(currentValue != 0 && state != MWMechanics::DrawState_Nothing); + } + } + else if (action == A_Jump) + { + if(joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) + action = A_CycleWeaponLeft; + + else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell)) + action = A_CycleSpellLeft; + + else + MWBase::Environment::get().getInputManager()->setAttemptJump(currentValue == 1.0 && previousValue == 0.0); + } + } + + if (currentValue == 1) + MWBase::Environment::get().getInputManager()->executeAction(action); + } +} diff --git a/apps/openmw/mwinput/bindingsmanager.hpp b/apps/openmw/mwinput/bindingsmanager.hpp new file mode 100644 index 000000000..35b26c877 --- /dev/null +++ b/apps/openmw/mwinput/bindingsmanager.hpp @@ -0,0 +1,73 @@ +#ifndef MWINPUT_MWBINDINGSMANAGER_H +#define MWINPUT_MWBINDINGSMANAGER_H + +#include +#include + +#include + +namespace MWInput +{ + class BindingsListener; + class InputControlSystem; + + class BindingsManager + { + public: + BindingsManager(const std::string& userFile, bool userFileExists); + + virtual ~BindingsManager(); + + std::string getActionDescription (int action); + std::string getActionKeyBindingName (int action); + std::string getActionControllerBindingName (int action); + std::vector getActionKeySorting(); + std::vector getActionControllerSorting(); + + void enableDetectingBindingMode (int action, bool keyboard); + bool isDetectingBindingState() const; + + void loadKeyDefaults(bool force = false); + void loadControllerDefaults(bool force = false); + + void setDragDrop(bool dragDrop); + + void update(float dt); + + void setPlayerControlsEnabled(bool enabled); + + bool isLeftOrRightButton(int action, bool joystick) const; + + bool actionIsActive(int id) const; + float getActionValue(int id) const; + + void mousePressed(const SDL_MouseButtonEvent &evt, int deviceID); + void mouseReleased(const SDL_MouseButtonEvent &arg, int deviceID); + void mouseMoved(const SDLUtil::MouseMotionEvent &arg); + void mouseWheelMoved(const SDL_MouseWheelEvent &arg); + + void keyPressed(const SDL_KeyboardEvent &arg); + void keyReleased(const SDL_KeyboardEvent &arg); + + void controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg); + void controllerRemoved(const SDL_ControllerDeviceEvent &arg); + void controllerButtonPressed(int deviceID, const SDL_ControllerButtonEvent &arg); + void controllerButtonReleased(int deviceID, const SDL_ControllerButtonEvent &arg); + void controllerAxisMoved(int deviceID, const SDL_ControllerAxisEvent &arg); + + SDL_Scancode getKeyBinding(int actionId); + + void actionValueChanged(int action, float currentValue, float previousValue); + + private: + void setupSDLKeyMappings(); + + InputControlSystem* mInputBinder; + BindingsListener* mListener; + + std::string mUserFile; + + bool mDragDrop; + }; +} +#endif diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 697d3ab91..b8fc0ca74 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -4,8 +4,6 @@ #include #include -#include - #include #include "../mwbase/environment.hpp" @@ -18,20 +16,18 @@ #include "actions.hpp" #include "actionmanager.hpp" +#include "bindingsmanager.hpp" #include "mousemanager.hpp" +#include "sdlmappings.hpp" namespace MWInput { - static const int sFakeDeviceID = 1; - - ControllerManager::ControllerManager(ICS::InputControlSystem* inputBinder, - SDLUtil::InputWrapper* inputWrapper, + ControllerManager::ControllerManager(BindingsManager* bindingsManager, ActionManager* actionManager, MouseManager* mouseManager, const std::string& userControllerBindingsFile, const std::string& controllerBindingsFile) - : mInputBinder(inputBinder) - , mInputWrapper(inputWrapper) + : mBindingsManager(bindingsManager) , mActionManager(actionManager) , mMouseManager(mouseManager) , mJoystickEnabled (Settings::Manager::getBool("enable controller", "Input")) @@ -62,7 +58,8 @@ namespace MWInput { SDL_ControllerDeviceEvent evt; evt.which = i; - controllerAdded(sFakeDeviceID, evt); + static const int fakeDeviceID = 1; + controllerAdded(fakeDeviceID, evt); Log(Debug::Info) << "Detected game controller: " << SDL_GameControllerNameForIndex(i); } else @@ -82,18 +79,13 @@ namespace MWInput void ControllerManager::processChangedSettings(const Settings::CategorySettingVector& changed) { - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) + for (const auto& setting : changed) { - if (it->first == "Input" && it->second == "enable controller") + if (setting.first == "Input" && setting.second == "enable controller") mJoystickEnabled = Settings::Manager::getBool("enable controller", "Input"); } } - bool ControllerManager::actionIsActive (int id) - { - return (mInputBinder->getChannel (id)->getValue ()==1.0); - } - bool ControllerManager::update(float dt, bool disableControls) { mControlsDisabled = disableControls; @@ -101,12 +93,12 @@ namespace MWInput if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled)) { - float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue()*2.0f-1.0f; - float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue()*2.0f-1.0f; - float zAxis = mInputBinder->getChannel(A_LookUpDown)->getValue()*2.0f-1.0f; + float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight)*2.0f-1.0f; + float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward)*2.0f-1.0f; + float zAxis = mBindingsManager->getActionValue(A_LookUpDown)*2.0f-1.0f; - xAxis *= (1.5f - mInputBinder->getChannel(A_Use)->getValue()); - yAxis *= (1.5f - mInputBinder->getChannel(A_Use)->getValue()); + xAxis *= (1.5f - mBindingsManager->getActionValue(A_Use)); + yAxis *= (1.5f - mBindingsManager->getActionValue(A_Use)); // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor @@ -137,8 +129,8 @@ namespace MWInput // be done in the physics system. if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) { - float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue(); - float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue(); + float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight); + float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward); if (xAxis != .5) { triedToMove = true; @@ -163,7 +155,7 @@ namespace MWInput { if(mJoystickLastUsed) { - if(actionIsActive(A_Sneak)) + if(mBindingsManager->actionIsActive(A_Sneak)) { if(mSneakToggleShortcutTimer) // New Sneak Button Press { @@ -189,13 +181,13 @@ namespace MWInput } } else - player.setSneak(actionIsActive(A_Sneak)); + player.setSneak(mBindingsManager->actionIsActive(A_Sneak)); } } if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch")) { - if (!actionIsActive(A_TogglePOV)) + if (!mBindingsManager->actionIsActive(A_TogglePOV)) mGamepadZoom = 0; if(mGamepadZoom) @@ -210,7 +202,7 @@ namespace MWInput void ControllerManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg) { - if (!mJoystickEnabled || mInputBinder->detectingBindingState()) + if (!mJoystickEnabled || mBindingsManager->isDetectingBindingState()) return; mJoystickLastUsed = true; @@ -231,26 +223,26 @@ namespace MWInput MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); } - MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!mousePressSuccess); + mBindingsManager->setPlayerControlsEnabled(!mousePressSuccess); } } } else - MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(true); + mBindingsManager->setPlayerControlsEnabled(true); //esc, to leave initial movie screen - auto kc = mInputWrapper->sdl2OISKeyCode(SDLK_ESCAPE); - MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0)); + auto kc = sdlKeyToMyGUI(SDLK_ESCAPE); + mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(kc, 0)); if (!mControlsDisabled) - mInputBinder->buttonPressed(deviceID, arg); + mBindingsManager->controllerButtonPressed(deviceID, arg); } - void ControllerManager::buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg ) + void ControllerManager::buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg) { - if(mInputBinder->detectingBindingState()) + if (mBindingsManager->isDetectingBindingState()) { - mInputBinder->buttonReleased(deviceID, arg); + mBindingsManager->controllerButtonReleased(deviceID, arg); return; } if (!mJoystickEnabled || mControlsDisabled) @@ -265,21 +257,21 @@ namespace MWInput if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click. { bool mousePressSuccess = mMouseManager->injectMouseButtonRelease(SDL_BUTTON_LEFT); - if (mInputBinder->detectingBindingState()) // If the player just triggered binding, don't let button release bind. + if (mBindingsManager->isDetectingBindingState()) // If the player just triggered binding, don't let button release bind. return; - MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!mousePressSuccess); + mBindingsManager->setPlayerControlsEnabled(!mousePressSuccess); } } } else - MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(true); + mBindingsManager->setPlayerControlsEnabled(true); //esc, to leave initial movie screen - auto kc = mInputWrapper->sdl2OISKeyCode(SDLK_ESCAPE); - MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); + auto kc = sdlKeyToMyGUI(SDLK_ESCAPE); + mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(kc)); - mInputBinder->buttonReleased(deviceID, arg); + mBindingsManager->controllerButtonReleased(deviceID, arg); } void ControllerManager::axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg) @@ -308,17 +300,17 @@ namespace MWInput } } } - mInputBinder->axisMoved(deviceID, arg); + mBindingsManager->controllerAxisMoved(deviceID, arg); } void ControllerManager::controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg) { - mInputBinder->controllerAdded(deviceID, arg); + mBindingsManager->controllerAdded(deviceID, arg); } void ControllerManager::controllerRemoved(const SDL_ControllerDeviceEvent &arg) { - mInputBinder->controllerRemoved(arg); + mBindingsManager->controllerRemoved(arg); } bool ControllerManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg) diff --git a/apps/openmw/mwinput/controllermanager.hpp b/apps/openmw/mwinput/controllermanager.hpp index 1ab6ea76d..f1be50ee6 100644 --- a/apps/openmw/mwinput/controllermanager.hpp +++ b/apps/openmw/mwinput/controllermanager.hpp @@ -5,23 +5,17 @@ #include #include -#include - -namespace ICS -{ - class InputControlSystem; -} namespace MWInput { class ActionManager; + class BindingsManager; class MouseManager; class ControllerManager : public SDLUtil::ControllerListener { public: - ControllerManager(ICS::InputControlSystem* inputBinder, - SDLUtil::InputWrapper* inputWrapper, + ControllerManager(BindingsManager* bindingsManager, ActionManager* actionManager, MouseManager* mouseManager, const std::string& userControllerBindingsFile, @@ -54,10 +48,7 @@ namespace MWInput bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg); bool gamepadToGuiControl(const SDL_ControllerAxisEvent &arg); - bool actionIsActive(int id); - - ICS::InputControlSystem* mInputBinder; - SDLUtil::InputWrapper* mInputWrapper; + BindingsManager* mBindingsManager; ActionManager* mActionManager; MouseManager* mMouseManager; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f7f32b9a9..0a43a62fc 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -30,6 +30,7 @@ #include "../mwmechanics/actorutil.hpp" #include "actionmanager.hpp" +#include "bindingsmanager.hpp" #include "controllermanager.hpp" #include "controlswitch.hpp" #include "keyboardmanager.hpp" @@ -46,38 +47,25 @@ namespace MWInput osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation, const std::string& userFile, bool userFileExists, const std::string& userControllerBindingsFile, const std::string& controllerBindingsFile, bool grab) - : mUserFile(userFile) - , mDragDrop(false) - , mGrabCursor(Settings::Manager::getBool("grab cursor", "Input")) + : mGrabCursor(Settings::Manager::getBool("grab cursor", "Input")) , mGuiCursorEnabled(true) - , mDetectingKeyboard(false) - , mFakeDeviceID(1) { mInputWrapper = new SDLUtil::InputWrapper(window, viewer, grab); mInputWrapper->setWindowEventCallback(MWBase::Environment::get().getWindowManager()); - std::string file = userFileExists ? userFile : ""; - mInputBinder = new ICS::InputControlSystem(file, true, this, nullptr, A_Last); - - loadKeyDefaults(); - loadControllerDefaults(); - - for (int i = 0; i < A_Last; ++i) - { - mInputBinder->getChannel (i)->addListener (this); - } + mBindingsManager = new BindingsManager(userFile, userFileExists); mControlSwitch = new ControlSwitch(); - mActionManager = new ActionManager(mInputBinder, screenCaptureOperation, viewer, screenCaptureHandler); + mActionManager = new ActionManager(mBindingsManager, screenCaptureOperation, viewer, screenCaptureHandler); - mKeyboardManager = new KeyboardManager(mInputBinder, mInputWrapper, mActionManager); + mKeyboardManager = new KeyboardManager(mBindingsManager, mActionManager); mInputWrapper->setKeyboardEventCallback(mKeyboardManager); - mMouseManager = new MouseManager(mInputBinder, mInputWrapper, window); + mMouseManager = new MouseManager(mBindingsManager, mInputWrapper, window); mInputWrapper->setMouseEventCallback(mMouseManager); - mControllerManager = new ControllerManager(mInputBinder, mInputWrapper, mActionManager, mMouseManager, userControllerBindingsFile, controllerBindingsFile); + mControllerManager = new ControllerManager(mBindingsManager, mActionManager, mMouseManager, userControllerBindingsFile, controllerBindingsFile); mInputWrapper->setControllerEventCallback(mControllerManager); mSensorManager = new SensorManager(); @@ -105,89 +93,14 @@ namespace MWInput delete mControlSwitch; - mInputBinder->save(mUserFile); - delete mInputBinder; + delete mBindingsManager; delete mInputWrapper; } - void InputManager::setPlayerControlsEnabled(bool enabled) + void InputManager::setAttemptJump(bool jumping) { - int playerChannels[] = {A_AutoMove, A_AlwaysRun, A_ToggleWeapon, - A_ToggleSpell, A_Rest, A_QuickKey1, A_QuickKey2, - A_QuickKey3, A_QuickKey4, A_QuickKey5, A_QuickKey6, - A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10, - A_Use, A_Journal}; - - for(size_t i = 0; i < sizeof(playerChannels)/sizeof(playerChannels[0]); i++) { - int pc = playerChannels[i]; - mInputBinder->getChannel(pc)->setEnabled(enabled); - } - } - - void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) - { - resetIdleTime (); - - int action = channel->getNumber(); - - if (mDragDrop && action != A_GameMenu && action != A_Inventory) - return; - - if((previousValue == 1 || previousValue == 0) && (currentValue==1 || currentValue==0)) - { - //Is a normal button press, so don't change it at all - } - //Otherwise only trigger button presses as they go through specific points - else if(previousValue >= .8 && currentValue < .8) - { - currentValue = 0.0; - previousValue = 1.0; - } - else if(previousValue <= .6 && currentValue > .6) - { - currentValue = 1.0; - previousValue = 0.0; - } - else - { - //If it's not switching between those values, ignore the channel change. - return; - } - - if (mControlSwitch->get("playercontrols")) - { - bool joystickUsed = mControllerManager->joystickLastUsed(); - if (action == A_Use) - { - if(joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) - action = A_CycleWeaponRight; - - else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell)) - action = A_CycleSpellRight; - - else - { - MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); - MWMechanics::DrawState_ state = player.getDrawState(); - player.setAttackingOrSpell(currentValue != 0 && state != MWMechanics::DrawState_Nothing); - } - } - else if (action == A_Jump) - { - if(joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) - action = A_CycleWeaponLeft; - - else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell)) - action = A_CycleSpellLeft; - - else - mActionManager->setAttemptJump(currentValue == 1.0 && previousValue == 0.0); - } - } - - if (currentValue == 1) - mActionManager->executeAction(action); + mActionManager->setAttemptJump(jumping); } void InputManager::updateCursorMode() @@ -225,8 +138,7 @@ namespace MWInput return; } - // update values of channels (as a result of pressed keys) - mInputBinder->update(dt); + mBindingsManager->update(dt); updateCursorMode(); @@ -238,7 +150,7 @@ namespace MWInput void InputManager::setDragDrop(bool dragDrop) { - mDragDrop = dragDrop; + mBindingsManager->setDragDrop(dragDrop); } void InputManager::setGamepadGuiCursorEnabled(bool enabled) @@ -260,10 +172,9 @@ namespace MWInput void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) { - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); - it != changed.end(); ++it) + for (const auto& setting : changed) { - if (it->first == "Input" && it->second == "grab cursor") + if (setting.first == "Input" && setting.second == "grab cursor") mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); } @@ -271,12 +182,12 @@ namespace MWInput mSensorManager->processChangedSettings(changed); } - bool InputManager::getControlSwitch (const std::string& sw) + bool InputManager::getControlSwitch(const std::string& sw) { return mControlSwitch->get(sw); } - void InputManager::toggleControlSwitch (const std::string& sw, bool value) + void InputManager::toggleControlSwitch(const std::string& sw, bool value) { mControlSwitch->set(sw, value); } @@ -286,488 +197,34 @@ namespace MWInput mActionManager->resetIdleTime(); } - bool InputManager::actionIsActive (int id) + std::string InputManager::getActionDescription(int action) { - return (mInputBinder->getChannel (id)->getValue ()==1.0); + return mBindingsManager->getActionDescription(action); } - void InputManager::loadKeyDefaults (bool force) + std::string InputManager::getActionKeyBindingName(int action) { - // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid - // across different versions of OpenMW (in the case where another input action is added) - std::map defaultKeyBindings; - - //Gets the Keyvalue from the Scancode; gives the button in the same place reguardless of keyboard format - defaultKeyBindings[A_Activate] = SDL_SCANCODE_SPACE; - defaultKeyBindings[A_MoveBackward] = SDL_SCANCODE_S; - defaultKeyBindings[A_MoveForward] = SDL_SCANCODE_W; - defaultKeyBindings[A_MoveLeft] = SDL_SCANCODE_A; - defaultKeyBindings[A_MoveRight] = SDL_SCANCODE_D; - defaultKeyBindings[A_ToggleWeapon] = SDL_SCANCODE_F; - defaultKeyBindings[A_ToggleSpell] = SDL_SCANCODE_R; - defaultKeyBindings[A_CycleSpellLeft] = SDL_SCANCODE_MINUS; - defaultKeyBindings[A_CycleSpellRight] = SDL_SCANCODE_EQUALS; - defaultKeyBindings[A_CycleWeaponLeft] = SDL_SCANCODE_LEFTBRACKET; - defaultKeyBindings[A_CycleWeaponRight] = SDL_SCANCODE_RIGHTBRACKET; - - defaultKeyBindings[A_QuickKeysMenu] = SDL_SCANCODE_F1; - defaultKeyBindings[A_Console] = SDL_SCANCODE_GRAVE; - defaultKeyBindings[A_Run] = SDL_SCANCODE_LSHIFT; - defaultKeyBindings[A_Sneak] = SDL_SCANCODE_LCTRL; - defaultKeyBindings[A_AutoMove] = SDL_SCANCODE_Q; - defaultKeyBindings[A_Jump] = SDL_SCANCODE_E; - defaultKeyBindings[A_Journal] = SDL_SCANCODE_J; - defaultKeyBindings[A_Rest] = SDL_SCANCODE_T; - defaultKeyBindings[A_GameMenu] = SDL_SCANCODE_ESCAPE; - defaultKeyBindings[A_TogglePOV] = SDL_SCANCODE_TAB; - defaultKeyBindings[A_QuickKey1] = SDL_SCANCODE_1; - defaultKeyBindings[A_QuickKey2] = SDL_SCANCODE_2; - defaultKeyBindings[A_QuickKey3] = SDL_SCANCODE_3; - defaultKeyBindings[A_QuickKey4] = SDL_SCANCODE_4; - defaultKeyBindings[A_QuickKey5] = SDL_SCANCODE_5; - defaultKeyBindings[A_QuickKey6] = SDL_SCANCODE_6; - defaultKeyBindings[A_QuickKey7] = SDL_SCANCODE_7; - defaultKeyBindings[A_QuickKey8] = SDL_SCANCODE_8; - defaultKeyBindings[A_QuickKey9] = SDL_SCANCODE_9; - defaultKeyBindings[A_QuickKey10] = SDL_SCANCODE_0; - defaultKeyBindings[A_Screenshot] = SDL_SCANCODE_F12; - defaultKeyBindings[A_ToggleHUD] = SDL_SCANCODE_F11; - defaultKeyBindings[A_ToggleDebug] = SDL_SCANCODE_F10; - defaultKeyBindings[A_AlwaysRun] = SDL_SCANCODE_CAPSLOCK; - defaultKeyBindings[A_QuickSave] = SDL_SCANCODE_F5; - defaultKeyBindings[A_QuickLoad] = SDL_SCANCODE_F9; - - std::map defaultMouseButtonBindings; - defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT; - defaultMouseButtonBindings[A_Use] = SDL_BUTTON_LEFT; - - std::map defaultMouseWheelBindings; - defaultMouseWheelBindings[A_ZoomIn] = ICS::InputControlSystem::MouseWheelClick::UP; - defaultMouseWheelBindings[A_ZoomOut] = ICS::InputControlSystem::MouseWheelClick::DOWN; - - for (int i = 0; i < A_Last; ++i) - { - ICS::Control* control; - bool controlExists = mInputBinder->getChannel(i)->getControlsCount () != 0; - if (!controlExists) - { - control = new ICS::Control(std::to_string(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); - mInputBinder->addControl(control); - control->attachChannel(mInputBinder->getChannel(i), ICS::Channel::DIRECT); - } - else - { - control = mInputBinder->getChannel(i)->getAttachedControls ().front().control; - } - - if (!controlExists || force || - ( mInputBinder->getKeyBinding (control, ICS::Control::INCREASE) == SDL_SCANCODE_UNKNOWN - && mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS - && mInputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE) == ICS::InputControlSystem::MouseWheelClick::UNASSIGNED - )) - { - clearAllKeyBindings(control); - - if (defaultKeyBindings.find(i) != defaultKeyBindings.end() - && (force || !mInputBinder->isKeyBound(defaultKeyBindings[i]))) - { - control->setInitialValue(0.0f); - mInputBinder->addKeyBinding(control, defaultKeyBindings[i], ICS::Control::INCREASE); - } - else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end() - && (force || !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i]))) - { - control->setInitialValue(0.0f); - mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); - } - else if (defaultMouseWheelBindings.find(i) != defaultMouseWheelBindings.end() - && (force || !mInputBinder->isMouseWheelBound(defaultMouseWheelBindings[i]))) - { - control->setInitialValue(0.f); - mInputBinder->addMouseWheelBinding(control, defaultMouseWheelBindings[i], ICS::Control::INCREASE); - } - - if (i == A_LookLeftRight && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_4) && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_6)) - { - mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_6, ICS::Control::INCREASE); - mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_4, ICS::Control::DECREASE); - } - if (i == A_LookUpDown && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_8) && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_2)) - { - mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_2, ICS::Control::INCREASE); - mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_8, ICS::Control::DECREASE); - } - } - } - } - - void InputManager::loadControllerDefaults(bool force) - { - // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid - // across different versions of OpenMW (in the case where another input action is added) - std::map defaultButtonBindings; - - defaultButtonBindings[A_Activate] = SDL_CONTROLLER_BUTTON_A; - defaultButtonBindings[A_ToggleWeapon] = SDL_CONTROLLER_BUTTON_X; - defaultButtonBindings[A_ToggleSpell] = SDL_CONTROLLER_BUTTON_Y; - //defaultButtonBindings[A_QuickButtonsMenu] = SDL_GetButtonFromScancode(SDL_SCANCODE_F1); // Need to implement, should be ToggleSpell(5) AND Wait(9) - defaultButtonBindings[A_Sneak] = SDL_CONTROLLER_BUTTON_LEFTSTICK; - defaultButtonBindings[A_Journal] = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; - defaultButtonBindings[A_Rest] = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; - defaultButtonBindings[A_TogglePOV] = SDL_CONTROLLER_BUTTON_RIGHTSTICK; - defaultButtonBindings[A_Inventory] = SDL_CONTROLLER_BUTTON_B; - defaultButtonBindings[A_GameMenu] = SDL_CONTROLLER_BUTTON_START; - defaultButtonBindings[A_QuickSave] = SDL_CONTROLLER_BUTTON_GUIDE; - defaultButtonBindings[A_MoveForward] = SDL_CONTROLLER_BUTTON_DPAD_UP; - defaultButtonBindings[A_MoveLeft] = SDL_CONTROLLER_BUTTON_DPAD_LEFT; - defaultButtonBindings[A_MoveBackward] = SDL_CONTROLLER_BUTTON_DPAD_DOWN; - defaultButtonBindings[A_MoveRight] = SDL_CONTROLLER_BUTTON_DPAD_RIGHT; - - std::map defaultAxisBindings; - defaultAxisBindings[A_MoveForwardBackward] = SDL_CONTROLLER_AXIS_LEFTY; - defaultAxisBindings[A_MoveLeftRight] = SDL_CONTROLLER_AXIS_LEFTX; - defaultAxisBindings[A_LookUpDown] = SDL_CONTROLLER_AXIS_RIGHTY; - defaultAxisBindings[A_LookLeftRight] = SDL_CONTROLLER_AXIS_RIGHTX; - defaultAxisBindings[A_Use] = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; - defaultAxisBindings[A_Jump] = SDL_CONTROLLER_AXIS_TRIGGERLEFT; - - for (int i = 0; i < A_Last; i++) - { - ICS::Control* control; - bool controlExists = mInputBinder->getChannel(i)->getControlsCount () != 0; - if (!controlExists) - { - float initial; - if (defaultAxisBindings.find(i) == defaultAxisBindings.end()) - initial = 0.0f; - else initial = 0.5f; - control = new ICS::Control(std::to_string(i), false, true, initial, ICS::ICS_MAX, ICS::ICS_MAX); - mInputBinder->addControl(control); - control->attachChannel(mInputBinder->getChannel(i), ICS::Channel::DIRECT); - } - else - { - control = mInputBinder->getChannel(i)->getAttachedControls ().front().control; - } - - if (!controlExists || force || ( mInputBinder->getJoystickAxisBinding (control, mFakeDeviceID, ICS::Control::INCREASE) == ICS::InputControlSystem::UNASSIGNED && mInputBinder->getJoystickButtonBinding (control, mFakeDeviceID, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS )) - { - clearAllControllerBindings(control); - - if (defaultButtonBindings.find(i) != defaultButtonBindings.end() - && (force || !mInputBinder->isJoystickButtonBound(mFakeDeviceID, defaultButtonBindings[i]))) - { - control->setInitialValue(0.0f); - mInputBinder->addJoystickButtonBinding(control, mFakeDeviceID, defaultButtonBindings[i], ICS::Control::INCREASE); - } - else if (defaultAxisBindings.find(i) != defaultAxisBindings.end() && (force || !mInputBinder->isJoystickAxisBound(mFakeDeviceID, defaultAxisBindings[i]))) - { - control->setValue(0.5f); - control->setInitialValue(0.5f); - mInputBinder->addJoystickAxisBinding(control, mFakeDeviceID, defaultAxisBindings[i], ICS::Control::INCREASE); - } - } - } + return mBindingsManager->getActionKeyBindingName(action); } - std::string InputManager::getActionDescription (int action) + std::string InputManager::getActionControllerBindingName(int action) { - std::map descriptions; - - if (action == A_Screenshot) - return "Screenshot"; - else if (action == A_ZoomIn) - return "Zoom In"; - else if (action == A_ZoomOut) - return "Zoom Out"; - else if (action == A_ToggleHUD) - return "Toggle HUD"; - - descriptions[A_Use] = "sUse"; - descriptions[A_Activate] = "sActivate"; - descriptions[A_MoveBackward] = "sBack"; - descriptions[A_MoveForward] = "sForward"; - descriptions[A_MoveLeft] = "sLeft"; - descriptions[A_MoveRight] = "sRight"; - descriptions[A_ToggleWeapon] = "sReady_Weapon"; - descriptions[A_ToggleSpell] = "sReady_Magic"; - descriptions[A_CycleSpellLeft] = "sPrevSpell"; - descriptions[A_CycleSpellRight] = "sNextSpell"; - descriptions[A_CycleWeaponLeft] = "sPrevWeapon"; - descriptions[A_CycleWeaponRight] = "sNextWeapon"; - descriptions[A_Console] = "sConsoleTitle"; - descriptions[A_Run] = "sRun"; - descriptions[A_Sneak] = "sCrouch_Sneak"; - descriptions[A_AutoMove] = "sAuto_Run"; - descriptions[A_Jump] = "sJump"; - descriptions[A_Journal] = "sJournal"; - descriptions[A_Rest] = "sRestKey"; - descriptions[A_Inventory] = "sInventory"; - descriptions[A_TogglePOV] = "sTogglePOVCmd"; - descriptions[A_QuickKeysMenu] = "sQuickMenu"; - descriptions[A_QuickKey1] = "sQuick1Cmd"; - descriptions[A_QuickKey2] = "sQuick2Cmd"; - descriptions[A_QuickKey3] = "sQuick3Cmd"; - descriptions[A_QuickKey4] = "sQuick4Cmd"; - descriptions[A_QuickKey5] = "sQuick5Cmd"; - descriptions[A_QuickKey6] = "sQuick6Cmd"; - descriptions[A_QuickKey7] = "sQuick7Cmd"; - descriptions[A_QuickKey8] = "sQuick8Cmd"; - descriptions[A_QuickKey9] = "sQuick9Cmd"; - descriptions[A_QuickKey10] = "sQuick10Cmd"; - descriptions[A_AlwaysRun] = "sAlways_Run"; - descriptions[A_QuickSave] = "sQuickSaveCmd"; - descriptions[A_QuickLoad] = "sQuickLoadCmd"; - - if (descriptions[action] == "") - return ""; // not configurable - - return "#{" + descriptions[action] + "}"; - } - - std::string InputManager::getActionKeyBindingName (int action) - { - if (mInputBinder->getChannel (action)->getControlsCount () == 0) - return "#{sNone}"; - - ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; - - SDL_Scancode key = mInputBinder->getKeyBinding (c, ICS::Control::INCREASE); - unsigned int mouse = mInputBinder->getMouseButtonBinding (c, ICS::Control::INCREASE); - ICS::InputControlSystem::MouseWheelClick wheel = mInputBinder->getMouseWheelBinding(c, ICS::Control::INCREASE); - if (key != SDL_SCANCODE_UNKNOWN) - return MyGUI::TextIterator::toTagsString(mInputBinder->scancodeToString (key)); - else if (mouse != ICS_MAX_DEVICE_BUTTONS) - return "#{sMouse} " + std::to_string(mouse); - else if (wheel != ICS::InputControlSystem::MouseWheelClick::UNASSIGNED) - switch (wheel) - { - case ICS::InputControlSystem::MouseWheelClick::UP: - return "Mouse Wheel Up"; - case ICS::InputControlSystem::MouseWheelClick::DOWN: - return "Mouse Wheel Down"; - case ICS::InputControlSystem::MouseWheelClick::RIGHT: - return "Mouse Wheel Right"; - case ICS::InputControlSystem::MouseWheelClick::LEFT: - return "Mouse Wheel Left"; - default: - return "#{sNone}"; - } - else - return "#{sNone}"; - } - - std::string InputManager::getActionControllerBindingName (int action) - { - if (mInputBinder->getChannel (action)->getControlsCount () == 0) - return "#{sNone}"; - - ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; - - if (mInputBinder->getJoystickAxisBinding (c, mFakeDeviceID, ICS::Control::INCREASE) != ICS::InputControlSystem::UNASSIGNED) - return sdlControllerAxisToString(mInputBinder->getJoystickAxisBinding (c, mFakeDeviceID, ICS::Control::INCREASE)); - else if (mInputBinder->getJoystickButtonBinding (c, mFakeDeviceID, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS ) - return sdlControllerButtonToString(mInputBinder->getJoystickButtonBinding (c, mFakeDeviceID, ICS::Control::INCREASE)); - else - return "#{sNone}"; + return mBindingsManager->getActionControllerBindingName(action); } std::vector InputManager::getActionKeySorting() { - std::vector ret; - ret.push_back(A_MoveForward); - ret.push_back(A_MoveBackward); - ret.push_back(A_MoveLeft); - ret.push_back(A_MoveRight); - ret.push_back(A_TogglePOV); - ret.push_back(A_ZoomIn); - ret.push_back(A_ZoomOut); - ret.push_back(A_Run); - ret.push_back(A_AlwaysRun); - ret.push_back(A_Sneak); - ret.push_back(A_Activate); - ret.push_back(A_Use); - ret.push_back(A_ToggleWeapon); - ret.push_back(A_ToggleSpell); - ret.push_back(A_CycleSpellLeft); - ret.push_back(A_CycleSpellRight); - ret.push_back(A_CycleWeaponLeft); - ret.push_back(A_CycleWeaponRight); - ret.push_back(A_AutoMove); - ret.push_back(A_Jump); - ret.push_back(A_Inventory); - ret.push_back(A_Journal); - ret.push_back(A_Rest); - ret.push_back(A_Console); - ret.push_back(A_QuickSave); - ret.push_back(A_QuickLoad); - ret.push_back(A_ToggleHUD); - ret.push_back(A_Screenshot); - ret.push_back(A_QuickKeysMenu); - ret.push_back(A_QuickKey1); - ret.push_back(A_QuickKey2); - ret.push_back(A_QuickKey3); - ret.push_back(A_QuickKey4); - ret.push_back(A_QuickKey5); - ret.push_back(A_QuickKey6); - ret.push_back(A_QuickKey7); - ret.push_back(A_QuickKey8); - ret.push_back(A_QuickKey9); - ret.push_back(A_QuickKey10); - - return ret; - } - std::vector InputManager::getActionControllerSorting() - { - std::vector ret; - ret.push_back(A_TogglePOV); - ret.push_back(A_ZoomIn); - ret.push_back(A_ZoomOut); - ret.push_back(A_Sneak); - ret.push_back(A_Activate); - ret.push_back(A_Use); - ret.push_back(A_ToggleWeapon); - ret.push_back(A_ToggleSpell); - ret.push_back(A_AutoMove); - ret.push_back(A_Jump); - ret.push_back(A_Inventory); - ret.push_back(A_Journal); - ret.push_back(A_Rest); - ret.push_back(A_QuickSave); - ret.push_back(A_QuickLoad); - ret.push_back(A_ToggleHUD); - ret.push_back(A_Screenshot); - ret.push_back(A_QuickKeysMenu); - ret.push_back(A_QuickKey1); - ret.push_back(A_QuickKey2); - ret.push_back(A_QuickKey3); - ret.push_back(A_QuickKey4); - ret.push_back(A_QuickKey5); - ret.push_back(A_QuickKey6); - ret.push_back(A_QuickKey7); - ret.push_back(A_QuickKey8); - ret.push_back(A_QuickKey9); - ret.push_back(A_QuickKey10); - ret.push_back(A_CycleSpellLeft); - ret.push_back(A_CycleSpellRight); - ret.push_back(A_CycleWeaponLeft); - ret.push_back(A_CycleWeaponRight); - - return ret; - } - - void InputManager::enableDetectingBindingMode (int action, bool keyboard) - { - mDetectingKeyboard = keyboard; - ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; - mInputBinder->enableDetectingBindingState (c, ICS::Control::INCREASE); - } - - void InputManager::keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control - , SDL_Scancode key, ICS::Control::ControlChangingDirection direction) - { - //Disallow binding escape key - if(key==SDL_SCANCODE_ESCAPE) - { - //Stop binding if esc pressed - mInputBinder->cancelDetectingBindingState(); - MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); - return; - } - - // Disallow binding reserved keys - if (key == SDL_SCANCODE_F3 || key == SDL_SCANCODE_F4 || key == SDL_SCANCODE_F10) - return; - - #ifndef __APPLE__ - // Disallow binding Windows/Meta keys - if (key == SDL_SCANCODE_LGUI || key == SDL_SCANCODE_RGUI) - return; - #endif - - if(!mDetectingKeyboard) - return; - - clearAllKeyBindings(control); - control->setInitialValue(0.0f); - ICS::DetectingBindingListener::keyBindingDetected (ICS, control, key, direction); - MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); - } - - void InputManager::mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control - , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction) - { - // we don't want mouse movement bindings - return; - } - - void InputManager::mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control - , unsigned int button, ICS::Control::ControlChangingDirection direction) - { - if(!mDetectingKeyboard) - return; - clearAllKeyBindings(control); - control->setInitialValue(0.0f); - ICS::DetectingBindingListener::mouseButtonBindingDetected (ICS, control, button, direction); - MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + return mBindingsManager->getActionKeySorting(); } - void InputManager::mouseWheelBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control - , ICS::InputControlSystem::MouseWheelClick click, ICS::Control::ControlChangingDirection direction) - { - if(!mDetectingKeyboard) - return; - clearAllKeyBindings(control); - control->setInitialValue(0.0f); - ICS::DetectingBindingListener::mouseWheelBindingDetected(ICS, control, click, direction); - MWBase::Environment::get().getWindowManager()->notifyInputActionBound(); - } - - void InputManager::joystickAxisBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control - , int axis, ICS::Control::ControlChangingDirection direction) - { - //only allow binding to the trigers - if(axis != SDL_CONTROLLER_AXIS_TRIGGERLEFT && axis != SDL_CONTROLLER_AXIS_TRIGGERRIGHT) - return; - if(mDetectingKeyboard) - return; - - clearAllControllerBindings(control); - control->setValue(0.5f); //axis bindings must start at 0.5 - control->setInitialValue(0.5f); - ICS::DetectingBindingListener::joystickAxisBindingDetected (ICS, deviceID, control, axis, direction); - MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); - } - - void InputManager::joystickButtonBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control - , unsigned int button, ICS::Control::ControlChangingDirection direction) - { - if(mDetectingKeyboard) - return; - clearAllControllerBindings(control); - control->setInitialValue(0.0f); - ICS::DetectingBindingListener::joystickButtonBindingDetected (ICS, deviceID, control, button, direction); - MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); - } - - void InputManager::clearAllKeyBindings (ICS::Control* control) + std::vector InputManager::getActionControllerSorting() { - // right now we don't really need multiple bindings for the same action, so remove all others first - if (mInputBinder->getKeyBinding (control, ICS::Control::INCREASE) != SDL_SCANCODE_UNKNOWN) - mInputBinder->removeKeyBinding (mInputBinder->getKeyBinding (control, ICS::Control::INCREASE)); - if (mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) - mInputBinder->removeMouseButtonBinding (mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE)); - if (mInputBinder->getMouseWheelBinding (control, ICS::Control::INCREASE) != ICS::InputControlSystem::MouseWheelClick::UNASSIGNED) - mInputBinder->removeMouseWheelBinding (mInputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE)); + return mBindingsManager->getActionControllerSorting(); } - void InputManager::clearAllControllerBindings (ICS::Control* control) + void InputManager::enableDetectingBindingMode(int action, bool keyboard) { - // right now we don't really need multiple bindings for the same action, so remove all others first - if (mInputBinder->getJoystickAxisBinding (control, mFakeDeviceID, ICS::Control::INCREASE) != SDL_SCANCODE_UNKNOWN) - mInputBinder->removeJoystickAxisBinding (mFakeDeviceID, mInputBinder->getJoystickAxisBinding (control, mFakeDeviceID, ICS::Control::INCREASE)); - if (mInputBinder->getJoystickButtonBinding (control, mFakeDeviceID, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) - mInputBinder->removeJoystickButtonBinding (mFakeDeviceID, mInputBinder->getJoystickButtonBinding (control, mFakeDeviceID, ICS::Control::INCREASE)); + mBindingsManager->enableDetectingBindingMode(action, keyboard); } int InputManager::countSavedGameRecords() const @@ -810,12 +267,12 @@ namespace MWInput void InputManager::resetToDefaultKeyBindings() { - loadKeyDefaults(true); + mBindingsManager->loadKeyDefaults(true); } void InputManager::resetToDefaultControllerBindings() { - loadControllerDefaults(true); + mBindingsManager->loadControllerDefaults(true); } void InputManager::setJoystickLastUsed(bool enabled) @@ -827,4 +284,9 @@ namespace MWInput { return mControllerManager->joystickLastUsed(); } + + void InputManager::executeAction(int action) + { + mActionManager->executeAction(action); + } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index ac056809e..8e8096d92 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -8,9 +8,6 @@ #include #include -#include -#include - #include #include #include @@ -23,6 +20,7 @@ namespace MWInput { class ControlSwitch; class ActionManager; + class BindingsManager; class ControllerManager; class KeyboardManager; class MouseManager; @@ -39,11 +37,6 @@ namespace MWBase class WindowManager; } -namespace ICS -{ - class InputControlSystem; -} - namespace Files { struct ConfigurationManager; @@ -62,9 +55,7 @@ namespace MWInput * @brief Class that handles all input and key bindings for OpenMW. */ class InputManager : - public MWBase::InputManager, - public ICS::ChannelListener, - public ICS::DetectingBindingListener + public MWBase::InputManager { public: InputManager( @@ -89,6 +80,7 @@ namespace MWInput virtual void setDragDrop(bool dragDrop); virtual void setGamepadGuiCursorEnabled(bool enabled); + virtual void setAttemptJump(bool jumping); virtual void toggleControlSwitch (const std::string& sw, bool value); virtual bool getControlSwitch (const std::string& sw); @@ -106,55 +98,25 @@ namespace MWInput virtual void setJoystickLastUsed(bool enabled); virtual bool joystickLastUsed(); - virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); - - virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control - , SDL_Scancode key, ICS::Control::ControlChangingDirection direction); - - virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control - , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction); - - virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control - , unsigned int button, ICS::Control::ControlChangingDirection direction); - - virtual void mouseWheelBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control - , ICS::InputControlSystem::MouseWheelClick click, ICS::Control::ControlChangingDirection direction); - - virtual void joystickAxisBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control - , int axis, ICS::Control::ControlChangingDirection direction); - - virtual void joystickButtonBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control - , unsigned int button, ICS::Control::ControlChangingDirection direction); - - void clearAllKeyBindings (ICS::Control* control); - void clearAllControllerBindings (ICS::Control* control); - virtual int countSavedGameRecords() const; virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress); virtual void readRecord(ESM::ESMReader& reader, uint32_t type); - virtual void setPlayerControlsEnabled(bool enabled); - virtual void resetIdleTime(); - private: - ICS::InputControlSystem* mInputBinder; + virtual void executeAction(int action); + private: SDLUtil::InputWrapper* mInputWrapper; - std::string mUserFile; - - bool mDragDrop; - bool mGrabCursor; bool mGuiCursorEnabled; - bool mDetectingKeyboard; - ControlSwitch* mControlSwitch; ActionManager* mActionManager; + BindingsManager* mBindingsManager; ControllerManager* mControllerManager; KeyboardManager* mKeyboardManager; MouseManager* mMouseManager; @@ -166,15 +128,11 @@ namespace MWInput void updateCursorMode(); - void quickKey (int index); + void quickKey(int index); void showQuickKeysMenu(); - bool actionIsActive (int id); - void loadKeyDefaults(bool force = false); void loadControllerDefaults(bool force = false); - - int mFakeDeviceID; //As we only support one controller at a time, use a fake deviceID so we don't lose bindings when switching controllers }; } #endif diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index 773ea4028..efe911593 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -2,10 +2,6 @@ #include -#include - -#include - #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwbase/statemanager.hpp" @@ -16,22 +12,18 @@ #include "actionmanager.hpp" #include "actions.hpp" +#include "bindingsmanager.hpp" +#include "sdlmappings.hpp" namespace MWInput { - KeyboardManager::KeyboardManager(ICS::InputControlSystem* inputBinder, SDLUtil::InputWrapper* inputWrapper, ActionManager* actionManager) - : mInputBinder(inputBinder) - , mInputWrapper(inputWrapper) + KeyboardManager::KeyboardManager(BindingsManager* bindingsManager, ActionManager* actionManager) + : mBindingsManager(bindingsManager) , mActionManager(actionManager) , mControlsDisabled(false) { } - bool KeyboardManager::actionIsActive (int id) - { - return (mInputBinder->getChannel(id)->getValue ()==1.0); - } - void KeyboardManager::textInput(const SDL_TextInputEvent &arg) { MyGUI::UString ustring(&arg.text[0]); @@ -42,39 +34,38 @@ namespace MWInput void KeyboardManager::keyPressed(const SDL_KeyboardEvent &arg) { - // HACK: to make Morrowind's default keybinding for the console work without printing an extra "^" upon closing + // HACK: to make default keybinding for the console work without printing an extra "^" upon closing // This assumes that SDL_TextInput events always come *after* the key event // (which is somewhat reasonable, and hopefully true for all SDL platforms) - OIS::KeyCode kc = mInputWrapper->sdl2OISKeyCode(arg.keysym.sym); - if (mInputBinder->getKeyBinding(mInputBinder->getControl(A_Console), ICS::Control::INCREASE) - == arg.keysym.scancode + auto kc = sdlKeyToMyGUI(arg.keysym.sym); + if (mBindingsManager->getKeyBinding(A_Console) == arg.keysym.scancode && MWBase::Environment::get().getWindowManager()->isConsoleMode()) SDL_StopTextInput(); bool consumed = false; - if (kc != OIS::KC_UNASSIGNED && !mInputBinder->detectingBindingState()) + if (kc != MyGUI::KeyCode::None && !mBindingsManager->isDetectingBindingState()) { - consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Enum(kc), 0, arg.repeat); + consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(kc, 0, arg.repeat); if (SDL_IsTextInputActive() && // Little trick to check if key is printable - ( !(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym))) + (!(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym))) consumed = true; - MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!consumed); + mBindingsManager->setPlayerControlsEnabled(!consumed); } if (arg.repeat) return; if (!mControlsDisabled && !consumed) - mInputBinder->keyPressed(arg); + mBindingsManager->keyPressed(arg); MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); } void KeyboardManager::keyReleased(const SDL_KeyboardEvent &arg) { MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); - OIS::KeyCode kc = mInputWrapper->sdl2OISKeyCode(arg.keysym.sym); + auto kc = sdlKeyToMyGUI(arg.keysym.sym); - if (!mInputBinder->detectingBindingState()) - MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); - mInputBinder->keyReleased(arg); + if (!mBindingsManager->isDetectingBindingState()) + mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(kc)); + mBindingsManager->keyReleased(arg); } } diff --git a/apps/openmw/mwinput/keyboardmanager.hpp b/apps/openmw/mwinput/keyboardmanager.hpp index 9d1c0b4fd..bba2f5dc4 100644 --- a/apps/openmw/mwinput/keyboardmanager.hpp +++ b/apps/openmw/mwinput/keyboardmanager.hpp @@ -9,19 +9,15 @@ namespace SDLUtil class InputWrapper; } -namespace ICS -{ - class InputControlSystem; -} - namespace MWInput { class ActionManager; + class BindingsManager; class KeyboardManager : public SDLUtil::KeyListener { public: - KeyboardManager(ICS::InputControlSystem* inputBinder, SDLUtil::InputWrapper* inputWrapper, ActionManager* actionManager); + KeyboardManager(BindingsManager* bindingsManager, ActionManager* actionManager); virtual ~KeyboardManager() = default; @@ -32,10 +28,7 @@ namespace MWInput void setControlsDisabled(bool disabled) { mControlsDisabled = disabled; } private: - bool actionIsActive(int id); - - ICS::InputControlSystem* mInputBinder; - SDLUtil::InputWrapper* mInputWrapper; + BindingsManager* mBindingsManager; ActionManager* mActionManager; diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index 3fbfc7813..30e32bef8 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -8,8 +8,6 @@ #include #include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" @@ -18,16 +16,17 @@ #include "../mwworld/player.hpp" #include "actions.hpp" +#include "bindingsmanager.hpp" #include "sdlmappings.hpp" namespace MWInput { - MouseManager::MouseManager(ICS::InputControlSystem* inputBinder, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window) + MouseManager::MouseManager(BindingsManager* bindingsManager, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window) : mInvertX(Settings::Manager::getBool("invert x axis", "Input")) , mInvertY(Settings::Manager::getBool("invert y axis", "Input")) , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) - , mInputBinder(inputBinder) + , mBindingsManager(bindingsManager) , mInputWrapper(inputWrapper) , mInvUiScalingFactor(1.f) , mGuiCursorX(0) @@ -54,22 +53,22 @@ namespace MWInput void MouseManager::processChangedSettings(const Settings::CategorySettingVector& changed) { - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) + for (const auto& setting : changed) { - if (it->first == "Input" && it->second == "invert x axis") + if (setting.first == "Input" && setting.second == "invert x axis") mInvertX = Settings::Manager::getBool("invert x axis", "Input"); - if (it->first == "Input" && it->second == "invert y axis") + if (setting.first == "Input" && setting.second == "invert y axis") mInvertY = Settings::Manager::getBool("invert y axis", "Input"); - if (it->first == "Input" && it->second == "camera sensitivity") + if (setting.first == "Input" && setting.second == "camera sensitivity") mCameraSensitivity = Settings::Manager::getFloat("camera sensitivity", "Input"); } } void MouseManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg) { - mInputBinder->mouseMoved (arg); + mBindingsManager->mouseMoved(arg); MWBase::InputManager* input = MWBase::Environment::get().getInputManager(); input->setJoystickLastUsed(false); @@ -122,26 +121,26 @@ namespace MWInput { MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); - if(mInputBinder->detectingBindingState()) + if(mBindingsManager->isDetectingBindingState()) { - mInputBinder->mouseReleased (arg, id); + mBindingsManager->mouseReleased(arg, id); } else { bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); guiMode = MyGUI::InputManager::getInstance().injectMouseRelease(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode; - if(mInputBinder->detectingBindingState()) return; // don't allow same mouseup to bind as initiated bind + if(mBindingsManager->isDetectingBindingState()) return; // don't allow same mouseup to bind as initiated bind - MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!guiMode); - mInputBinder->mouseReleased (arg, id); + mBindingsManager->setPlayerControlsEnabled(!guiMode); + mBindingsManager->mouseReleased(arg, id); } } void MouseManager::mouseWheelMoved(const SDL_MouseWheelEvent &arg) { - if (mInputBinder->detectingBindingState() || !mControlsDisabled) - mInputBinder->mouseWheelMoved(arg); + if (mBindingsManager->isDetectingBindingState() || !mControlsDisabled) + mBindingsManager->mouseWheelMoved(arg); MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); } @@ -166,11 +165,11 @@ namespace MWInput MWBase::Environment::get().getWindowManager()->setCursorActive(true); } - MWBase::Environment::get().getInputManager()->setPlayerControlsEnabled(!guiMode); + mBindingsManager->setPlayerControlsEnabled(!guiMode); // Don't trigger any mouse bindings while in settings menu, otherwise rebinding controls becomes impossible if (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings) - mInputBinder->mousePressed (arg, id); + mBindingsManager->mousePressed(arg, id); } void MouseManager::update(float dt, bool disableControls) @@ -180,8 +179,8 @@ namespace MWInput if (!mMouseLookEnabled) return; - float xAxis = mInputBinder->getChannel(A_LookLeftRight)->getValue()*2.0f-1.0f; - float yAxis = mInputBinder->getChannel(A_LookUpDown)->getValue()*2.0f-1.0f; + float xAxis = mBindingsManager->getActionValue(A_LookLeftRight)*2.0f-1.0f; + float yAxis = mBindingsManager->getActionValue(A_LookUpDown)*2.0f-1.0f; if (xAxis == 0 && yAxis == 0) return; diff --git a/apps/openmw/mwinput/mousemanager.hpp b/apps/openmw/mwinput/mousemanager.hpp index 6e59a639b..2686c59b7 100644 --- a/apps/openmw/mwinput/mousemanager.hpp +++ b/apps/openmw/mwinput/mousemanager.hpp @@ -9,22 +9,14 @@ namespace SDLUtil class InputWrapper; } -namespace MWWorld -{ - class Player; -} - -namespace ICS -{ - class InputControlSystem; -} - namespace MWInput { + class BindingsManager; + class MouseManager : public SDLUtil::MouseListener { public: - MouseManager(ICS::InputControlSystem* inputBinder, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window); + MouseManager(BindingsManager* bindingsManager, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window); virtual ~MouseManager() = default; @@ -53,7 +45,7 @@ namespace MWInput float mCameraSensitivity; float mCameraYMultiplier; - ICS::InputControlSystem* mInputBinder; + BindingsManager* mBindingsManager; SDLUtil::InputWrapper* mInputWrapper; float mInvUiScalingFactor; diff --git a/apps/openmw/mwinput/sdlmappings.cpp b/apps/openmw/mwinput/sdlmappings.cpp index 53c9e77fe..8a9ccf4e7 100644 --- a/apps/openmw/mwinput/sdlmappings.cpp +++ b/apps/openmw/mwinput/sdlmappings.cpp @@ -1,5 +1,7 @@ #include "sdlmappings.hpp" +#include + #include #include @@ -78,4 +80,142 @@ namespace MWInput //MyGUI's buttons are 0 indexed return MyGUI::MouseButton::Enum(button - 1); } + + void initKeyMap(std::map& keyMap) + { + keyMap[SDLK_UNKNOWN] = MyGUI::KeyCode::None; + keyMap[SDLK_ESCAPE] = MyGUI::KeyCode::Escape; + keyMap[SDLK_1] = MyGUI::KeyCode::One; + keyMap[SDLK_2] = MyGUI::KeyCode::Two; + keyMap[SDLK_3] = MyGUI::KeyCode::Three; + keyMap[SDLK_4] = MyGUI::KeyCode::Four; + keyMap[SDLK_5] = MyGUI::KeyCode::Five; + keyMap[SDLK_6] = MyGUI::KeyCode::Six; + keyMap[SDLK_7] = MyGUI::KeyCode::Seven; + keyMap[SDLK_8] = MyGUI::KeyCode::Eight; + keyMap[SDLK_9] = MyGUI::KeyCode::Nine; + keyMap[SDLK_0] = MyGUI::KeyCode::Zero; + keyMap[SDLK_MINUS] = MyGUI::KeyCode::Minus; + keyMap[SDLK_EQUALS] = MyGUI::KeyCode::Equals; + keyMap[SDLK_BACKSPACE] = MyGUI::KeyCode::Backspace; + keyMap[SDLK_TAB] = MyGUI::KeyCode::Tab; + keyMap[SDLK_q] = MyGUI::KeyCode::Q; + keyMap[SDLK_w] = MyGUI::KeyCode::W; + keyMap[SDLK_e] = MyGUI::KeyCode::E; + keyMap[SDLK_r] = MyGUI::KeyCode::R; + keyMap[SDLK_t] = MyGUI::KeyCode::T; + keyMap[SDLK_y] = MyGUI::KeyCode::Y; + keyMap[SDLK_u] = MyGUI::KeyCode::U; + keyMap[SDLK_i] = MyGUI::KeyCode::I; + keyMap[SDLK_o] = MyGUI::KeyCode::O; + keyMap[SDLK_p] = MyGUI::KeyCode::P; + keyMap[SDLK_RETURN] = MyGUI::KeyCode::Return; + keyMap[SDLK_a] = MyGUI::KeyCode::A; + keyMap[SDLK_s] = MyGUI::KeyCode::S; + keyMap[SDLK_d] = MyGUI::KeyCode::D; + keyMap[SDLK_f] = MyGUI::KeyCode::F; + keyMap[SDLK_g] = MyGUI::KeyCode::G; + keyMap[SDLK_h] = MyGUI::KeyCode::H; + keyMap[SDLK_j] = MyGUI::KeyCode::J; + keyMap[SDLK_k] = MyGUI::KeyCode::K; + keyMap[SDLK_l] = MyGUI::KeyCode::L; + keyMap[SDLK_SEMICOLON] = MyGUI::KeyCode::Semicolon; + keyMap[SDLK_QUOTE] = MyGUI::KeyCode::Apostrophe; + keyMap[SDLK_BACKQUOTE] = MyGUI::KeyCode::Grave; + keyMap[SDLK_LSHIFT] = MyGUI::KeyCode::LeftShift; + keyMap[SDLK_BACKSLASH] = MyGUI::KeyCode::Backslash; + keyMap[SDLK_z] = MyGUI::KeyCode::Z; + keyMap[SDLK_x] = MyGUI::KeyCode::X; + keyMap[SDLK_c] = MyGUI::KeyCode::C; + keyMap[SDLK_v] = MyGUI::KeyCode::V; + keyMap[SDLK_b] = MyGUI::KeyCode::B; + keyMap[SDLK_n] = MyGUI::KeyCode::N; + keyMap[SDLK_m] = MyGUI::KeyCode::M; + keyMap[SDLK_COMMA] = MyGUI::KeyCode::Comma; + keyMap[SDLK_PERIOD] = MyGUI::KeyCode::Period; + keyMap[SDLK_SLASH] = MyGUI::KeyCode::Slash; + keyMap[SDLK_RSHIFT] = MyGUI::KeyCode::RightShift; + keyMap[SDLK_KP_MULTIPLY] = MyGUI::KeyCode::Multiply; + keyMap[SDLK_LALT] = MyGUI::KeyCode::LeftAlt; + keyMap[SDLK_SPACE] = MyGUI::KeyCode::Space; + keyMap[SDLK_CAPSLOCK] = MyGUI::KeyCode::Capital; + keyMap[SDLK_F1] = MyGUI::KeyCode::F1; + keyMap[SDLK_F2] = MyGUI::KeyCode::F2; + keyMap[SDLK_F3] = MyGUI::KeyCode::F3; + keyMap[SDLK_F4] = MyGUI::KeyCode::F4; + keyMap[SDLK_F5] = MyGUI::KeyCode::F5; + keyMap[SDLK_F6] = MyGUI::KeyCode::F6; + keyMap[SDLK_F7] = MyGUI::KeyCode::F7; + keyMap[SDLK_F8] = MyGUI::KeyCode::F8; + keyMap[SDLK_F9] = MyGUI::KeyCode::F9; + keyMap[SDLK_F10] = MyGUI::KeyCode::F10; + keyMap[SDLK_NUMLOCKCLEAR] = MyGUI::KeyCode::NumLock; + keyMap[SDLK_SCROLLLOCK] = MyGUI::KeyCode::ScrollLock; + keyMap[SDLK_KP_7] = MyGUI::KeyCode::Numpad7; + keyMap[SDLK_KP_8] = MyGUI::KeyCode::Numpad8; + keyMap[SDLK_KP_9] = MyGUI::KeyCode::Numpad9; + keyMap[SDLK_KP_MINUS] = MyGUI::KeyCode::Subtract; + keyMap[SDLK_KP_4] = MyGUI::KeyCode::Numpad4; + keyMap[SDLK_KP_5] = MyGUI::KeyCode::Numpad5; + keyMap[SDLK_KP_6] = MyGUI::KeyCode::Numpad6; + keyMap[SDLK_KP_PLUS] = MyGUI::KeyCode::Add; + keyMap[SDLK_KP_1] = MyGUI::KeyCode::Numpad1; + keyMap[SDLK_KP_2] = MyGUI::KeyCode::Numpad2; + keyMap[SDLK_KP_3] = MyGUI::KeyCode::Numpad3; + keyMap[SDLK_KP_0] = MyGUI::KeyCode::Numpad0; + keyMap[SDLK_KP_PERIOD] = MyGUI::KeyCode::Decimal; + keyMap[SDLK_F11] = MyGUI::KeyCode::F11; + keyMap[SDLK_F12] = MyGUI::KeyCode::F12; + keyMap[SDLK_F13] = MyGUI::KeyCode::F13; + keyMap[SDLK_F14] = MyGUI::KeyCode::F14; + keyMap[SDLK_F15] = MyGUI::KeyCode::F15; + keyMap[SDLK_KP_EQUALS] = MyGUI::KeyCode::NumpadEquals; + keyMap[SDLK_COLON] = MyGUI::KeyCode::Colon; + keyMap[SDLK_KP_ENTER] = MyGUI::KeyCode::NumpadEnter; + keyMap[SDLK_KP_DIVIDE] = MyGUI::KeyCode::Divide; + keyMap[SDLK_SYSREQ] = MyGUI::KeyCode::SysRq; + keyMap[SDLK_RALT] = MyGUI::KeyCode::RightAlt; + keyMap[SDLK_HOME] = MyGUI::KeyCode::Home; + keyMap[SDLK_UP] = MyGUI::KeyCode::ArrowUp; + keyMap[SDLK_PAGEUP] = MyGUI::KeyCode::PageUp; + keyMap[SDLK_LEFT] = MyGUI::KeyCode::ArrowLeft; + keyMap[SDLK_RIGHT] = MyGUI::KeyCode::ArrowRight; + keyMap[SDLK_END] = MyGUI::KeyCode::End; + keyMap[SDLK_DOWN] = MyGUI::KeyCode::ArrowDown; + keyMap[SDLK_PAGEDOWN] = MyGUI::KeyCode::PageDown; + keyMap[SDLK_INSERT] = MyGUI::KeyCode::Insert; + keyMap[SDLK_DELETE] = MyGUI::KeyCode::Delete; + keyMap[SDLK_APPLICATION] = MyGUI::KeyCode::AppMenu; + +//The function of the Ctrl and Meta keys are switched on macOS compared to other platforms. +//For instance] = Cmd+C versus Ctrl+C to copy from the system clipboard +#if defined(__APPLE__) + keyMap[SDLK_LGUI] = MyGUI::KeyCode::LeftControl; + keyMap[SDLK_RGUI] = MyGUI::KeyCode::RightControl; + keyMap[SDLK_LCTRL] = MyGUI::KeyCode::LeftWindows; + keyMap[SDLK_RCTRL] = MyGUI::KeyCode::RightWindows; +#else + keyMap[SDLK_LGUI] = MyGUI::KeyCode::LeftWindows; + keyMap[SDLK_RGUI] = MyGUI::KeyCode::RightWindows; + keyMap[SDLK_LCTRL] = MyGUI::KeyCode::LeftControl; + keyMap[SDLK_RCTRL] = MyGUI::KeyCode::RightControl; +#endif + } + + MyGUI::KeyCode sdlKeyToMyGUI(SDL_Keycode code) + { + static std::map keyMap; + if (keyMap.empty()) + { + initKeyMap(keyMap); + } + + MyGUI::KeyCode kc = MyGUI::KeyCode::None; + + auto foundKey = keyMap.find(code); + if(foundKey != keyMap.end()) + kc = foundKey->second; + + return kc; + } } diff --git a/apps/openmw/mwinput/sdlmappings.hpp b/apps/openmw/mwinput/sdlmappings.hpp index dd6d750cb..0cdd4694f 100644 --- a/apps/openmw/mwinput/sdlmappings.hpp +++ b/apps/openmw/mwinput/sdlmappings.hpp @@ -3,7 +3,9 @@ #include -#include +#include + +#include namespace MyGUI { @@ -17,5 +19,7 @@ namespace MWInput std::string sdlControllerAxisToString(int axis); MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button); + + MyGUI::KeyCode sdlKeyToMyGUI(SDL_Keycode code); } #endif diff --git a/apps/openmw/mwinput/sensormanager.cpp b/apps/openmw/mwinput/sensormanager.cpp index 27879d214..f78607fa2 100644 --- a/apps/openmw/mwinput/sensormanager.cpp +++ b/apps/openmw/mwinput/sensormanager.cpp @@ -156,30 +156,30 @@ namespace MWInput void SensorManager::processChangedSettings(const Settings::CategorySettingVector& changed) { - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) + for (const auto& setting : changed) { - if (it->first == "Input" && it->second == "invert x axis") + if (setting.first == "Input" && setting.second == "invert x axis") mInvertX = Settings::Manager::getBool("invert x axis", "Input"); - if (it->first == "Input" && it->second == "invert y axis") + if (setting.first == "Input" && setting.second == "invert y axis") mInvertY = Settings::Manager::getBool("invert y axis", "Input"); - if (it->first == "Input" && it->second == "gyro horizontal sensitivity") + if (setting.first == "Input" && setting.second == "gyro horizontal sensitivity") mGyroHSensitivity = Settings::Manager::getFloat("gyro horizontal sensitivity", "Input"); - if (it->first == "Input" && it->second == "gyro vertical sensitivity") + if (setting.first == "Input" && setting.second == "gyro vertical sensitivity") mGyroVSensitivity = Settings::Manager::getFloat("gyro vertical sensitivity", "Input"); - if (it->first == "Input" && it->second == "enable gyroscope") + if (setting.first == "Input" && setting.second == "enable gyroscope") init(); - if (it->first == "Input" && it->second == "gyro horizontal axis") + if (setting.first == "Input" && setting.second == "gyro horizontal axis") correctGyroscopeAxes(); - if (it->first == "Input" && it->second == "gyro vertical axis") + if (setting.first == "Input" && setting.second == "gyro vertical axis") correctGyroscopeAxes(); - if (it->first == "Input" && it->second == "gyro input threshold") + if (setting.first == "Input" && setting.second == "gyro input threshold") mGyroInputThreshold = Settings::Manager::getFloat("gyro input threshold", "Input"); } } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 06c777c02..a7d70b4e0 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -139,7 +139,7 @@ add_component_dir (fontloader ) add_component_dir (sdlutil - sdlgraphicswindow imagetosurface sdlinputwrapper sdlvideowrapper OISCompat events sdlcursormanager + sdlgraphicswindow imagetosurface sdlinputwrapper sdlvideowrapper events sdlcursormanager ) add_component_dir (version diff --git a/components/sdlutil/OISCompat.hpp b/components/sdlutil/OISCompat.hpp deleted file mode 100644 index a0acc5837..000000000 --- a/components/sdlutil/OISCompat.hpp +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef OIS_SDL_COMPAT_H -#define OIS_SDL_COMPAT_H - -#include -#include - -namespace OIS -{ -//! Keyboard scan codes -enum KeyCode -{ - KC_UNASSIGNED = 0x00, - KC_ESCAPE = 0x01, - KC_1 = 0x02, - KC_2 = 0x03, - KC_3 = 0x04, - KC_4 = 0x05, - KC_5 = 0x06, - KC_6 = 0x07, - KC_7 = 0x08, - KC_8 = 0x09, - KC_9 = 0x0A, - KC_0 = 0x0B, - KC_MINUS = 0x0C, // - on main keyboard - KC_EQUALS = 0x0D, - KC_BACK = 0x0E, // backspace - KC_TAB = 0x0F, - KC_Q = 0x10, - KC_W = 0x11, - KC_E = 0x12, - KC_R = 0x13, - KC_T = 0x14, - KC_Y = 0x15, - KC_U = 0x16, - KC_I = 0x17, - KC_O = 0x18, - KC_P = 0x19, - KC_LBRACKET = 0x1A, - KC_RBRACKET = 0x1B, - KC_RETURN = 0x1C, // Enter on main keyboard - KC_LCONTROL = 0x1D, - KC_A = 0x1E, - KC_S = 0x1F, - KC_D = 0x20, - KC_F = 0x21, - KC_G = 0x22, - KC_H = 0x23, - KC_J = 0x24, - KC_K = 0x25, - KC_L = 0x26, - KC_SEMICOLON = 0x27, - KC_APOSTROPHE = 0x28, - KC_GRAVE = 0x29, // accent - KC_LSHIFT = 0x2A, - KC_BACKSLASH = 0x2B, - KC_Z = 0x2C, - KC_X = 0x2D, - KC_C = 0x2E, - KC_V = 0x2F, - KC_B = 0x30, - KC_N = 0x31, - KC_M = 0x32, - KC_COMMA = 0x33, - KC_PERIOD = 0x34, // . on main keyboard - KC_SLASH = 0x35, // / on main keyboard - KC_RSHIFT = 0x36, - KC_MULTIPLY = 0x37, // * on numeric keypad - KC_LMENU = 0x38, // left Alt - KC_SPACE = 0x39, - KC_CAPITAL = 0x3A, - KC_F1 = 0x3B, - KC_F2 = 0x3C, - KC_F3 = 0x3D, - KC_F4 = 0x3E, - KC_F5 = 0x3F, - KC_F6 = 0x40, - KC_F7 = 0x41, - KC_F8 = 0x42, - KC_F9 = 0x43, - KC_F10 = 0x44, - KC_NUMLOCK = 0x45, - KC_SCROLL = 0x46, // Scroll Lock - KC_NUMPAD7 = 0x47, - KC_NUMPAD8 = 0x48, - KC_NUMPAD9 = 0x49, - KC_SUBTRACT = 0x4A, // - on numeric keypad - KC_NUMPAD4 = 0x4B, - KC_NUMPAD5 = 0x4C, - KC_NUMPAD6 = 0x4D, - KC_ADD = 0x4E, // + on numeric keypad - KC_NUMPAD1 = 0x4F, - KC_NUMPAD2 = 0x50, - KC_NUMPAD3 = 0x51, - KC_NUMPAD0 = 0x52, - KC_DECIMAL = 0x53, // . on numeric keypad - KC_OEM_102 = 0x56, // < > | on UK/Germany keyboards - KC_F11 = 0x57, - KC_F12 = 0x58, - KC_F13 = 0x64, // (NEC PC98) - KC_F14 = 0x65, // (NEC PC98) - KC_F15 = 0x66, // (NEC PC98) - KC_KANA = 0x70, // (Japanese keyboard) - KC_ABNT_C1 = 0x73, // / ? on Portugese (Brazilian) keyboards - KC_CONVERT = 0x79, // (Japanese keyboard) - KC_NOCONVERT = 0x7B, // (Japanese keyboard) - KC_YEN = 0x7D, // (Japanese keyboard) - KC_ABNT_C2 = 0x7E, // Numpad . on Portugese (Brazilian) keyboards - KC_NUMPADEQUALS= 0x8D, // = on numeric keypad (NEC PC98) - KC_PREVTRACK = 0x90, // Previous Track (KC_CIRCUMFLEX on Japanese keyboard) - KC_AT = 0x91, // (NEC PC98) - KC_COLON = 0x92, // (NEC PC98) - KC_UNDERLINE = 0x93, // (NEC PC98) - KC_KANJI = 0x94, // (Japanese keyboard) - KC_STOP = 0x95, // (NEC PC98) - KC_AX = 0x96, // (Japan AX) - KC_UNLABELED = 0x97, // (J3100) - KC_NEXTTRACK = 0x99, // Next Track - KC_NUMPADENTER = 0x9C, // Enter on numeric keypad - KC_RCONTROL = 0x9D, - KC_MUTE = 0xA0, // Mute - KC_CALCULATOR = 0xA1, // Calculator - KC_PLAYPAUSE = 0xA2, // Play / Pause - KC_MEDIASTOP = 0xA4, // Media Stop - KC_VOLUMEDOWN = 0xAE, // Volume - - KC_VOLUMEUP = 0xB0, // Volume + - KC_WEBHOME = 0xB2, // Web home - KC_NUMPADCOMMA = 0xB3, // , on numeric keypad (NEC PC98) - KC_DIVIDE = 0xB5, // / on numeric keypad - KC_SYSRQ = 0xB7, - KC_RMENU = 0xB8, // right Alt - KC_PAUSE = 0xC5, // Pause - KC_HOME = 0xC7, // Home on arrow keypad - KC_UP = 0xC8, // UpArrow on arrow keypad - KC_PGUP = 0xC9, // PgUp on arrow keypad - KC_LEFT = 0xCB, // LeftArrow on arrow keypad - KC_RIGHT = 0xCD, // RightArrow on arrow keypad - KC_END = 0xCF, // End on arrow keypad - KC_DOWN = 0xD0, // DownArrow on arrow keypad - KC_PGDOWN = 0xD1, // PgDn on arrow keypad - KC_INSERT = 0xD2, // Insert on arrow keypad - KC_DELETE = 0xD3, // Delete on arrow keypad - KC_LWIN = 0xDB, // Left Windows key - KC_RWIN = 0xDC, // Right Windows key - KC_APPS = 0xDD, // AppMenu key - KC_POWER = 0xDE, // System Power - KC_SLEEP = 0xDF, // System Sleep - KC_WAKE = 0xE3, // System Wake - KC_WEBSEARCH = 0xE5, // Web Search - KC_WEBFAVORITES= 0xE6, // Web Favorites - KC_WEBREFRESH = 0xE7, // Web Refresh - KC_WEBSTOP = 0xE8, // Web Stop - KC_WEBFORWARD = 0xE9, // Web Forward - KC_WEBBACK = 0xEA, // Web Back - KC_MYCOMPUTER = 0xEB, // My Computer - KC_MAIL = 0xEC, // Mail - KC_MEDIASELECT = 0xED // Media Select -}; -} -#endif diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index 3ea11c6d0..8d6a124e2 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -33,8 +33,6 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v mWindowHasFocus(true), mMouseInWindow(true) { - _setupOISKeys(); - Uint32 flags = SDL_GetWindowFlags(mSDLWindow); mWindowHasFocus = (flags & SDL_WINDOW_INPUT_FOCUS); mMouseInWindow = (flags & SDL_WINDOW_MOUSE_FOCUS); @@ -397,139 +395,4 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v return pack_evt; } - - OIS::KeyCode InputWrapper::sdl2OISKeyCode(SDL_Keycode code) - { - OIS::KeyCode kc = OIS::KC_UNASSIGNED; - - KeyMap::const_iterator ois_equiv = mKeyMap.find(code); - - if(ois_equiv != mKeyMap.end()) - kc = ois_equiv->second; - - return kc; - } - - void InputWrapper::_setupOISKeys() - { - //lifted from OIS's SDLKeyboard.cpp - - mKeyMap.insert( KeyMap::value_type(SDLK_UNKNOWN, OIS::KC_UNASSIGNED)); - mKeyMap.insert( KeyMap::value_type(SDLK_ESCAPE, OIS::KC_ESCAPE) ); - mKeyMap.insert( KeyMap::value_type(SDLK_1, OIS::KC_1) ); - mKeyMap.insert( KeyMap::value_type(SDLK_2, OIS::KC_2) ); - mKeyMap.insert( KeyMap::value_type(SDLK_3, OIS::KC_3) ); - mKeyMap.insert( KeyMap::value_type(SDLK_4, OIS::KC_4) ); - mKeyMap.insert( KeyMap::value_type(SDLK_5, OIS::KC_5) ); - mKeyMap.insert( KeyMap::value_type(SDLK_6, OIS::KC_6) ); - mKeyMap.insert( KeyMap::value_type(SDLK_7, OIS::KC_7) ); - mKeyMap.insert( KeyMap::value_type(SDLK_8, OIS::KC_8) ); - mKeyMap.insert( KeyMap::value_type(SDLK_9, OIS::KC_9) ); - mKeyMap.insert( KeyMap::value_type(SDLK_0, OIS::KC_0) ); - mKeyMap.insert( KeyMap::value_type(SDLK_MINUS, OIS::KC_MINUS) ); - mKeyMap.insert( KeyMap::value_type(SDLK_EQUALS, OIS::KC_EQUALS) ); - mKeyMap.insert( KeyMap::value_type(SDLK_BACKSPACE, OIS::KC_BACK) ); - mKeyMap.insert( KeyMap::value_type(SDLK_TAB, OIS::KC_TAB) ); - mKeyMap.insert( KeyMap::value_type(SDLK_q, OIS::KC_Q) ); - mKeyMap.insert( KeyMap::value_type(SDLK_w, OIS::KC_W) ); - mKeyMap.insert( KeyMap::value_type(SDLK_e, OIS::KC_E) ); - mKeyMap.insert( KeyMap::value_type(SDLK_r, OIS::KC_R) ); - mKeyMap.insert( KeyMap::value_type(SDLK_t, OIS::KC_T) ); - mKeyMap.insert( KeyMap::value_type(SDLK_y, OIS::KC_Y) ); - mKeyMap.insert( KeyMap::value_type(SDLK_u, OIS::KC_U) ); - mKeyMap.insert( KeyMap::value_type(SDLK_i, OIS::KC_I) ); - mKeyMap.insert( KeyMap::value_type(SDLK_o, OIS::KC_O) ); - mKeyMap.insert( KeyMap::value_type(SDLK_p, OIS::KC_P) ); - mKeyMap.insert( KeyMap::value_type(SDLK_RETURN, OIS::KC_RETURN) ); - mKeyMap.insert( KeyMap::value_type(SDLK_a, OIS::KC_A) ); - mKeyMap.insert( KeyMap::value_type(SDLK_s, OIS::KC_S) ); - mKeyMap.insert( KeyMap::value_type(SDLK_d, OIS::KC_D) ); - mKeyMap.insert( KeyMap::value_type(SDLK_f, OIS::KC_F) ); - mKeyMap.insert( KeyMap::value_type(SDLK_g, OIS::KC_G) ); - mKeyMap.insert( KeyMap::value_type(SDLK_h, OIS::KC_H) ); - mKeyMap.insert( KeyMap::value_type(SDLK_j, OIS::KC_J) ); - mKeyMap.insert( KeyMap::value_type(SDLK_k, OIS::KC_K) ); - mKeyMap.insert( KeyMap::value_type(SDLK_l, OIS::KC_L) ); - mKeyMap.insert( KeyMap::value_type(SDLK_SEMICOLON, OIS::KC_SEMICOLON) ); - mKeyMap.insert( KeyMap::value_type(SDLK_COLON, OIS::KC_COLON) ); - mKeyMap.insert( KeyMap::value_type(SDLK_QUOTE, OIS::KC_APOSTROPHE) ); - mKeyMap.insert( KeyMap::value_type(SDLK_BACKQUOTE, OIS::KC_GRAVE) ); - mKeyMap.insert( KeyMap::value_type(SDLK_LSHIFT, OIS::KC_LSHIFT) ); - mKeyMap.insert( KeyMap::value_type(SDLK_BACKSLASH, OIS::KC_BACKSLASH) ); - mKeyMap.insert( KeyMap::value_type(SDLK_SLASH, OIS::KC_SLASH) ); - mKeyMap.insert( KeyMap::value_type(SDLK_z, OIS::KC_Z) ); - mKeyMap.insert( KeyMap::value_type(SDLK_x, OIS::KC_X) ); - mKeyMap.insert( KeyMap::value_type(SDLK_c, OIS::KC_C) ); - mKeyMap.insert( KeyMap::value_type(SDLK_v, OIS::KC_V) ); - mKeyMap.insert( KeyMap::value_type(SDLK_b, OIS::KC_B) ); - mKeyMap.insert( KeyMap::value_type(SDLK_n, OIS::KC_N) ); - mKeyMap.insert( KeyMap::value_type(SDLK_m, OIS::KC_M) ); - mKeyMap.insert( KeyMap::value_type(SDLK_COMMA, OIS::KC_COMMA) ); - mKeyMap.insert( KeyMap::value_type(SDLK_PERIOD, OIS::KC_PERIOD)); - mKeyMap.insert( KeyMap::value_type(SDLK_RSHIFT, OIS::KC_RSHIFT)); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_MULTIPLY, OIS::KC_MULTIPLY) ); - mKeyMap.insert( KeyMap::value_type(SDLK_LALT, OIS::KC_LMENU) ); - mKeyMap.insert( KeyMap::value_type(SDLK_SPACE, OIS::KC_SPACE)); - mKeyMap.insert( KeyMap::value_type(SDLK_CAPSLOCK, OIS::KC_CAPITAL) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F1, OIS::KC_F1) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F2, OIS::KC_F2) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F3, OIS::KC_F3) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F4, OIS::KC_F4) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F5, OIS::KC_F5) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F6, OIS::KC_F6) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F7, OIS::KC_F7) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F8, OIS::KC_F8) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F9, OIS::KC_F9) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F10, OIS::KC_F10) ); - mKeyMap.insert( KeyMap::value_type(SDLK_NUMLOCKCLEAR, OIS::KC_NUMLOCK) ); - mKeyMap.insert( KeyMap::value_type(SDLK_SCROLLLOCK, OIS::KC_SCROLL)); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_7, OIS::KC_NUMPAD7) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_8, OIS::KC_NUMPAD8) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_9, OIS::KC_NUMPAD9) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_MINUS, OIS::KC_SUBTRACT) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_4, OIS::KC_NUMPAD4) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_5, OIS::KC_NUMPAD5) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_6, OIS::KC_NUMPAD6) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_PLUS, OIS::KC_ADD) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_1, OIS::KC_NUMPAD1) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_2, OIS::KC_NUMPAD2) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_3, OIS::KC_NUMPAD3) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_0, OIS::KC_NUMPAD0) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_PERIOD, OIS::KC_DECIMAL) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F11, OIS::KC_F11) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F12, OIS::KC_F12) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F13, OIS::KC_F13) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F14, OIS::KC_F14) ); - mKeyMap.insert( KeyMap::value_type(SDLK_F15, OIS::KC_F15) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_EQUALS, OIS::KC_NUMPADEQUALS) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_DIVIDE, OIS::KC_DIVIDE) ); - mKeyMap.insert( KeyMap::value_type(SDLK_SYSREQ, OIS::KC_SYSRQ) ); - mKeyMap.insert( KeyMap::value_type(SDLK_RALT, OIS::KC_RMENU) ); - mKeyMap.insert( KeyMap::value_type(SDLK_HOME, OIS::KC_HOME) ); - mKeyMap.insert( KeyMap::value_type(SDLK_UP, OIS::KC_UP) ); - mKeyMap.insert( KeyMap::value_type(SDLK_PAGEUP, OIS::KC_PGUP) ); - mKeyMap.insert( KeyMap::value_type(SDLK_LEFT, OIS::KC_LEFT) ); - mKeyMap.insert( KeyMap::value_type(SDLK_RIGHT, OIS::KC_RIGHT) ); - mKeyMap.insert( KeyMap::value_type(SDLK_END, OIS::KC_END) ); - mKeyMap.insert( KeyMap::value_type(SDLK_DOWN, OIS::KC_DOWN) ); - mKeyMap.insert( KeyMap::value_type(SDLK_PAGEDOWN, OIS::KC_PGDOWN) ); - mKeyMap.insert( KeyMap::value_type(SDLK_INSERT, OIS::KC_INSERT) ); - mKeyMap.insert( KeyMap::value_type(SDLK_DELETE, OIS::KC_DELETE) ); - mKeyMap.insert( KeyMap::value_type(SDLK_KP_ENTER, OIS::KC_NUMPADENTER) ); - mKeyMap.insert( KeyMap::value_type(SDLK_APPLICATION, OIS::KC_APPS) ); - -//The function of the Ctrl and Meta keys are switched on macOS compared to other platforms. -//For instance, Cmd+C versus Ctrl+C to copy from the system clipboard -#if defined(__APPLE__) - mKeyMap.insert( KeyMap::value_type(SDLK_LGUI, OIS::KC_LCONTROL) ); - mKeyMap.insert( KeyMap::value_type(SDLK_RGUI, OIS::KC_RCONTROL) ); - mKeyMap.insert( KeyMap::value_type(SDLK_LCTRL, OIS::KC_LWIN)); - mKeyMap.insert( KeyMap::value_type(SDLK_RCTRL, OIS::KC_RWIN) ); -#else - mKeyMap.insert( KeyMap::value_type(SDLK_LGUI, OIS::KC_LWIN) ); - mKeyMap.insert( KeyMap::value_type(SDLK_RGUI, OIS::KC_RWIN) ); - mKeyMap.insert( KeyMap::value_type(SDLK_LCTRL, OIS::KC_LCONTROL)); - mKeyMap.insert( KeyMap::value_type(SDLK_RCTRL, OIS::KC_RCONTROL) ); -#endif - } } diff --git a/components/sdlutil/sdlinputwrapper.hpp b/components/sdlutil/sdlinputwrapper.hpp index fde37f35f..39b6530fe 100644 --- a/components/sdlutil/sdlinputwrapper.hpp +++ b/components/sdlutil/sdlinputwrapper.hpp @@ -8,7 +8,6 @@ #include #include -#include "OISCompat.hpp" #include "events.hpp" namespace osgViewer @@ -40,8 +39,6 @@ namespace SDLUtil bool getMouseRelative() { return mMouseRelative; } void setGrabPointer(bool grab); - OIS::KeyCode sdl2OISKeyCode(SDL_Keycode code); - void warpMouse(int x, int y); void updateMouseSettings(); @@ -53,8 +50,6 @@ namespace SDLUtil void _wrapMousePointer(const SDL_MouseMotionEvent &evt); MouseMotionEvent _packageMouseMotion(const SDL_Event& evt); - void _setupOISKeys(); - SDL_Window* mSDLWindow; osg::ref_ptr mViewer; @@ -64,9 +59,6 @@ namespace SDLUtil WindowListener* mWindowListener; ControllerListener* mConListener; - typedef std::map KeyMap; - KeyMap mKeyMap; - Uint16 mWarpX; Uint16 mWarpY; bool mWarpCompensate; From 3328775eff45c69a841a2c7adf5e518aded2e8c6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 17 Apr 2020 15:41:52 +0400 Subject: [PATCH 059/227] Unify cursor enabling --- apps/openmw/mwinput/inputmanagerimp.cpp | 11 +++++------ apps/openmw/mwinput/inputmanagerimp.hpp | 2 -- apps/openmw/mwinput/sensormanager.cpp | 5 +++-- apps/openmw/mwinput/sensormanager.hpp | 6 +++++- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 0a43a62fc..be58ec265 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -48,7 +48,6 @@ namespace MWInput const std::string& userFile, bool userFileExists, const std::string& userControllerBindingsFile, const std::string& controllerBindingsFile, bool grab) : mGrabCursor(Settings::Manager::getBool("grab cursor", "Input")) - , mGuiCursorEnabled(true) { mInputWrapper = new SDLUtil::InputWrapper(window, viewer, grab); mInputWrapper->setWindowEventCallback(MWBase::Environment::get().getWindowManager()); @@ -144,7 +143,7 @@ namespace MWInput bool controllerMove = mControllerManager->update(dt, disableControls); mMouseManager->update(dt, disableControls); - mSensorManager->update(dt, mGuiCursorEnabled); + mSensorManager->update(dt); mActionManager->update(dt, controllerMove); } @@ -160,10 +159,10 @@ namespace MWInput void InputManager::changeInputMode(bool guiMode) { - mGuiCursorEnabled = guiMode; - mControllerManager->setGuiCursorEnabled(mGuiCursorEnabled); - mMouseManager->setGuiCursorEnabled(mGuiCursorEnabled); - mMouseManager->setMouseLookEnabled(!mGuiCursorEnabled); + mControllerManager->setGuiCursorEnabled(guiMode); + mMouseManager->setGuiCursorEnabled(guiMode); + mSensorManager->setGuiCursorEnabled(guiMode); + mMouseManager->setMouseLookEnabled(!guiMode); if (guiMode) MWBase::Environment::get().getWindowManager()->showCrosshair(false); MWBase::Environment::get().getWindowManager()->setCursorVisible(guiMode && (!mControllerManager->joystickLastUsed() || mControllerManager->gamepadGuiCursorEnabled())); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 8e8096d92..f7e732d99 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -111,8 +111,6 @@ namespace MWInput bool mGrabCursor; - bool mGuiCursorEnabled; - ControlSwitch* mControlSwitch; ActionManager* mActionManager; diff --git a/apps/openmw/mwinput/sensormanager.cpp b/apps/openmw/mwinput/sensormanager.cpp index f78607fa2..02669798c 100644 --- a/apps/openmw/mwinput/sensormanager.cpp +++ b/apps/openmw/mwinput/sensormanager.cpp @@ -22,6 +22,7 @@ namespace MWInput , mGyroVAxis(GyroscopeAxis::Y) , mGyroInputThreshold(Settings::Manager::getFloat("gyro input threshold", "Input")) , mGyroscope(nullptr) + , mGuiCursorEnabled(true) { init(); } @@ -235,7 +236,7 @@ namespace MWInput } } - void SensorManager::update(float dt, bool isCursorEnabled) + void SensorManager::update(float dt) { if (mGyroXSpeed == 0.f && mGyroYSpeed == 0.f) return; @@ -252,7 +253,7 @@ namespace MWInput mGyroUpdateTimer += dt; - if (!isCursorEnabled) + if (!mGuiCursorEnabled) { float rot[3]; rot[0] = mGyroYSpeed * dt * mGyroVSensitivity * 4 * (mInvertY ? -1 : 1); diff --git a/apps/openmw/mwinput/sensormanager.hpp b/apps/openmw/mwinput/sensormanager.hpp index 283a53c1f..9b24328f7 100644 --- a/apps/openmw/mwinput/sensormanager.hpp +++ b/apps/openmw/mwinput/sensormanager.hpp @@ -29,12 +29,14 @@ namespace MWInput void clear(); - void update(float dt, bool isCursorEnabled); + void update(float dt); virtual void sensorUpdated(const SDL_SensorEvent &arg); virtual void displayOrientationChanged(); void processChangedSettings(const Settings::CategorySettingVector& changed); + void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; } + private: enum GyroscopeAxis { @@ -66,6 +68,8 @@ namespace MWInput float mGyroInputThreshold; SDL_Sensor* mGyroscope; + + bool mGuiCursorEnabled; }; } #endif From 85f91a7de8e088c26744c3d2d81522f15362c264 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 17 Apr 2020 15:59:37 +0400 Subject: [PATCH 060/227] Remove some redundant code --- apps/openmw/mwinput/inputmanagerimp.cpp | 19 +------------------ apps/openmw/mwinput/inputmanagerimp.hpp | 12 ++---------- apps/openmw/mwinput/keyboardmanager.cpp | 6 +----- apps/openmw/mwinput/keyboardmanager.hpp | 10 +--------- 4 files changed, 5 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index be58ec265..fb49ff464 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -2,33 +2,16 @@ #include -#include -#include -#include -#include - -#include - -#include #include #include #include #include -#include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/statemanager.hpp" #include "../mwbase/environment.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/player.hpp" -#include "../mwworld/class.hpp" -#include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwmechanics/npcstats.hpp" -#include "../mwmechanics/actorutil.hpp" - #include "actionmanager.hpp" #include "bindingsmanager.hpp" #include "controllermanager.hpp" @@ -58,7 +41,7 @@ namespace MWInput mActionManager = new ActionManager(mBindingsManager, screenCaptureOperation, viewer, screenCaptureHandler); - mKeyboardManager = new KeyboardManager(mBindingsManager, mActionManager); + mKeyboardManager = new KeyboardManager(mBindingsManager); mInputWrapper->setKeyboardEventCallback(mKeyboardManager); mMouseManager = new MouseManager(mBindingsManager, mInputWrapper, window); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index f7e732d99..211a09583 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -1,19 +1,16 @@ #ifndef MWINPUT_MWINPUTMANAGERIMP_H #define MWINPUT_MWINPUTMANAGERIMP_H -#include "../mwgui/mode.hpp" - -#include - #include #include #include -#include #include #include "../mwbase/inputmanager.hpp" +#include "../mwgui/mode.hpp" + #include "actions.hpp" namespace MWInput @@ -37,11 +34,6 @@ namespace MWBase class WindowManager; } -namespace Files -{ - struct ConfigurationManager; -} - namespace SDLUtil { class InputWrapper; diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index efe911593..159ea388f 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -4,22 +4,18 @@ #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" -#include "../mwbase/statemanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/world.hpp" #include "../mwworld/player.hpp" -#include "actionmanager.hpp" #include "actions.hpp" #include "bindingsmanager.hpp" #include "sdlmappings.hpp" namespace MWInput { - KeyboardManager::KeyboardManager(BindingsManager* bindingsManager, ActionManager* actionManager) + KeyboardManager::KeyboardManager(BindingsManager* bindingsManager) : mBindingsManager(bindingsManager) - , mActionManager(actionManager) , mControlsDisabled(false) { } diff --git a/apps/openmw/mwinput/keyboardmanager.hpp b/apps/openmw/mwinput/keyboardmanager.hpp index bba2f5dc4..ae473be51 100644 --- a/apps/openmw/mwinput/keyboardmanager.hpp +++ b/apps/openmw/mwinput/keyboardmanager.hpp @@ -4,20 +4,14 @@ #include #include -namespace SDLUtil -{ - class InputWrapper; -} - namespace MWInput { - class ActionManager; class BindingsManager; class KeyboardManager : public SDLUtil::KeyListener { public: - KeyboardManager(BindingsManager* bindingsManager, ActionManager* actionManager); + KeyboardManager(BindingsManager* bindingsManager); virtual ~KeyboardManager() = default; @@ -30,8 +24,6 @@ namespace MWInput private: BindingsManager* mBindingsManager; - ActionManager* mActionManager; - bool mControlsDisabled; }; } From b575712cb1ce9ddd8d5e15ed2b0601499b97eb22 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 17 Apr 2020 16:51:47 +0400 Subject: [PATCH 061/227] Formatting changes --- apps/openmw/mwinput/actionmanager.cpp | 30 ++++----- apps/openmw/mwinput/actionmanager.hpp | 2 - apps/openmw/mwinput/bindingsmanager.cpp | 80 +++++++++++------------ apps/openmw/mwinput/controllermanager.cpp | 43 ++++++------ apps/openmw/mwinput/controllermanager.hpp | 2 - apps/openmw/mwinput/inputmanagerimp.cpp | 19 +++--- apps/openmw/mwinput/inputmanagerimp.hpp | 48 +++++++------- apps/openmw/mwinput/keyboardmanager.cpp | 2 + apps/openmw/mwinput/mousemanager.cpp | 13 ++-- apps/openmw/mwinput/mousemanager.hpp | 2 - apps/openmw/mwinput/sdlmappings.cpp | 5 +- apps/openmw/mwinput/sensormanager.cpp | 10 +-- apps/openmw/mwinput/sensormanager.hpp | 2 - 13 files changed, 114 insertions(+), 144 deletions(-) diff --git a/apps/openmw/mwinput/actionmanager.cpp b/apps/openmw/mwinput/actionmanager.cpp index e90a4ddaf..f2af3089d 100644 --- a/apps/openmw/mwinput/actionmanager.cpp +++ b/apps/openmw/mwinput/actionmanager.cpp @@ -218,22 +218,22 @@ namespace MWInput handleGuiArrowKey(action); break; case A_Journal: - toggleJournal (); + toggleJournal(); break; case A_AutoMove: - toggleAutoMove (); + toggleAutoMove(); break; case A_AlwaysRun: - toggleWalking (); + toggleWalking(); break; case A_ToggleWeapon: - toggleWeapon (); + toggleWeapon(); break; case A_Rest: rest(); break; case A_ToggleSpell: - toggleSpell (); + toggleSpell(); break; case A_QuickKey1: quickKey(1); @@ -344,9 +344,9 @@ namespace MWInput { osg::ref_ptr screenshot (new osg::Image); - if (MWBase::Environment::get().getWorld()->screenshot360(screenshot.get(),settingStr)) + if (MWBase::Environment::get().getWorld()->screenshot360(screenshot.get(), settingStr)) { - (*mScreenCaptureOperation) (*(screenshot.get()),0); + (*mScreenCaptureOperation) (*(screenshot.get()), 0); // FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason } } @@ -444,10 +444,10 @@ namespace MWInput if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) return; - if (!MWBase::Environment::get().getWindowManager()->getRestEnabled () || MWBase::Environment::get().getWindowManager()->isGuiMode ()) + if (!MWBase::Environment::get().getWindowManager()->getRestEnabled() || MWBase::Environment::get().getWindowManager()->isGuiMode()) return; - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_Rest); //Open rest GUI + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Rest); //Open rest GUI } void ActionManager::toggleInventory() @@ -476,7 +476,7 @@ namespace MWInput void ActionManager::toggleConsole() { - if (MyGUI::InputManager::getInstance ().isModalAny()) + if (MyGUI::InputManager::getInstance().isModalAny()) return; MWBase::Environment::get().getWindowManager()->toggleConsole(); @@ -489,14 +489,14 @@ namespace MWInput if (MyGUI::InputManager::getInstance ().isModalAny()) return; - if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal + if (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) { MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); } - else if(MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Journal)) + else if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Journal)) { MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Journal); } @@ -528,7 +528,7 @@ namespace MWInput } else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) { - while(MyGUI::InputManager::getInstance().isModalAny()) + while (MyGUI::InputManager::getInstance().isModalAny()) { //Handle any open Modal windows MWBase::Environment::get().getWindowManager()->exitCurrentModal(); } @@ -579,10 +579,6 @@ namespace MWInput player.setSneak(mSneaking); } - void ActionManager::clear() - { - } - void ActionManager::handleGuiArrowKey(int action) { bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed(); diff --git a/apps/openmw/mwinput/actionmanager.hpp b/apps/openmw/mwinput/actionmanager.hpp index 31fd993c9..7aa73f520 100644 --- a/apps/openmw/mwinput/actionmanager.hpp +++ b/apps/openmw/mwinput/actionmanager.hpp @@ -23,8 +23,6 @@ namespace MWInput osg::ref_ptr viewer, osg::ref_ptr screenCaptureHandler); - void clear(); - void update(float dt, bool triedToMove); void executeAction(int action); diff --git a/apps/openmw/mwinput/bindingsmanager.cpp b/apps/openmw/mwinput/bindingsmanager.cpp index f871f0c51..f21e184b7 100644 --- a/apps/openmw/mwinput/bindingsmanager.cpp +++ b/apps/openmw/mwinput/bindingsmanager.cpp @@ -72,7 +72,7 @@ namespace MWInput , SDL_Scancode key, ICS::Control::ControlChangingDirection direction) { //Disallow binding escape key - if(key==SDL_SCANCODE_ESCAPE) + if (key==SDL_SCANCODE_ESCAPE) { //Stop binding if esc pressed mInputBinder->cancelDetectingBindingState(); @@ -90,7 +90,7 @@ namespace MWInput return; #endif - if(!mDetectingKeyboard) + if (!mDetectingKeyboard) return; clearAllKeyBindings(mInputBinder, control); @@ -109,7 +109,7 @@ namespace MWInput virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control , unsigned int button, ICS::Control::ControlChangingDirection direction) { - if(!mDetectingKeyboard) + if (!mDetectingKeyboard) return; clearAllKeyBindings(mInputBinder, control); control->setInitialValue(0.0f); @@ -120,7 +120,7 @@ namespace MWInput virtual void mouseWheelBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control , ICS::InputControlSystem::MouseWheelClick click, ICS::Control::ControlChangingDirection direction) { - if(!mDetectingKeyboard) + if (!mDetectingKeyboard) return; clearAllKeyBindings(mInputBinder, control); control->setInitialValue(0.0f); @@ -132,9 +132,9 @@ namespace MWInput , int axis, ICS::Control::ControlChangingDirection direction) { //only allow binding to the trigers - if(axis != SDL_CONTROLLER_AXIS_TRIGGERLEFT && axis != SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + if (axis != SDL_CONTROLLER_AXIS_TRIGGERLEFT && axis != SDL_CONTROLLER_AXIS_TRIGGERRIGHT) return; - if(mDetectingKeyboard) + if (mDetectingKeyboard) return; clearAllControllerBindings(mInputBinder, control); @@ -147,7 +147,7 @@ namespace MWInput virtual void joystickButtonBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control , unsigned int button, ICS::Control::ControlChangingDirection direction) { - if(mDetectingKeyboard) + if (mDetectingKeyboard) return; clearAllControllerBindings(mInputBinder,control); control->setInitialValue(0.0f); @@ -220,8 +220,8 @@ namespace MWInput A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10, A_Use, A_Journal}; - for(size_t i = 0; i < sizeof(playerChannels)/sizeof(playerChannels[0]); i++) { - int pc = playerChannels[i]; + for(int pc : playerChannels) + { mInputBinder->getChannel(pc)->setEnabled(enabled); } } @@ -293,7 +293,7 @@ namespace MWInput for (int i = 0; i < A_Last; ++i) { ICS::Control* control; - bool controlExists = mInputBinder->getChannel(i)->getControlsCount () != 0; + bool controlExists = mInputBinder->getChannel(i)->getControlsCount() != 0; if (!controlExists) { control = new ICS::Control(std::to_string(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); @@ -302,14 +302,13 @@ namespace MWInput } else { - control = mInputBinder->getChannel(i)->getAttachedControls ().front().control; + control = mInputBinder->getChannel(i)->getAttachedControls().front().control; } if (!controlExists || force || - ( mInputBinder->getKeyBinding (control, ICS::Control::INCREASE) == SDL_SCANCODE_UNKNOWN - && mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS - && mInputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE) == ICS::InputControlSystem::MouseWheelClick::UNASSIGNED - )) + (mInputBinder->getKeyBinding(control, ICS::Control::INCREASE) == SDL_SCANCODE_UNKNOWN + && mInputBinder->getMouseButtonBinding(control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS + && mInputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE) == ICS::InputControlSystem::MouseWheelClick::UNASSIGNED)) { clearAllKeyBindings(mInputBinder, control); @@ -323,7 +322,7 @@ namespace MWInput && (force || !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i]))) { control->setInitialValue(0.0f); - mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); + mInputBinder->addMouseButtonBinding(control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); } else if (defaultMouseWheelBindings.find(i) != defaultMouseWheelBindings.end() && (force || !mInputBinder->isMouseWheelBound(defaultMouseWheelBindings[i]))) @@ -379,7 +378,7 @@ namespace MWInput for (int i = 0; i < A_Last; i++) { ICS::Control* control; - bool controlExists = mInputBinder->getChannel(i)->getControlsCount () != 0; + bool controlExists = mInputBinder->getChannel(i)->getControlsCount() != 0; if (!controlExists) { float initial; @@ -392,10 +391,11 @@ namespace MWInput } else { - control = mInputBinder->getChannel(i)->getAttachedControls ().front().control; + control = mInputBinder->getChannel(i)->getAttachedControls().front().control; } - if (!controlExists || force || ( mInputBinder->getJoystickAxisBinding (control, sFakeDeviceId, ICS::Control::INCREASE) == ICS::InputControlSystem::UNASSIGNED && mInputBinder->getJoystickButtonBinding (control, sFakeDeviceId, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS )) + if (!controlExists || force || (mInputBinder->getJoystickAxisBinding(control, sFakeDeviceId, ICS::Control::INCREASE) == ICS::InputControlSystem::UNASSIGNED && + mInputBinder->getJoystickButtonBinding(control, sFakeDeviceId, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS)) { clearAllControllerBindings(mInputBinder, control); @@ -415,7 +415,7 @@ namespace MWInput } } - std::string BindingsManager::getActionDescription (int action) + std::string BindingsManager::getActionDescription(int action) { std::map descriptions; @@ -464,24 +464,24 @@ namespace MWInput descriptions[A_QuickSave] = "sQuickSaveCmd"; descriptions[A_QuickLoad] = "sQuickLoadCmd"; - if (descriptions[action] == "") - return ""; // not configurable + if (descriptions[action].empty()) + return std::string(); // not configurable return "#{" + descriptions[action] + "}"; } - std::string BindingsManager::getActionKeyBindingName (int action) + std::string BindingsManager::getActionKeyBindingName(int action) { - if (mInputBinder->getChannel (action)->getControlsCount () == 0) + if (mInputBinder->getChannel(action)->getControlsCount() == 0) return "#{sNone}"; - ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; + ICS::Control* c = mInputBinder->getChannel(action)->getAttachedControls().front().control; - SDL_Scancode key = mInputBinder->getKeyBinding (c, ICS::Control::INCREASE); - unsigned int mouse = mInputBinder->getMouseButtonBinding (c, ICS::Control::INCREASE); + SDL_Scancode key = mInputBinder->getKeyBinding(c, ICS::Control::INCREASE); + unsigned int mouse = mInputBinder->getMouseButtonBinding(c, ICS::Control::INCREASE); ICS::InputControlSystem::MouseWheelClick wheel = mInputBinder->getMouseWheelBinding(c, ICS::Control::INCREASE); if (key != SDL_SCANCODE_UNKNOWN) - return MyGUI::TextIterator::toTagsString(mInputBinder->scancodeToString (key)); + return MyGUI::TextIterator::toTagsString(mInputBinder->scancodeToString(key)); else if (mouse != ICS_MAX_DEVICE_BUTTONS) return "#{sMouse} " + std::to_string(mouse); else if (wheel != ICS::InputControlSystem::MouseWheelClick::UNASSIGNED) @@ -502,17 +502,17 @@ namespace MWInput return "#{sNone}"; } - std::string BindingsManager::getActionControllerBindingName (int action) + std::string BindingsManager::getActionControllerBindingName(int action) { - if (mInputBinder->getChannel (action)->getControlsCount () == 0) + if (mInputBinder->getChannel(action)->getControlsCount() == 0) return "#{sNone}"; - ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; + ICS::Control* c = mInputBinder->getChannel(action)->getAttachedControls().front().control; - if (mInputBinder->getJoystickAxisBinding (c, sFakeDeviceId, ICS::Control::INCREASE) != ICS::InputControlSystem::UNASSIGNED) - return sdlControllerAxisToString(mInputBinder->getJoystickAxisBinding (c, sFakeDeviceId, ICS::Control::INCREASE)); - else if (mInputBinder->getJoystickButtonBinding (c, sFakeDeviceId, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS ) - return sdlControllerButtonToString(mInputBinder->getJoystickButtonBinding (c, sFakeDeviceId, ICS::Control::INCREASE)); + if (mInputBinder->getJoystickAxisBinding(c, sFakeDeviceId, ICS::Control::INCREASE) != ICS::InputControlSystem::UNASSIGNED) + return sdlControllerAxisToString(mInputBinder->getJoystickAxisBinding(c, sFakeDeviceId, ICS::Control::INCREASE)); + else if (mInputBinder->getJoystickButtonBinding(c, sFakeDeviceId, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + return sdlControllerButtonToString(mInputBinder->getJoystickButtonBinding(c, sFakeDeviceId, ICS::Control::INCREASE)); else return "#{sNone}"; } @@ -680,17 +680,17 @@ namespace MWInput if (mDragDrop && action != A_GameMenu && action != A_Inventory) return; - if((previousValue == 1 || previousValue == 0) && (currentValue==1 || currentValue==0)) + if ((previousValue == 1 || previousValue == 0) && (currentValue==1 || currentValue==0)) { //Is a normal button press, so don't change it at all } //Otherwise only trigger button presses as they go through specific points - else if(previousValue >= .8 && currentValue < .8) + else if (previousValue >= 0.8 && currentValue < 0.8) { currentValue = 0.0; previousValue = 1.0; } - else if(previousValue <= .6 && currentValue > .6) + else if (previousValue <= 0.6 && currentValue > 0.6) { currentValue = 1.0; previousValue = 0.0; @@ -706,7 +706,7 @@ namespace MWInput bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed(); if (action == A_Use) { - if(joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) + if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) action = A_CycleWeaponRight; else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell)) @@ -721,7 +721,7 @@ namespace MWInput } else if (action == A_Jump) { - if(joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) + if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon)) action = A_CycleWeaponLeft; else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell)) diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index b8fc0ca74..5c5246caf 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -41,20 +41,21 @@ namespace MWInput , mSneakGamepadShortcut(false) , mGamepadPreviewMode(false) { - if(!controllerBindingsFile.empty()) + if (!controllerBindingsFile.empty()) { SDL_GameControllerAddMappingsFromFile(controllerBindingsFile.c_str()); } - if(!userControllerBindingsFile.empty()) + + if (!userControllerBindingsFile.empty()) { SDL_GameControllerAddMappingsFromFile(userControllerBindingsFile.c_str()); } // Open all presently connected sticks int numSticks = SDL_NumJoysticks(); - for(int i = 0; i < numSticks; i++) + for (int i = 0; i < numSticks; i++) { - if(SDL_IsGameController(i)) + if (SDL_IsGameController(i)) { SDL_ControllerDeviceEvent evt; evt.which = i; @@ -73,10 +74,6 @@ namespace MWInput mInvUiScalingFactor = 1.f / uiScale; } - void ControllerManager::clear() - { - } - void ControllerManager::processChangedSettings(const Settings::CategorySettingVector& changed) { for (const auto& setting : changed) @@ -131,13 +128,13 @@ namespace MWInput { float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight); float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward); - if (xAxis != .5) + if (xAxis != 0.5) { triedToMove = true; player.setLeftRight((xAxis - 0.5f) * 2); } - if (yAxis != .5) + if (yAxis != 0.5) { triedToMove = true; player.setAutoMove (false); @@ -153,13 +150,13 @@ namespace MWInput static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input"); if (!isToggleSneak) { - if(mJoystickLastUsed) + if (mJoystickLastUsed) { - if(mBindingsManager->actionIsActive(A_Sneak)) + if (mBindingsManager->actionIsActive(A_Sneak)) { - if(mSneakToggleShortcutTimer) // New Sneak Button Press + if (mSneakToggleShortcutTimer) // New Sneak Button Press { - if(mSneakToggleShortcutTimer <= 0.3f) + if (mSneakToggleShortcutTimer <= 0.3f) { mSneakGamepadShortcut = true; mActionManager->toggleSneaking(); @@ -168,15 +165,15 @@ namespace MWInput mSneakGamepadShortcut = false; } - if(!mActionManager->isSneaking()) + if (!mActionManager->isSneaking()) mActionManager->toggleSneaking(); mSneakToggleShortcutTimer = 0.f; } else { - if(!mSneakGamepadShortcut && mActionManager->isSneaking()) + if (!mSneakGamepadShortcut && mActionManager->isSneaking()) mActionManager->toggleSneaking(); - if(mSneakToggleShortcutTimer <= 0.3f) + if (mSneakToggleShortcutTimer <= 0.3f) mSneakToggleShortcutTimer += dt; } } @@ -190,7 +187,7 @@ namespace MWInput if (!mBindingsManager->actionIsActive(A_TogglePOV)) mGamepadZoom = 0; - if(mGamepadZoom) + if (mGamepadZoom) { MWBase::Environment::get().getWorld()->changeVanityModeScale(mGamepadZoom); MWBase::Environment::get().getWorld()->setCameraDistance(mGamepadZoom, true, true); @@ -210,6 +207,7 @@ namespace MWInput { if (gamepadToGuiControl(arg)) return; + if (mGamepadGuiCursorEnabled) { // Temporary mouse binding until keyboard controls are available: @@ -245,6 +243,7 @@ namespace MWInput mBindingsManager->controllerButtonReleased(deviceID, arg); return; } + if (!mJoystickEnabled || mControlsDisabled) return; @@ -276,7 +275,7 @@ namespace MWInput void ControllerManager::axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg) { - if(!mJoystickEnabled || mControlsDisabled) + if (!mJoystickEnabled || mControlsDisabled) return; mJoystickLastUsed = true; @@ -286,14 +285,14 @@ namespace MWInput } else { - if(mGamepadPreviewMode && arg.value) // Preview Mode Gamepad Zooming + if (mGamepadPreviewMode && arg.value) // Preview Mode Gamepad Zooming { - if(arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) { mGamepadZoom = arg.value * 0.85f / 1000.f; return; // Do not propagate event. } - else if(arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT) + else if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT) { mGamepadZoom = -arg.value * 0.85f / 1000.f; return; // Do not propagate event. diff --git a/apps/openmw/mwinput/controllermanager.hpp b/apps/openmw/mwinput/controllermanager.hpp index f1be50ee6..6b9546b0c 100644 --- a/apps/openmw/mwinput/controllermanager.hpp +++ b/apps/openmw/mwinput/controllermanager.hpp @@ -23,8 +23,6 @@ namespace MWInput virtual ~ControllerManager() = default; - void clear(); - bool update(float dt, bool disableControls); virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index fb49ff464..4f1ee7855 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -58,11 +58,6 @@ namespace MWInput { // Enable all controls mControlSwitch->clear(); - - mActionManager->clear(); - mControllerManager->clear(); - mSensorManager->clear(); - mMouseManager->clear(); } InputManager::~InputManager() @@ -90,19 +85,19 @@ namespace MWInput bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && !MWBase::Environment::get().getWindowManager()->isConsoleMode(); - bool was_relative = mInputWrapper->getMouseRelative(); - bool is_relative = !MWBase::Environment::get().getWindowManager()->isGuiMode(); + bool wasRelative = mInputWrapper->getMouseRelative(); + bool isRelative = !MWBase::Environment::get().getWindowManager()->isGuiMode(); // don't keep the pointer away from the window edge in gui mode // stop using raw mouse motions and switch to system cursor movements - mInputWrapper->setMouseRelative(is_relative); + mInputWrapper->setMouseRelative(isRelative); //we let the mouse escape in the main menu - mInputWrapper->setGrabPointer(grab && (mGrabCursor || is_relative)); + mInputWrapper->setGrabPointer(grab && (mGrabCursor || isRelative)); //we switched to non-relative mode, move our cursor to where the in-game //cursor is - if(!is_relative && was_relative != is_relative) + if (!isRelative && wasRelative != isRelative) { mMouseManager->warpMouse(); } @@ -148,7 +143,9 @@ namespace MWInput mMouseManager->setMouseLookEnabled(!guiMode); if (guiMode) MWBase::Environment::get().getWindowManager()->showCrosshair(false); - MWBase::Environment::get().getWindowManager()->setCursorVisible(guiMode && (!mControllerManager->joystickLastUsed() || mControllerManager->gamepadGuiCursorEnabled())); + + bool isCursorVisible = guiMode && (!mControllerManager->joystickLastUsed() || mControllerManager->gamepadGuiCursorEnabled()); + MWBase::Environment::get().getWindowManager()->setCursorVisible(isCursorVisible); // if not in gui mode, the camera decides whether to show crosshair or not. } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 211a09583..4ecb6a82d 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -13,17 +13,6 @@ #include "actions.hpp" -namespace MWInput -{ - class ControlSwitch; - class ActionManager; - class BindingsManager; - class ControllerManager; - class KeyboardManager; - class MouseManager; - class SensorManager; -} - namespace MWWorld { class Player; @@ -43,11 +32,18 @@ struct SDL_Window; namespace MWInput { + class ControlSwitch; + class ActionManager; + class BindingsManager; + class ControllerManager; + class KeyboardManager; + class MouseManager; + class SensorManager; + /** - * @brief Class that handles all input and key bindings for OpenMW. + * @brief Class that provides a high-level API for game input */ - class InputManager : - public MWBase::InputManager + class InputManager : public MWBase::InputManager { public: InputManager( @@ -99,6 +95,18 @@ namespace MWInput virtual void executeAction(int action); private: + void convertMousePosForMyGUI(int& x, int& y); + + void handleGuiArrowKey(int action); + + void updateCursorMode(); + + void quickKey(int index); + void showQuickKeysMenu(); + + void loadKeyDefaults(bool force = false); + void loadControllerDefaults(bool force = false); + SDLUtil::InputWrapper* mInputWrapper; bool mGrabCursor; @@ -111,18 +119,6 @@ namespace MWInput KeyboardManager* mKeyboardManager; MouseManager* mMouseManager; SensorManager* mSensorManager; - - void convertMousePosForMyGUI(int& x, int& y); - - void handleGuiArrowKey(int action); - - void updateCursorMode(); - - void quickKey(int index); - void showQuickKeysMenu(); - - void loadKeyDefaults(bool force = false); - void loadControllerDefaults(bool force = false); }; } #endif diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index 159ea388f..afeef632f 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -47,11 +47,13 @@ namespace MWInput consumed = true; mBindingsManager->setPlayerControlsEnabled(!consumed); } + if (arg.repeat) return; if (!mControlsDisabled && !consumed) mBindingsManager->keyPressed(arg); + MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); } diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index 30e32bef8..cc20cc1ef 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -47,10 +47,6 @@ namespace MWInput mGuiCursorY = mInvUiScalingFactor * h / 2.f; } - void MouseManager::clear() - { - } - void MouseManager::processChangedSettings(const Settings::CategorySettingVector& changed) { for (const auto& setting : changed) @@ -103,7 +99,7 @@ namespace MWInput rot[2] = -x; // Only actually turn player when we're not in vanity mode - if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && input->getControlSwitch("playerlooking")) + if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && input->getControlSwitch("playerlooking")) { MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); player.yaw(x); @@ -130,7 +126,8 @@ namespace MWInput bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); guiMode = MyGUI::InputManager::getInstance().injectMouseRelease(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode; - if(mBindingsManager->isDetectingBindingState()) return; // don't allow same mouseup to bind as initiated bind + if (mBindingsManager->isDetectingBindingState()) + return; // don't allow same mouseup to bind as initiated bind mBindingsManager->setPlayerControlsEnabled(!guiMode); mBindingsManager->mouseReleased(arg, id); @@ -154,9 +151,9 @@ namespace MWInput { guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); guiMode = MyGUI::InputManager::getInstance().injectMousePress(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode; - if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) + if (MyGUI::InputManager::getInstance().getMouseFocusWidget () != 0) { - MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType(false); + MyGUI::Button* b = MyGUI::InputManager::getInstance().getMouseFocusWidget()->castType(false); if (b && b->getEnabled() && id == SDL_BUTTON_LEFT) { MWBase::Environment::get().getWindowManager()->playSound("Menu Click"); diff --git a/apps/openmw/mwinput/mousemanager.hpp b/apps/openmw/mwinput/mousemanager.hpp index 2686c59b7..58d97f6e5 100644 --- a/apps/openmw/mwinput/mousemanager.hpp +++ b/apps/openmw/mwinput/mousemanager.hpp @@ -20,8 +20,6 @@ namespace MWInput virtual ~MouseManager() = default; - void clear(); - void update(float dt, bool disableControls); virtual void mouseMoved(const SDLUtil::MouseMotionEvent &arg); diff --git a/apps/openmw/mwinput/sdlmappings.cpp b/apps/openmw/mwinput/sdlmappings.cpp index 8a9ccf4e7..0c3f5c5d8 100644 --- a/apps/openmw/mwinput/sdlmappings.cpp +++ b/apps/openmw/mwinput/sdlmappings.cpp @@ -206,14 +206,11 @@ namespace MWInput { static std::map keyMap; if (keyMap.empty()) - { initKeyMap(keyMap); - } MyGUI::KeyCode kc = MyGUI::KeyCode::None; - auto foundKey = keyMap.find(code); - if(foundKey != keyMap.end()) + if (foundKey != keyMap.end()) kc = foundKey->second; return kc; diff --git a/apps/openmw/mwinput/sensormanager.cpp b/apps/openmw/mwinput/sensormanager.cpp index 02669798c..d70662f6a 100644 --- a/apps/openmw/mwinput/sensormanager.cpp +++ b/apps/openmw/mwinput/sensormanager.cpp @@ -33,13 +33,6 @@ namespace MWInput updateSensors(); } - void SensorManager::clear() - { - mGyroXSpeed = 0.f; - mGyroYSpeed = 0.f; - mGyroUpdateTimer = 0.f; - } - SensorManager::~SensorManager() { if (mGyroscope != nullptr) @@ -246,7 +239,8 @@ namespace MWInput // More than half of second passed since the last gyroscope update. // A device more likely was disconnected or switched to the sleep mode. // Reset current rotation speed and wait for update. - clear(); + mGyroXSpeed = 0.f; + mGyroYSpeed = 0.f; mGyroUpdateTimer = 0.f; return; } diff --git a/apps/openmw/mwinput/sensormanager.hpp b/apps/openmw/mwinput/sensormanager.hpp index 9b24328f7..8f333ad31 100644 --- a/apps/openmw/mwinput/sensormanager.hpp +++ b/apps/openmw/mwinput/sensormanager.hpp @@ -27,8 +27,6 @@ namespace MWInput void init(); - void clear(); - void update(float dt); virtual void sensorUpdated(const SDL_SensorEvent &arg); From 0455f48d0218c3abd3e534d57e0581f5e0a7d143 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 17 Apr 2020 17:06:19 +0400 Subject: [PATCH 062/227] More formatting changes --- apps/openmw/mwinput/controllermanager.cpp | 9 ++++----- apps/openmw/mwinput/mousemanager.cpp | 22 +++++++++++----------- apps/openmw/mwinput/sensormanager.cpp | 19 +++++++++---------- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 5c5246caf..a71c5b31a 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -90,9 +90,9 @@ namespace MWInput if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled)) { - float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight)*2.0f-1.0f; - float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward)*2.0f-1.0f; - float zAxis = mBindingsManager->getActionValue(A_LookUpDown)*2.0f-1.0f; + float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight) * 2.0f - 1.0f; + float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward) * 2.0f - 1.0f; + float zAxis = mBindingsManager->getActionValue(A_LookUpDown) * 2.0f - 1.0f; xAxis *= (1.5f - mBindingsManager->getActionValue(A_Use)); yAxis *= (1.5f - mBindingsManager->getActionValue(A_Use)); @@ -138,7 +138,7 @@ namespace MWInput { triedToMove = true; player.setAutoMove (false); - player.setForwardBackward((yAxis - 0.5f) * 2 * -1); + player.setForwardBackward((0.5f - yAxis) * 2); } if (triedToMove) @@ -316,7 +316,6 @@ namespace MWInput { // Presumption of GUI mode will be removed in the future. // MyGUI KeyCodes *may* change. - MyGUI::KeyCode key = MyGUI::KeyCode::None; switch (arg.button) { diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index cc20cc1ef..2cce1cd80 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -90,8 +90,8 @@ namespace MWInput if (mMouseLookEnabled && !mControlsDisabled) { - float x = arg.xrel * mCameraSensitivity * (1.0f/256.f) * (mInvertX ? -1 : 1); - float y = arg.yrel * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; + float x = arg.xrel * mCameraSensitivity * (mInvertX ? -1 : 1) / 256.f; + float y = arg.yrel * mCameraSensitivity * (mInvertY ? -1 : 1) * mCameraYMultiplier / 256.f; float rot[3]; rot[0] = -y; @@ -117,7 +117,7 @@ namespace MWInput { MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); - if(mBindingsManager->isDetectingBindingState()) + if (mBindingsManager->isDetectingBindingState()) { mBindingsManager->mouseReleased(arg, id); } @@ -176,18 +176,18 @@ namespace MWInput if (!mMouseLookEnabled) return; - float xAxis = mBindingsManager->getActionValue(A_LookLeftRight)*2.0f-1.0f; - float yAxis = mBindingsManager->getActionValue(A_LookUpDown)*2.0f-1.0f; + float xAxis = mBindingsManager->getActionValue(A_LookLeftRight) * 2.0f - 1.0f; + float yAxis = mBindingsManager->getActionValue(A_LookUpDown) * 2.0f - 1.0f; if (xAxis == 0 && yAxis == 0) return; float rot[3]; - rot[0] = yAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; + rot[0] = yAxis * dt * 1000.0f * mCameraSensitivity * (mInvertY ? -1 : 1) * mCameraYMultiplier / 256.f; rot[1] = 0.0f; - rot[2] = xAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertX ? -1 : 1); + rot[2] = xAxis * dt * 1000.0f * mCameraSensitivity * (mInvertX ? -1 : 1) / 256.f; // Only actually turn player when we're not in vanity mode - if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) + if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) { MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); player.yaw(rot[2]); @@ -214,14 +214,14 @@ namespace MWInput mMouseWheel += mouseWheelMove; const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - mGuiCursorX = std::max(0.f, std::min(mGuiCursorX, float(viewSize.width-1))); - mGuiCursorY = std::max(0.f, std::min(mGuiCursorY, float(viewSize.height-1))); + mGuiCursorX = std::max(0.f, std::min(mGuiCursorX, float(viewSize.width - 1))); + mGuiCursorY = std::max(0.f, std::min(mGuiCursorY, float(viewSize.height - 1))); MyGUI::InputManager::getInstance().injectMouseMove(static_cast(mGuiCursorX), static_cast(mGuiCursorY), mMouseWheel); } void MouseManager::warpMouse() { - mInputWrapper->warpMouse(static_cast(mGuiCursorX/mInvUiScalingFactor), static_cast(mGuiCursorY/mInvUiScalingFactor)); + mInputWrapper->warpMouse(static_cast(mGuiCursorX / mInvUiScalingFactor), static_cast(mGuiCursorY / mInvUiScalingFactor)); } } diff --git a/apps/openmw/mwinput/sensormanager.cpp b/apps/openmw/mwinput/sensormanager.cpp index d70662f6a..f3c2c2305 100644 --- a/apps/openmw/mwinput/sensormanager.cpp +++ b/apps/openmw/mwinput/sensormanager.cpp @@ -109,7 +109,6 @@ namespace MWInput if (Settings::Manager::getBool("enable gyroscope", "Input")) { int numSensors = SDL_NumSensors(); - for (int i = 0; i < numSensors; ++i) { if (SDL_SensorGetDeviceType(i) == SDL_SENSOR_GYRO) @@ -214,15 +213,15 @@ namespace MWInput switch (SDL_SensorGetType(sensor)) { - case SDL_SENSOR_ACCEL: - break; - case SDL_SENSOR_GYRO: - { - mGyroXSpeed = getGyroAxisSpeed(mGyroHAxis, arg); - mGyroYSpeed = getGyroAxisSpeed(mGyroVAxis, arg); - mGyroUpdateTimer = 0.f; + case SDL_SENSOR_ACCEL: + break; + case SDL_SENSOR_GYRO: + { + mGyroXSpeed = getGyroAxisSpeed(mGyroHAxis, arg); + mGyroYSpeed = getGyroAxisSpeed(mGyroVAxis, arg); + mGyroUpdateTimer = 0.f; - break; + break; } default: break; @@ -255,7 +254,7 @@ namespace MWInput rot[2] = mGyroXSpeed * dt * mGyroHSensitivity * 4 * (mInvertX ? -1 : 1); // Only actually turn player when we're not in vanity mode - if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && MWBase::Environment::get().getInputManager()->getControlSwitch("playerlooking")) + if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && MWBase::Environment::get().getInputManager()->getControlSwitch("playerlooking")) { MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); player.yaw(rot[2]); From b4e52a6bc8751fbd49e3f2a96294934f865501d9 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 17 Apr 2020 20:03:00 +0400 Subject: [PATCH 063/227] Add missing include --- apps/openmw/mwinput/keyboardmanager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index afeef632f..20115155e 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -1,5 +1,7 @@ #include "keyboardmanager.hpp" +#include + #include #include "../mwbase/environment.hpp" From 73552f1d3cfb2c9de93017b82bed7dfdd6b43a81 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 24 Apr 2020 12:43:17 +0400 Subject: [PATCH 064/227] Move control switch reading/writing to relevant class --- apps/openmw/mwinput/controlswitch.cpp | 41 +++++++++++++++++++++++++ apps/openmw/mwinput/controlswitch.hpp | 16 ++++++++++ apps/openmw/mwinput/inputmanagerimp.cpp | 28 +++-------------- 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwinput/controlswitch.cpp b/apps/openmw/mwinput/controlswitch.cpp index 6ea51064f..33c4b75dc 100644 --- a/apps/openmw/mwinput/controlswitch.cpp +++ b/apps/openmw/mwinput/controlswitch.cpp @@ -1,5 +1,11 @@ #include "controlswitch.hpp" +#include +#include +#include + +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -55,4 +61,39 @@ namespace MWInput } mSwitches[key] = value; } + + void ControlSwitch::write(ESM::ESMWriter& writer, Loading::Listener& /*progress*/) + { + ESM::ControlsState controls; + controls.mViewSwitchDisabled = !mSwitches["playerviewswitch"]; + controls.mControlsDisabled = !mSwitches["playercontrols"]; + controls.mJumpingDisabled = !mSwitches["playerjumping"]; + controls.mLookingDisabled = !mSwitches["playerlooking"]; + controls.mVanityModeDisabled = !mSwitches["vanitymode"]; + controls.mWeaponDrawingDisabled = !mSwitches["playerfighting"]; + controls.mSpellDrawingDisabled = !mSwitches["playermagic"]; + + writer.startRecord (ESM::REC_INPU); + controls.save(writer); + writer.endRecord (ESM::REC_INPU); + } + + void ControlSwitch::readRecord(ESM::ESMReader& reader, uint32_t type) + { + ESM::ControlsState controls; + controls.load(reader); + + set("playerviewswitch", !controls.mViewSwitchDisabled); + set("playercontrols", !controls.mControlsDisabled); + set("playerjumping", !controls.mJumpingDisabled); + set("playerlooking", !controls.mLookingDisabled); + set("vanitymode", !controls.mVanityModeDisabled); + set("playerfighting", !controls.mWeaponDrawingDisabled); + set("playermagic", !controls.mSpellDrawingDisabled); + } + + int ControlSwitch::countSavedGameRecords() const + { + return 1; + } } diff --git a/apps/openmw/mwinput/controlswitch.hpp b/apps/openmw/mwinput/controlswitch.hpp index 5fa475e77..3ca989e2a 100644 --- a/apps/openmw/mwinput/controlswitch.hpp +++ b/apps/openmw/mwinput/controlswitch.hpp @@ -3,6 +3,18 @@ #include +namespace ESM +{ + struct ControlsState; + class ESMReader; + class ESMWriter; +} + +namespace Loading +{ + class Listener; +} + namespace MWInput { class ControlSwitch @@ -14,6 +26,10 @@ namespace MWInput void set(const std::string& key, bool value); void clear(); + void write(ESM::ESMWriter& writer, Loading::Listener& progress); + void readRecord(ESM::ESMReader& reader, uint32_t type); + int countSavedGameRecords() const; + private: std::map mSwitches; }; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 4f1ee7855..48e1581be 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -208,39 +208,19 @@ namespace MWInput int InputManager::countSavedGameRecords() const { - return 1; + return mControlSwitch->countSavedGameRecords(); } - void InputManager::write(ESM::ESMWriter& writer, Loading::Listener& /*progress*/) + void InputManager::write(ESM::ESMWriter& writer, Loading::Listener& progress) { - ESM::ControlsState controls; - controls.mViewSwitchDisabled = !getControlSwitch("playerviewswitch"); - controls.mControlsDisabled = !getControlSwitch("playercontrols"); - controls.mJumpingDisabled = !getControlSwitch("playerjumping"); - controls.mLookingDisabled = !getControlSwitch("playerlooking"); - controls.mVanityModeDisabled = !getControlSwitch("vanitymode"); - controls.mWeaponDrawingDisabled = !getControlSwitch("playerfighting"); - controls.mSpellDrawingDisabled = !getControlSwitch("playermagic"); - - writer.startRecord (ESM::REC_INPU); - controls.save(writer); - writer.endRecord (ESM::REC_INPU); + mControlSwitch->write(writer, progress); } void InputManager::readRecord(ESM::ESMReader& reader, uint32_t type) { if (type == ESM::REC_INPU) { - ESM::ControlsState controls; - controls.load(reader); - - toggleControlSwitch("playerviewswitch", !controls.mViewSwitchDisabled); - toggleControlSwitch("playercontrols", !controls.mControlsDisabled); - toggleControlSwitch("playerjumping", !controls.mJumpingDisabled); - toggleControlSwitch("playerlooking", !controls.mLookingDisabled); - toggleControlSwitch("vanitymode", !controls.mVanityModeDisabled); - toggleControlSwitch("playerfighting", !controls.mWeaponDrawingDisabled); - toggleControlSwitch("playermagic", !controls.mSpellDrawingDisabled); + mControlSwitch->readRecord(reader, type); } } From a6514e7740712b92a69d57d901c1a95d7b338cf1 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 9 May 2020 11:28:30 +0400 Subject: [PATCH 065/227] Add missing include --- apps/openmw/mwinput/controlswitch.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwinput/controlswitch.hpp b/apps/openmw/mwinput/controlswitch.hpp index 3ca989e2a..38d01066b 100644 --- a/apps/openmw/mwinput/controlswitch.hpp +++ b/apps/openmw/mwinput/controlswitch.hpp @@ -2,6 +2,7 @@ #define MWINPUT_CONTROLSWITCH_H #include +#include namespace ESM { From c0b322b264bd87d647372e274ce0e123bee9e5c8 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 9 May 2020 16:00:00 +0300 Subject: [PATCH 066/227] Improve color mode handling in shaders --- components/shader/shadervisitor.cpp | 16 ++++++++----- files/shaders/lighting.glsl | 35 ++++++++++++++++------------- files/shaders/objects_fragment.glsl | 2 ++ files/shaders/terrain_fragment.glsl | 4 +++- 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 8165effb1..c30307f29 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -263,15 +263,21 @@ namespace Shader case osg::Material::OFF: colorMode = 0; break; - case GL_AMBIENT: - colorMode = 3; + case osg::Material::EMISSION: + colorMode = 1; break; default: - case GL_AMBIENT_AND_DIFFUSE: + case osg::Material::AMBIENT_AND_DIFFUSE: colorMode = 2; break; - case GL_EMISSION: - colorMode = 1; + case osg::Material::AMBIENT: + colorMode = 3; + break; + case osg::Material::DIFFUSE: + colorMode = 4; + break; + case osg::Material::SPECULAR: + colorMode = 5; break; } diff --git a/files/shaders/lighting.glsl b/files/shaders/lighting.glsl index 67308379f..e5587a448 100644 --- a/files/shaders/lighting.glsl +++ b/files/shaders/lighting.glsl @@ -2,6 +2,13 @@ uniform int colorMode; +const int ColorMode_None = 0; +const int ColorMode_Emission = 1; +const int ColorMode_AmbientAndDiffuse = 2; +const int ColorMode_Ambient = 3; +const int ColorMode_Diffuse = 4; +const int ColorMode_Specular = 5; + void perLight(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal, vec4 diffuse, vec3 ambient) { vec3 lightDir; @@ -22,22 +29,25 @@ vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, float shadowing vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, out vec3 shadowDiffuse) #endif { - vec4 diffuse; - vec3 ambient; - if (colorMode == 3) + vec4 diffuse = gl_FrontMaterial.diffuse; + vec3 ambient = gl_FrontMaterial.ambient.xyz; + vec3 emission = gl_FrontMaterial.emission.xyz; + if (colorMode == ColorMode_AmbientAndDiffuse) { - diffuse = gl_FrontMaterial.diffuse; + diffuse = vertexColor; ambient = vertexColor.xyz; } - else if (colorMode == 2) + else if (colorMode == ColorMode_Ambient) { - diffuse = vertexColor; ambient = vertexColor.xyz; } - else + else if (colorMode == ColorMode_Diffuse) { - diffuse = gl_FrontMaterial.diffuse; - ambient = gl_FrontMaterial.ambient.xyz; + diffuse = vertexColor; + } + else if (colorMode == ColorMode_Emission) + { + emission = vertexColor.xyz; } vec4 lightResult = vec4(0.0, 0.0, 0.0, diffuse.a); @@ -55,12 +65,7 @@ vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, out vec3 shadow lightResult.xyz += ambientLight + diffuseLight; } - lightResult.xyz += gl_LightModel.ambient.xyz * ambient; - - if (colorMode == 1) - lightResult.xyz += vertexColor.xyz; - else - lightResult.xyz += gl_FrontMaterial.emission.xyz; + lightResult.xyz += gl_LightModel.ambient.xyz * ambient + emission; #if @clamp lightResult = clamp(lightResult, vec4(0.0), vec4(1.0)); diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 5372833dd..6f064d0a6 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -178,6 +178,8 @@ void main() #else float shininess = gl_FrontMaterial.shininess; vec3 matSpec = gl_FrontMaterial.specular.xyz; + if (colorMode == ColorMode_Specular) + matSpec = passColor.xyz; #endif gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index 505061681..2dcb4e1eb 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -83,10 +83,12 @@ void main() #if @specularMap float shininess = 128.0; // TODO: make configurable - vec3 matSpec = vec3(diffuseTex.a, diffuseTex.a, diffuseTex.a); + vec3 matSpec = vec3(diffuseTex.a); #else float shininess = gl_FrontMaterial.shininess; vec3 matSpec = gl_FrontMaterial.specular.xyz; + if (colorMode == ColorMode_Specular) + matSpec = passColor.xyz; #endif gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing; From aa5161f99e1f4f87d9934733b9102d921500f850 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 9 May 2020 20:17:49 +0300 Subject: [PATCH 067/227] Log some more things --- apps/openmw/mwstate/statemanagerimp.cpp | 10 +++++++++- components/esm/loadcell.cpp | 14 ++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 01da76e90..a2b28d958 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -144,6 +144,7 @@ void MWState::StateManager::newGame (bool bypass) try { + Log(Debug::Info) << "Starting a new game"; MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); MWBase::Environment::get().getWorld()->startNewGame (bypass); @@ -220,6 +221,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mTimePlayed = mTimePlayed; profile.mDescription = description; + Log(Debug::Info) << "Making a screenshot for saved game '" << description << "'";; writeScreenshot(profile.mScreenshot); if (!slot) @@ -230,6 +232,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot // Make sure the animation state held by references is up to date before saving the game. MWBase::Environment::get().getMechanicsManager()->persistAnimationStates(); + Log(Debug::Info) << "Writing saved game '" << description << "' for character " << profile.mPlayerName; + // Write to a memory stream first. If there is an exception during the save process, we don't want to trash the // existing save file we are overwriting. std::stringstream stream; @@ -380,6 +384,8 @@ void MWState::StateManager::loadGame (const Character *character, const std::str { cleanup(); + Log(Debug::Info) << "Reading saved game " << filepath; + ESM::ESMReader reader; reader.open (filepath); @@ -418,6 +424,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str return; } mTimePlayed = profile.mTimePlayed; + Log(Debug::Info) << "Loading saved game '" << profile.mDescription << "' for character " << profile.mPlayerName; } break; @@ -526,6 +533,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str else { // Cell no longer exists (i.e. changed game files), choose a default cell + Log(Debug::Warning) << "Warning: Player character's cell no longer exists, changing to the default cell"; MWWorld::CellStore* cell = MWBase::Environment::get().getWorld()->getExterior(0,0); float x,y; MWBase::Environment::get().getWorld()->indexToPosition(0,0,x,y,false); @@ -628,7 +636,7 @@ bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), *it) == selectedContentFiles.end()) { - Log(Debug::Warning) << "Warning: Savegame dependency " << *it << " is missing."; + Log(Debug::Warning) << "Warning: Saved game dependency " << *it << " is missing."; notFound = true; } } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index f92a752a4..bf70aad96 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -210,13 +210,15 @@ namespace ESM std::string Cell::getDescription() const { if (mData.mFlags & Interior) - { return mName; - } - else - { - return std::to_string(mData.mX) + ", " + std::to_string(mData.mY); - } + + std::string cellGrid = "(" + std::to_string(mData.mX) + ", " + std::to_string(mData.mY) + ")"; + if (!mName.empty()) + return mName + ' ' + cellGrid; + // FIXME: should use sDefaultCellname GMST instead, but it's not available in this scope + std::string region = !mRegion.empty() ? mRegion : "Wilderness"; + + return region + ' ' + cellGrid; } bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool &isDeleted, bool ignoreMoves, MovedCellRef *mref) From 99cd99bc3b4bd467b3ec7914902b40f9373bab7a Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 9 May 2020 19:41:55 +0200 Subject: [PATCH 068/227] Remove unused BUILD_MYGUI_PLUGIN option --- CI/before_script.linux.sh | 1 - CI/before_script.msvc.sh | 3 +-- CI/before_script.osx.sh | 1 - CMakeLists.txt | 9 --------- 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/CI/before_script.linux.sh b/CI/before_script.linux.sh index c9b5b55bf..a1c04cf66 100755 --- a/CI/before_script.linux.sh +++ b/CI/before_script.linux.sh @@ -25,7 +25,6 @@ ${ANALYZE} cmake \ -DBUILD_ESSIMPORTER=${BUILD_OPENMW_CS} \ -DBUILD_WIZARD=${BUILD_OPENMW_CS} \ -DBUILD_NIFTEST=${BUILD_OPENMW_CS} \ - -DBUILD_MYGUI_PLUGIN=${BUILD_OPENMW_CS} \ -DBUILD_UNITTESTS=1 \ -DUSE_SYSTEM_TINYXML=1 \ -DDESIRED_QT_VERSION=5 \ diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 2427f2be1..235ef0295 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -759,8 +759,7 @@ echo cd $DEPS_INSTALL/.. echo echo "Setting up OpenMW build..." -add_cmake_opts -DBUILD_MYGUI_PLUGIN=no \ - -DOPENMW_MP_BUILD=on +add_cmake_opts -DOPENMW_MP_BUILD=on if [ ! -z $CI ]; then case $STEP in components ) diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index 1c3a71bdd..9fae97af6 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -19,6 +19,5 @@ cmake \ -D OPENMW_OSX_DEPLOYMENT=TRUE \ -D DESIRED_QT_VERSION=5 \ -D BUILD_ESMTOOL=FALSE \ --D BUILD_MYGUI_PLUGIN=FALSE \ -G"Unix Makefiles" \ .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 23f894156..ba73908ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,6 @@ option(BUILD_ESSIMPORTER "Build ESS (Morrowind save game) importer" ON) option(BUILD_BSATOOL "Build BSA extractor" ON) option(BUILD_ESMTOOL "Build ESM inspector" ON) option(BUILD_NIFTEST "Build nif file tester" ON) -option(BUILD_MYGUI_PLUGIN "Build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON) option(BUILD_DOCS "Build documentation." OFF ) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF) @@ -468,9 +467,6 @@ IF(NOT WIN32 AND NOT APPLE) IF(BUILD_WIZARD) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-wizard" DESTINATION "${BINDIR}" ) ENDIF(BUILD_WIZARD) - #if(BUILD_MYGUI_PLUGIN) - # INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Plugin_MyGUI_OpenMW_Resources.so" DESTINATION "${LIBDIR}" ) - #ENDIF(BUILD_MYGUI_PLUGIN) # Install licenses INSTALL(FILES "files/mygui/DejaVu Font License.txt" DESTINATION "${LICDIR}" ) @@ -517,11 +513,6 @@ if(WIN32) INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug) INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) - if(BUILD_MYGUI_PLUGIN) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Debug/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Debug) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) - ENDIF(BUILD_MYGUI_PLUGIN) - IF(DESIRED_QT_VERSION MATCHES 5) INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug) INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) From e095f6b306f162cf059f6228cceed18374502066 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 9 May 2020 19:43:48 +0200 Subject: [PATCH 069/227] Remove install for not existing file --- CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba73908ac..da9eceb7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -513,11 +513,6 @@ if(WIN32) INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug) INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) - IF(DESIRED_QT_VERSION MATCHES 5) - INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug) - INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) - ENDIF() - INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/resources" DESTINATION "." CONFIGURATIONS Debug) INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/resources" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) From a5d0d13e1435ad535ecbceebb43ec239a51f9ee4 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 9 May 2020 21:14:58 +0300 Subject: [PATCH 070/227] Allow guards to pursue an invisible player (bug #4774) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/aipursue.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e778c0f..7d16502fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Bug #1952: Incorrect particle lighting Bug #3676: NiParticleColorModifier isn't applied properly + Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #5358: ForceGreeting always resets the dialogue window completely Bug #5363: Enchantment autocalc not always 0/1 Bug #5364: Script fails/stops if trying to startscript an unknown script diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 1445a85d6..52b2866bb 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -3,6 +3,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" @@ -42,8 +43,9 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled()) return true; - if (isTargetMagicallyHidden(target)) - return true; + if (!MWBase::Environment::get().getWorld()->getLOS(target, actor) + || !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(target, actor)) + return false; if (target.getClass().getCreatureStats(target).isDead()) return true; From 41beca8125563729643407662ec51c04ddaa971c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 10 May 2020 10:13:19 +0400 Subject: [PATCH 071/227] Refactor actions order setup --- apps/openmw/mwinput/bindingsmanager.cpp | 232 +++++++++++------------- 1 file changed, 104 insertions(+), 128 deletions(-) diff --git a/apps/openmw/mwinput/bindingsmanager.cpp b/apps/openmw/mwinput/bindingsmanager.cpp index f21e184b7..11f714205 100644 --- a/apps/openmw/mwinput/bindingsmanager.cpp +++ b/apps/openmw/mwinput/bindingsmanager.cpp @@ -417,57 +417,89 @@ namespace MWInput std::string BindingsManager::getActionDescription(int action) { - std::map descriptions; - - if (action == A_Screenshot) - return "Screenshot"; - else if (action == A_ZoomIn) - return "Zoom In"; - else if (action == A_ZoomOut) - return "Zoom Out"; - else if (action == A_ToggleHUD) - return "Toggle HUD"; - - descriptions[A_Use] = "sUse"; - descriptions[A_Activate] = "sActivate"; - descriptions[A_MoveBackward] = "sBack"; - descriptions[A_MoveForward] = "sForward"; - descriptions[A_MoveLeft] = "sLeft"; - descriptions[A_MoveRight] = "sRight"; - descriptions[A_ToggleWeapon] = "sReady_Weapon"; - descriptions[A_ToggleSpell] = "sReady_Magic"; - descriptions[A_CycleSpellLeft] = "sPrevSpell"; - descriptions[A_CycleSpellRight] = "sNextSpell"; - descriptions[A_CycleWeaponLeft] = "sPrevWeapon"; - descriptions[A_CycleWeaponRight] = "sNextWeapon"; - descriptions[A_Console] = "sConsoleTitle"; - descriptions[A_Run] = "sRun"; - descriptions[A_Sneak] = "sCrouch_Sneak"; - descriptions[A_AutoMove] = "sAuto_Run"; - descriptions[A_Jump] = "sJump"; - descriptions[A_Journal] = "sJournal"; - descriptions[A_Rest] = "sRestKey"; - descriptions[A_Inventory] = "sInventory"; - descriptions[A_TogglePOV] = "sTogglePOVCmd"; - descriptions[A_QuickKeysMenu] = "sQuickMenu"; - descriptions[A_QuickKey1] = "sQuick1Cmd"; - descriptions[A_QuickKey2] = "sQuick2Cmd"; - descriptions[A_QuickKey3] = "sQuick3Cmd"; - descriptions[A_QuickKey4] = "sQuick4Cmd"; - descriptions[A_QuickKey5] = "sQuick5Cmd"; - descriptions[A_QuickKey6] = "sQuick6Cmd"; - descriptions[A_QuickKey7] = "sQuick7Cmd"; - descriptions[A_QuickKey8] = "sQuick8Cmd"; - descriptions[A_QuickKey9] = "sQuick9Cmd"; - descriptions[A_QuickKey10] = "sQuick10Cmd"; - descriptions[A_AlwaysRun] = "sAlways_Run"; - descriptions[A_QuickSave] = "sQuickSaveCmd"; - descriptions[A_QuickLoad] = "sQuickLoadCmd"; - - if (descriptions[action].empty()) - return std::string(); // not configurable - - return "#{" + descriptions[action] + "}"; + switch (action) + { + case A_Screenshot: + return "Screenshot"; + case A_ZoomIn: + return "Zoom In"; + case A_ZoomOut: + return "Zoom Out"; + case A_ToggleHUD: + return "Toggle HUD"; + case A_Use: + return "#{sUse}"; + case A_Activate: + return "#{sActivate}"; + case A_MoveBackward: + return "#{sBack}"; + case A_MoveForward: + return "#{sForward}"; + case A_MoveLeft: + return "#{sLeft}"; + case A_MoveRight: + return "#{sRight}"; + case A_ToggleWeapon: + return "#{sReady_Weapon}"; + case A_ToggleSpell: + return "#{sReady_Magic}"; + case A_CycleSpellLeft: + return "#{sPrevSpell}"; + case A_CycleSpellRight: + return "#{sNextSpell}"; + case A_CycleWeaponLeft: + return "#{sPrevWeapon}"; + case A_CycleWeaponRight: + return "#{sNextWeapon}"; + case A_Console: + return "#{sConsoleTitle}"; + case A_Run: + return "#{sRun}"; + case A_Sneak: + return "#{sCrouch_Sneak}"; + case A_AutoMove: + return "#{sAuto_Run}"; + case A_Jump: + return "#{sJump}"; + case A_Journal: + return "#{sJournal}"; + case A_Rest: + return "#{sRestKey}"; + case A_Inventory: + return "#{sInventory}"; + case A_TogglePOV: + return "#{sTogglePOVCmd}"; + case A_QuickKeysMenu: + return "#{sQuickMenu}"; + case A_QuickKey1: + return "#{sQuick1Cmd}"; + case A_QuickKey2: + return "#{sQuick2Cmd}"; + case A_QuickKey3: + return "#{sQuick3Cmd}"; + case A_QuickKey4: + return "#{sQuick4Cmd}"; + case A_QuickKey5: + return "#{sQuick5Cmd}"; + case A_QuickKey6: + return "#{sQuick6Cmd}"; + case A_QuickKey7: + return "#{sQuick7Cmd}"; + case A_QuickKey8: + return "#{sQuick8Cmd}"; + case A_QuickKey9: + return "#{sQuick9Cmd}"; + case A_QuickKey10: + return "#{sQuick10Cmd}"; + case A_AlwaysRun: + return "#{sAlways_Run}"; + case A_QuickSave: + return "#{sQuickSaveCmd}"; + case A_QuickLoad: + return "#{sQuickLoadCmd}"; + default: + return std::string(); // not configurable + } } std::string BindingsManager::getActionKeyBindingName(int action) @@ -519,86 +551,30 @@ namespace MWInput std::vector BindingsManager::getActionKeySorting() { - std::vector ret; - ret.push_back(A_MoveForward); - ret.push_back(A_MoveBackward); - ret.push_back(A_MoveLeft); - ret.push_back(A_MoveRight); - ret.push_back(A_TogglePOV); - ret.push_back(A_ZoomIn); - ret.push_back(A_ZoomOut); - ret.push_back(A_Run); - ret.push_back(A_AlwaysRun); - ret.push_back(A_Sneak); - ret.push_back(A_Activate); - ret.push_back(A_Use); - ret.push_back(A_ToggleWeapon); - ret.push_back(A_ToggleSpell); - ret.push_back(A_CycleSpellLeft); - ret.push_back(A_CycleSpellRight); - ret.push_back(A_CycleWeaponLeft); - ret.push_back(A_CycleWeaponRight); - ret.push_back(A_AutoMove); - ret.push_back(A_Jump); - ret.push_back(A_Inventory); - ret.push_back(A_Journal); - ret.push_back(A_Rest); - ret.push_back(A_Console); - ret.push_back(A_QuickSave); - ret.push_back(A_QuickLoad); - ret.push_back(A_ToggleHUD); - ret.push_back(A_Screenshot); - ret.push_back(A_QuickKeysMenu); - ret.push_back(A_QuickKey1); - ret.push_back(A_QuickKey2); - ret.push_back(A_QuickKey3); - ret.push_back(A_QuickKey4); - ret.push_back(A_QuickKey5); - ret.push_back(A_QuickKey6); - ret.push_back(A_QuickKey7); - ret.push_back(A_QuickKey8); - ret.push_back(A_QuickKey9); - ret.push_back(A_QuickKey10); - - return ret; + static const std::vector actions + { + A_MoveForward, A_MoveBackward, A_MoveLeft, A_MoveRight, A_TogglePOV, A_ZoomIn, A_ZoomOut, + A_Run, A_AlwaysRun, A_Sneak, A_Activate, A_Use, A_ToggleWeapon, A_ToggleSpell, + A_CycleSpellLeft, A_CycleSpellRight, A_CycleWeaponLeft, A_CycleWeaponRight, A_AutoMove, + A_Jump, A_Inventory, A_Journal, A_Rest, A_Console, A_QuickSave, A_QuickLoad, + A_ToggleHUD, A_Screenshot, A_QuickKeysMenu, A_QuickKey1, A_QuickKey2, A_QuickKey3, + A_QuickKey4, A_QuickKey5, A_QuickKey6, A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10 + }; + + return actions; } std::vector BindingsManager::getActionControllerSorting() { - std::vector ret; - ret.push_back(A_TogglePOV); - ret.push_back(A_ZoomIn); - ret.push_back(A_ZoomOut); - ret.push_back(A_Sneak); - ret.push_back(A_Activate); - ret.push_back(A_Use); - ret.push_back(A_ToggleWeapon); - ret.push_back(A_ToggleSpell); - ret.push_back(A_AutoMove); - ret.push_back(A_Jump); - ret.push_back(A_Inventory); - ret.push_back(A_Journal); - ret.push_back(A_Rest); - ret.push_back(A_QuickSave); - ret.push_back(A_QuickLoad); - ret.push_back(A_ToggleHUD); - ret.push_back(A_Screenshot); - ret.push_back(A_QuickKeysMenu); - ret.push_back(A_QuickKey1); - ret.push_back(A_QuickKey2); - ret.push_back(A_QuickKey3); - ret.push_back(A_QuickKey4); - ret.push_back(A_QuickKey5); - ret.push_back(A_QuickKey6); - ret.push_back(A_QuickKey7); - ret.push_back(A_QuickKey8); - ret.push_back(A_QuickKey9); - ret.push_back(A_QuickKey10); - ret.push_back(A_CycleSpellLeft); - ret.push_back(A_CycleSpellRight); - ret.push_back(A_CycleWeaponLeft); - ret.push_back(A_CycleWeaponRight); - - return ret; + static const std::vector actions + { + A_TogglePOV, A_ZoomIn, A_ZoomOut, A_Sneak, A_Activate, A_Use, A_ToggleWeapon, A_ToggleSpell, + A_AutoMove, A_Jump, A_Inventory, A_Journal, A_Rest, A_QuickSave, A_QuickLoad, A_ToggleHUD, + A_Screenshot, A_QuickKeysMenu, A_QuickKey1, A_QuickKey2, A_QuickKey3, A_QuickKey4, + A_QuickKey5, A_QuickKey6, A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10, + A_CycleSpellLeft, A_CycleSpellRight, A_CycleWeaponLeft, A_CycleWeaponRight + }; + + return actions; } void BindingsManager::enableDetectingBindingMode(int action, bool keyboard) From b91d0d889f1e0d0a0c2f88507a6ffe7517eb1e44 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 10 May 2020 14:38:07 +0300 Subject: [PATCH 072/227] Fix warning /home/travis/build/OpenMW/openmw/components/nifosg/nifloader.cpp:615:42: warning: using the result of an assignment as a condition without parentheses [-Wparentheses] if (hasVisController |= (ctrl->recType == Nif::RC_NiVisController)) ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/travis/build/OpenMW/openmw/components/nifosg/nifloader.cpp:615:42: note: place parentheses around the assignment to silence this warning if (hasVisController |= (ctrl->recType == Nif::RC_NiVisController)) ^ ( ) --- components/nifosg/nifloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 00576943a..be39f7d55 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -612,7 +612,7 @@ namespace NifOsg bool hasVisController = false; for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { - if (hasVisController |= (ctrl->recType == Nif::RC_NiVisController)) + if ((hasVisController |= (ctrl->recType == Nif::RC_NiVisController))) break; } From 3b4782959ebd67865d61ba844dfa736aaa385587 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 10 May 2020 14:57:06 +0200 Subject: [PATCH 073/227] Allow targeting non-unique actors with StartScript (bug #2311) --- CHANGELOG.md | 1 + apps/openmw/mwbase/scriptmanager.hpp | 2 +- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwscript/globalscripts.cpp | 189 +++++++++++++++----- apps/openmw/mwscript/globalscripts.hpp | 20 ++- apps/openmw/mwscript/interpretercontext.cpp | 72 +++----- apps/openmw/mwscript/interpretercontext.hpp | 25 +-- apps/openmw/mwscript/scriptmanagerimp.cpp | 14 +- apps/openmw/mwscript/scriptmanagerimp.hpp | 2 +- apps/openmw/mwworld/cells.cpp | 32 ++++ apps/openmw/mwworld/cells.hpp | 5 + apps/openmw/mwworld/cellstore.cpp | 27 +++ apps/openmw/mwworld/cellstore.hpp | 6 + apps/openmw/mwworld/worldimp.cpp | 7 + apps/openmw/mwworld/worldimp.hpp | 2 + components/esm/globalscript.cpp | 13 +- components/esm/globalscript.hpp | 2 + components/esm/savedgame.cpp | 2 +- components/interpreter/context.hpp | 2 - components/interpreter/scriptopcodes.hpp | 2 +- 20 files changed, 309 insertions(+), 118 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e778c0f..611a7b625 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ------ Bug #1952: Incorrect particle lighting + Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #3676: NiParticleColorModifier isn't applied properly Bug #5358: ForceGreeting always resets the dialogue window completely Bug #5363: Enchantment autocalc not always 0/1 diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp index 7bdeba132..da3454d34 100644 --- a/apps/openmw/mwbase/scriptmanager.hpp +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -35,7 +35,7 @@ namespace MWBase virtual ~ScriptManager() {} - virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0; + virtual bool run (const std::string& name, Interpreter::Context& interpreterContext) = 0; ///< Run the script with the given name (compile first, if not compiled yet) virtual bool compile (const std::string& name) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 31d4a1b3c..84f9b4984 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -189,6 +189,8 @@ namespace MWBase virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0; ///< Search is limited to the active cells. + virtual MWWorld::Ptr searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum) = 0; + virtual MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) = 0; ///< Return a pointer to a liveCellRef which contains \a ptr. /// \note Search is limited to the active cells. diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 786cce072..5dad9d6ec 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -5,83 +5,171 @@ #include #include +#include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "interpretercontext.hpp" +namespace +{ + struct ScriptCreatingVisitor : public boost::static_visitor + { + ESM::GlobalScript operator()(const MWWorld::Ptr &ptr) const + { + ESM::GlobalScript script; + script.mTargetRef.unset(); + if (!ptr.isEmpty()) + { + if (ptr.getCellRef().hasContentFile()) + { + script.mTargetId = ptr.getCellRef().getRefId(); + script.mTargetRef = ptr.getCellRef().getRefNum(); + } + else if (MWBase::Environment::get().getWorld()->getPlayerPtr() == ptr) + script.mTargetId = ptr.getCellRef().getRefId(); + } + return script; + } + + ESM::GlobalScript operator()(const std::pair &pair) const + { + ESM::GlobalScript script; + script.mTargetId = pair.second; + script.mTargetRef = pair.first; + return script; + } + }; + + struct PtrGettingVisitor : public boost::static_visitor + { + const MWWorld::Ptr* operator()(const MWWorld::Ptr &ptr) const + { + return &ptr; + } + + const MWWorld::Ptr* operator()(const std::pair &pair) const + { + return nullptr; + } + }; + + struct PtrResolvingVisitor : public boost::static_visitor + { + MWWorld::Ptr operator()(const MWWorld::Ptr &ptr) const + { + return ptr; + } + + MWWorld::Ptr operator()(const std::pair &pair) const + { + if (pair.second.empty()) + return MWWorld::Ptr(); + else if(pair.first.hasContentFile()) + return MWBase::Environment::get().getWorld()->searchPtrViaRefNum(pair.second, pair.first); + return MWBase::Environment::get().getWorld()->searchPtr(pair.second, false); + } + }; + + class MatchPtrVisitor : public boost::static_visitor + { + const MWWorld::Ptr& mPtr; + public: + MatchPtrVisitor(const MWWorld::Ptr& ptr) : mPtr(ptr) {} + + bool operator()(const MWWorld::Ptr &ptr) const + { + return ptr == mPtr; + } + + bool operator()(const std::pair &pair) const + { + return false; + } + }; +} + namespace MWScript { GlobalScriptDesc::GlobalScriptDesc() : mRunning (false) {} + const MWWorld::Ptr* GlobalScriptDesc::getPtrIfPresent() const + { + return boost::apply_visitor(PtrGettingVisitor(), mTarget); + } + + MWWorld::Ptr GlobalScriptDesc::getPtr() + { + MWWorld::Ptr ptr = boost::apply_visitor(PtrResolvingVisitor(), mTarget); + mTarget = ptr; + return ptr; + } + GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store) : mStore (store) {} - void GlobalScripts::addScript (const std::string& name, const std::string& targetId) + void GlobalScripts::addScript (const std::string& name, const MWWorld::Ptr& target) { - std::map::iterator iter = - mScripts.find (::Misc::StringUtils::lowerCase (name)); + const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) { if (const ESM::Script *script = mStore.get().search(name)) { - GlobalScriptDesc desc; - desc.mRunning = true; - desc.mLocals.configure (*script); - desc.mId = targetId; - - mScripts.insert (std::make_pair (name, desc)); + auto desc = std::make_shared(); + MWWorld::Ptr ptr = target; + desc->mTarget = ptr; + desc->mRunning = true; + desc->mLocals.configure (*script); + mScripts.insert (std::make_pair(name, desc)); } else { Log(Debug::Error) << "Failed to add global script " << name << ": script record not found"; } } - else if (!iter->second.mRunning) + else if (!iter->second->mRunning) { - iter->second.mRunning = true; - iter->second.mId = targetId; + iter->second->mRunning = true; + MWWorld::Ptr ptr = target; + iter->second->mTarget = ptr; } } void GlobalScripts::removeScript (const std::string& name) { - std::map::iterator iter = - mScripts.find (::Misc::StringUtils::lowerCase (name)); + const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter!=mScripts.end()) - iter->second.mRunning = false; + iter->second->mRunning = false; } bool GlobalScripts::isRunning (const std::string& name) const { - std::map::const_iterator iter = - mScripts.find (::Misc::StringUtils::lowerCase (name)); + const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) return false; - return iter->second.mRunning; + return iter->second->mRunning; } void GlobalScripts::run() { - for (std::map::iterator iter (mScripts.begin()); - iter!=mScripts.end(); ++iter) + for (const auto& script : mScripts) { - if (iter->second.mRunning) + if (script.second->mRunning) { - MWWorld::Ptr ptr; - - MWScript::InterpreterContext interpreterContext ( - &iter->second.mLocals, MWWorld::Ptr(), iter->second.mId); - - MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext); + MWScript::InterpreterContext context(script.second); + if (!MWBase::Environment::get().getScriptManager()->run(script.first, context)) + script.second->mRunning = false; } } } @@ -129,18 +217,15 @@ namespace MWScript void GlobalScripts::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { - for (std::map::const_iterator iter (mScripts.begin()); - iter!=mScripts.end(); ++iter) + for (const auto& iter : mScripts) { - ESM::GlobalScript script; + ESM::GlobalScript script = boost::apply_visitor (ScriptCreatingVisitor(), iter.second->mTarget); - script.mId = iter->first; + script.mId = iter.first; - iter->second.mLocals.write (script.mLocals, iter->first); + iter.second->mLocals.write (script.mLocals, iter.first); - script.mRunning = iter->second.mRunning ? 1 : 0; - - script.mTargetId = iter->second.mId; + script.mRunning = iter.second->mRunning ? 1 : 0; writer.startRecord (ESM::REC_GSCR); script.save (writer); @@ -155,8 +240,7 @@ namespace MWScript ESM::GlobalScript script; script.load (reader); - std::map::iterator iter = - mScripts.find (script.mId); + auto iter = mScripts.find (script.mId); if (iter==mScripts.end()) { @@ -164,8 +248,12 @@ namespace MWScript { try { - GlobalScriptDesc desc; - desc.mLocals.configure (*scriptRecord); + auto desc = std::make_shared(); + if (!script.mTargetId.empty()) + { + desc->mTarget = std::make_pair(script.mTargetRef, script.mTargetId); + } + desc->mLocals.configure (*scriptRecord); iter = mScripts.insert (std::make_pair (script.mId, desc)).first; } @@ -182,9 +270,8 @@ namespace MWScript return true; } - iter->second.mRunning = script.mRunning!=0; - iter->second.mLocals.read (script.mLocals, script.mId); - iter->second.mId = script.mTargetId; + iter->second->mRunning = script.mRunning!=0; + iter->second->mLocals.read (script.mLocals, script.mId); return true; } @@ -195,18 +282,28 @@ namespace MWScript Locals& GlobalScripts::getLocals (const std::string& name) { std::string name2 = ::Misc::StringUtils::lowerCase (name); - std::map::iterator iter = mScripts.find (name2); + auto iter = mScripts.find (name2); if (iter==mScripts.end()) { const ESM::Script *script = mStore.get().find (name); - GlobalScriptDesc desc; - desc.mLocals.configure (*script); + auto desc = std::make_shared(); + desc->mLocals.configure (*script); iter = mScripts.insert (std::make_pair (name2, desc)).first; } - return iter->second.mLocals; + return iter->second->mLocals; + } + + void GlobalScripts::updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated) + { + MatchPtrVisitor visitor(base); + for (const auto& script : mScripts) + { + if (boost::apply_visitor (visitor, script.second->mTarget)) + script.second->mTarget = updated; + } } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 9b7aa0514..36d89a7eb 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -1,17 +1,24 @@ #ifndef GAME_SCRIPT_GLOBALSCRIPTS_H #define GAME_SCRIPT_GLOBALSCRIPTS_H +#include + #include #include +#include +#include #include #include "locals.hpp" +#include "../mwworld/ptr.hpp" + namespace ESM { class ESMWriter; class ESMReader; + struct RefNum; } namespace Loading @@ -30,21 +37,25 @@ namespace MWScript { bool mRunning; Locals mLocals; - std::string mId; // ID used to start targeted script (empty if not a targeted script) + boost::variant > mTarget; // Used to start targeted script GlobalScriptDesc(); + + const MWWorld::Ptr* getPtrIfPresent() const; // Returns a Ptr if one has been resolved + + MWWorld::Ptr getPtr(); // Resolves mTarget to a Ptr and caches the (potentially empty) result }; class GlobalScripts { const MWWorld::ESMStore& mStore; - std::map mScripts; + std::map > mScripts; public: GlobalScripts (const MWWorld::ESMStore& store); - void addScript (const std::string& name, const std::string& targetId = ""); + void addScript (const std::string& name, const MWWorld::Ptr& target = MWWorld::Ptr()); void removeScript (const std::string& name); @@ -70,6 +81,9 @@ namespace MWScript Locals& getLocals (const std::string& name); ///< If the script \a name has not been added as a global script yet, it is added /// automatically, but is not set to running state. + + void updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated); + ///< Update the Ptrs stored in mTarget. Should be called after the reference has been moved to a new cell. }; } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 30c56406e..58596a2f4 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -1,7 +1,6 @@ #include "interpretercontext.hpp" #include -#include #include #include @@ -28,26 +27,6 @@ namespace MWScript { - MWWorld::Ptr InterpreterContext::getReferenceImp ( - const std::string& id, bool activeOnly, bool doThrow) - { - if (!id.empty()) - { - return MWBase::Environment::get().getWorld()->getPtr (id, activeOnly); - } - else - { - if (mReference.isEmpty() && !mTargetId.empty()) - mReference = - MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false); - - if (mReference.isEmpty() && doThrow) - throw std::runtime_error ("no implicit reference"); - - return mReference; - } - } - const MWWorld::Ptr InterpreterContext::getReferenceImp ( const std::string& id, bool activeOnly, bool doThrow) const { @@ -57,12 +36,11 @@ namespace MWScript } else { - if (mReference.isEmpty() && !mTargetId.empty()) - mReference = - MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false); + if (mReference.isEmpty() && mGlobalScriptDesc) + mReference = mGlobalScriptDesc->getPtr(); if (mReference.isEmpty() && doThrow) - throw std::runtime_error ("no implicit reference"); + throw MissingImplicitRefError(); return mReference; } @@ -80,7 +58,7 @@ namespace MWScript { const MWWorld::Ptr ptr = getReferenceImp (id, false); - id = ptr.getClass().getScript (ptr); + id = ptr.getClass().getScript (ptr); ptr.getRefData().setLocals ( *MWBase::Environment::get().getWorld()->getStore().get().find (id)); @@ -109,6 +87,8 @@ namespace MWScript } } + MissingImplicitRefError::MissingImplicitRefError() : std::runtime_error("no implicit reference") {} + int InterpreterContext::findLocalVariableIndex (const std::string& scriptId, const std::string& name, char type) const { @@ -134,16 +114,21 @@ namespace MWScript throw std::runtime_error (stream.str().c_str()); } + InterpreterContext::InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference) + : mLocals (locals), mReference (reference) + {} - InterpreterContext::InterpreterContext ( - MWScript::Locals *locals, const MWWorld::Ptr& reference, const std::string& targetId) - : mLocals (locals), mReference (reference), mTargetId (targetId) + InterpreterContext::InterpreterContext (std::shared_ptr globalScriptDesc) + : mLocals (&(globalScriptDesc->mLocals)) { - // If we run on a reference (local script, dialogue script or console with object - // selected), store the ID of that reference store it so it can be inherited by - // targeted scripts started from this one. - if (targetId.empty() && !reference.isEmpty()) - mTargetId = reference.getCellRef().getRefId(); + const MWWorld::Ptr* ptr = globalScriptDesc->getPtrIfPresent(); + // A nullptr here signifies that the script's target has not yet been resolved after loading the game. + // Script targets are lazily resolved to MWWorld::Ptrs (which can, upon resolution, be empty) + // because scripts started through dialogue often don't use their implicit target. + if (ptr) + mReference = *ptr; + else + mGlobalScriptDesc = globalScriptDesc; } int InterpreterContext::getLocalShort (int index) const @@ -437,7 +422,12 @@ namespace MWScript void InterpreterContext::startScript (const std::string& name, const std::string& targetId) { - MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, targetId); + MWWorld::Ptr target; + if (targetId.empty()) + target = getReference(false); + else + target = getReferenceImp(targetId); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, target); } void InterpreterContext::stopScript (const std::string& name) @@ -449,12 +439,7 @@ namespace MWScript { // NOTE: id may be empty, indicating an implicit reference - MWWorld::Ptr ref2; - - if (id.empty()) - ref2 = getReferenceImp(); - else - ref2 = MWBase::Environment::get().getWorld()->getPtr(id, false); + MWWorld::Ptr ref2 = getReferenceImp(id); if (ref2.getContainerStore()) // is the object contained? { @@ -578,11 +563,6 @@ namespace MWScript return getReferenceImp ("", true, required); } - std::string InterpreterContext::getTargetId() const - { - return mTargetId; - } - void InterpreterContext::updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated) { if (!mReference.isEmpty() && base == mReference) diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 0e001d692..1c465ee3e 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -1,8 +1,13 @@ #ifndef GAME_SCRIPT_INTERPRETERCONTEXT_H #define GAME_SCRIPT_INTERPRETERCONTEXT_H +#include +#include + #include +#include "globalscripts.hpp" + #include "../mwworld/ptr.hpp" namespace MWSound @@ -19,17 +24,17 @@ namespace MWScript { class Locals; + class MissingImplicitRefError : public std::runtime_error + { + public: + MissingImplicitRefError(); + }; + class InterpreterContext : public Interpreter::Context { Locals *mLocals; mutable MWWorld::Ptr mReference; - - std::string mTargetId; - - /// If \a id is empty, a reference the script is run from is returned or in case - /// of a non-local script the reference derived from the target ID. - MWWorld::Ptr getReferenceImp (const std::string& id = "", bool activeOnly = false, - bool doThrow=true); + std::shared_ptr mGlobalScriptDesc; /// If \a id is empty, a reference the script is run from is returned or in case /// of a non-local script the reference derived from the target ID. @@ -47,9 +52,9 @@ namespace MWScript char type) const; public: + InterpreterContext (std::shared_ptr globalScriptDesc); - InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference, - const std::string& targetId = ""); + InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference); ///< The ownership of \a locals is not transferred. 0-pointer allowed. virtual int getLocalShort (int index) const; @@ -153,8 +158,6 @@ namespace MWScript void updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated); ///< Update the Ptr stored in mReference, if there is one stored there. Should be called after the reference has been moved to a new cell. - - virtual std::string getTargetId() const; }; } diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 267ff7da6..b69eb7f60 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -19,6 +19,7 @@ #include "../mwworld/esmstore.hpp" #include "extensions.hpp" +#include "interpretercontext.hpp" namespace MWScript { @@ -88,7 +89,7 @@ namespace MWScript return false; } - void ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext) + bool ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext) { // compile script ScriptCollection::iterator iter = mScripts.find (name); @@ -100,7 +101,7 @@ namespace MWScript // failed -> ignore script from now on. std::vector empty; mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals()))); - return; + return false; } iter = mScripts.find (name); @@ -118,14 +119,19 @@ namespace MWScript } mInterpreter.run (&iter->second.first[0], iter->second.first.size(), interpreterContext); + return true; + } + catch (const MissingImplicitRefError& e) + { + Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what(); } catch (const std::exception& e) { - Log(Debug::Error) << "Execution of script " << name << " failed:"; - Log(Debug::Error) << e.what(); + Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what(); iter->second.first.clear(); // don't execute again. } + return false; } std::pair ScriptManager::compileAll() diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index c22a5da81..27567a191 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -55,7 +55,7 @@ namespace MWScript Compiler::Context& compilerContext, int warningsMode, const std::vector& scriptBlacklist); - virtual void run (const std::string& name, Interpreter::Context& interpreterContext); + virtual bool run (const std::string& name, Interpreter::Context& interpreterContext); ///< Run the script with the given name (compile first, if not compiled yet) virtual bool compile (const std::string& name); diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index a724c9bdb..bf107b422 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -270,6 +271,37 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) return Ptr(); } +MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& id, const ESM::RefNum& refNum) +{ + for (auto& pair : mInteriors) + { + Ptr ptr = getPtr(pair.second, id, refNum); + if (!ptr.isEmpty()) + return ptr; + } + for (auto& pair : mExteriors) + { + Ptr ptr = getPtr(pair.second, id, refNum); + if (!ptr.isEmpty()) + return ptr; + } + return Ptr(); +} + +MWWorld::Ptr MWWorld::Cells::getPtr(CellStore& cellStore, const std::string& id, const ESM::RefNum& refNum) +{ + if (cellStore.getState() == CellStore::State_Unloaded) + cellStore.preload(); + if (cellStore.getState() == CellStore::State_Preloaded) + { + if (cellStore.hasId(id)) + cellStore.load(); + else + return Ptr(); + } + return cellStore.searchViaRefNum(refNum); +} + void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector &out) { const MWWorld::Store &cells = mStore.get(); diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 4a6d7abf6..3e64ad975 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -13,6 +13,7 @@ namespace ESM class ESMWriter; struct CellId; struct Cell; + struct RefNum; } namespace Loading @@ -41,6 +42,8 @@ namespace MWWorld Ptr getPtrAndCache (const std::string& name, CellStore& cellStore); + Ptr getPtr(CellStore& cellStore, const std::string& id, const ESM::RefNum& refNum); + void writeCell (ESM::ESMWriter& writer, CellStore& cell) const; public: @@ -62,6 +65,8 @@ namespace MWWorld /// @note name must be lower case Ptr getPtr (const std::string& name); + Ptr getPtr(const std::string& id, const ESM::RefNum& refNum); + void rest (double hours); void recharge (float duration); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 607551f7d..599f345b8 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -435,6 +436,32 @@ namespace MWWorld return Ptr(); } + class RefNumSearchVisitor + { + const ESM::RefNum& mRefNum; + public: + RefNumSearchVisitor(const ESM::RefNum& refNum) : mRefNum(refNum) {} + + Ptr mFound; + + bool operator()(const Ptr& ptr) + { + if (ptr.getCellRef().getRefNum() == mRefNum) + { + mFound = ptr; + return false; + } + return true; + } + }; + + Ptr CellStore::searchViaRefNum (const ESM::RefNum& refNum) + { + RefNumSearchVisitor searchVisitor(refNum); + forEach(searchVisitor); + return searchVisitor.mFound; + } + float CellStore::getWaterLevel() const { if (isExterior()) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 371867631..edd8577ae 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -41,6 +41,7 @@ namespace ESM struct CellState; struct FogState; struct CellId; + struct RefNum; } namespace MWWorld @@ -242,6 +243,11 @@ namespace MWWorld Ptr searchViaActorId (int id); ///< Will return an empty Ptr if cell is not loaded. + Ptr searchViaRefNum (const ESM::RefNum& refNum); + ///< Will return an empty Ptr if cell is not loaded. Does not check references in + /// containers. + /// @note Triggers CellStore hasState flag. + float getWaterLevel() const; bool movedHere(const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a623a5d52..8caec9876 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -752,6 +753,11 @@ namespace MWWorld return mWorldScene->searchPtrViaActorId (actorId); } + Ptr World::searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum) + { + return mCells.getPtr (id, refNum); + } + struct FindContainerVisitor { ConstPtr mContainedPtr; @@ -1290,6 +1296,7 @@ namespace MWWorld mRendering->updatePtr(ptr, newPtr); MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr); mPhysics->updatePtr(ptr, newPtr); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr); MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->updateCell(ptr, newPtr); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index fd36e5ba2..7b6d2afdc 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -297,6 +297,8 @@ namespace MWWorld Ptr searchPtrViaActorId (int actorId) override; ///< Search is limited to the active cells. + Ptr searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum) override; + MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) override; ///< Return a pointer to a liveCellRef which contains \a ptr. /// \note Search is limited to the active cells. diff --git a/components/esm/globalscript.cpp b/components/esm/globalscript.cpp index a42cdc230..239d162f2 100644 --- a/components/esm/globalscript.cpp +++ b/components/esm/globalscript.cpp @@ -12,7 +12,11 @@ void ESM::GlobalScript::load (ESMReader &esm) mRunning = 0; esm.getHNOT (mRunning, "RUN_"); - mTargetId = esm.getHNOString ("TARG"); + mTargetRef.unset(); + if (esm.peekNextSub("TARG")) + mTargetId = esm.getHNString ("TARG"); + if (esm.peekNextSub("FRMR")) + mTargetRef.load(esm, true, "FRMR"); } void ESM::GlobalScript::save (ESMWriter &esm) const @@ -24,5 +28,10 @@ void ESM::GlobalScript::save (ESMWriter &esm) const if (mRunning) esm.writeHNT ("RUN_", mRunning); - esm.writeHNOString ("TARG", mTargetId); + if (!mTargetId.empty()) + { + esm.writeHNOString ("TARG", mTargetId); + if (mTargetRef.hasContentFile()) + mTargetRef.save (esm, true, "FRMR"); + } } diff --git a/components/esm/globalscript.hpp b/components/esm/globalscript.hpp index 8b7e62795..1a1a6cf4e 100644 --- a/components/esm/globalscript.hpp +++ b/components/esm/globalscript.hpp @@ -2,6 +2,7 @@ #define OPENMW_ESM_GLOBALSCRIPT_H #include "locals.hpp" +#include "cellref.hpp" namespace ESM { @@ -16,6 +17,7 @@ namespace ESM Locals mLocals; int mRunning; std::string mTargetId; // for targeted scripts + RefNum mTargetRef; void load (ESMReader &esm); void save (ESMWriter &esm) const; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index ea9fef4fb..633f7c9fd 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 5; +int ESM::SavedGame::sCurrentFormat = 6; void ESM::SavedGame::load (ESMReader &esm) { diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 4c320879e..88fdae128 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -108,8 +108,6 @@ namespace Interpreter virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global) = 0; - - virtual std::string getTargetId() const = 0; }; } diff --git a/components/interpreter/scriptopcodes.hpp b/components/interpreter/scriptopcodes.hpp index 976390eb5..c98bcd23e 100644 --- a/components/interpreter/scriptopcodes.hpp +++ b/components/interpreter/scriptopcodes.hpp @@ -26,7 +26,7 @@ namespace Interpreter { std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - runtime.getContext().startScript (name, runtime.getContext().getTargetId()); + runtime.getContext().startScript (name); } }; From 4e0c07de0fea488704f5222da4b42c8bb0e26626 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 10 May 2020 12:45:56 +0200 Subject: [PATCH 074/227] Build install target in CI --- CI/before_script.msvc.sh | 10 +++++++++- appveyor.yml | 5 +++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 235ef0295..d4be543bb 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -36,6 +36,7 @@ PLATFORM="" CONFIGURATION="" TEST_FRAMEWORK="" GOOGLE_INSTALL_ROOT="" +INSTALL_PREFIX="." while [ $# -gt 0 ]; do ARGSTR=$1 @@ -83,9 +84,13 @@ while [ $# -gt 0 ]; do t ) TEST_FRAMEWORK=true ;; + i ) + INSTALL_PREFIX=$(echo "$1" | sed 's;\\;/;g' | sed -E 's;/+;/;g') + shift ;; + h ) cat < Set the configuration, can also be set with environment variable CONFIGURATION. @@ -109,6 +114,8 @@ Options: Produce NMake makefiles instead of a Visual Studio solution. -V Run verbosely + -i + CMake install prefix EOF exit 0 ;; @@ -760,6 +767,7 @@ cd $DEPS_INSTALL/.. echo echo "Setting up OpenMW build..." add_cmake_opts -DOPENMW_MP_BUILD=on +add_cmake_opts -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" if [ ! -z $CI ]; then case $STEP in components ) diff --git a/appveyor.yml b/appveyor.yml index 5095e7abd..3057cc725 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -50,15 +50,16 @@ install: before_build: - cmd: git submodule update --init --recursive - - cmd: sh %APPVEYOR_BUILD_FOLDER%\CI\before_script.msvc.sh -c %configuration% -p %PLATFORM% -v %msvc% -V + - cmd: sh %APPVEYOR_BUILD_FOLDER%\CI\before_script.msvc.sh -c %configuration% -p %PLATFORM% -v %msvc% -V -i %APPVEYOR_BUILD_FOLDER%\install build_script: - cmd: if %PLATFORM%==Win32 set build=MSVC%msvc%_32 - cmd: if %PLATFORM%==x64 set build=MSVC%msvc%_64 - cmd: msbuild %build%\OpenMW.sln /t:Build /p:Configuration=%configuration% /m:2 /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + - cmd: cmake --install %build% --config %configuration% after_build: - - cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%.zip %APPVEYOR_BUILD_FOLDER%\MSVC%msvc%_64\%configuration%\ -xr"!*.pdb" + - cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%.zip %APPVEYOR_BUILD_FOLDER%\install -xr"!*.pdb" #- cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%_pdb.zip %APPVEYOR_BUILD_FOLDER%\MSVC%msvc%_64\%configuration%\*.pdb test: off From ece0db4f82aa94f9021c89dc12c2cf04b3eda0b7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 11 May 2020 15:11:32 +0300 Subject: [PATCH 075/227] Cap movement animation playback speed --- apps/openmw/mwmechanics/character.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b5addea70..92eec1af6 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2289,8 +2289,12 @@ void CharacterController::update(float duration, bool animationOnly) } else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed) { - float speedmult = speed / mMovementAnimSpeed; - mAnimation->adjustSpeedMult(mCurrentMovement, speedmult); + // Vanilla caps the played animation speed. + const float maxSpeedMult = 10.f; + const float speedMult = speed / mMovementAnimSpeed; + mAnimation->adjustSpeedMult(mCurrentMovement, std::min(maxSpeedMult, speedMult)); + // Make sure the actual speed is the "expected" speed even though the animation is slower + scale *= std::max(1.f, speedMult / maxSpeedMult); } if (!mSkipAnim) From d5806fd0ed009a0cf0618ea5b75b5a5387227c18 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 12 May 2020 14:33:00 +0400 Subject: [PATCH 076/227] Fix merge conflicts --- CHANGELOG.md | 2 +- components/esm/fogstate.cpp | 2 +- components/esm/savedgame.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f5bf7ef2..2b88b5f9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #3676: NiParticleColorModifier isn't applied properly Bug #4774: Guards are ignorant of an invisible player that tries to attack them + Bug #5108: Savegame bloating due to inefficient fog textures format Bug #5358: ForceGreeting always resets the dialogue window completely Bug #5363: Enchantment autocalc not always 0/1 Bug #5364: Script fails/stops if trying to startscript an unknown script @@ -170,7 +171,6 @@ Bug #5103: Sneaking state behavior is still inconsistent Bug #5104: Black Dart's enchantment doesn't trigger at low Enchant levels Bug #5106: Still can jump even when encumbered - Bug #5108: Savegame bloating due to inefficient fog textures format Bug #5110: ModRegion with a redundant numerical argument breaks script execution Bug #5112: Insufficient magicka for current spell not reflected on HUD icon Bug #5113: Unknown alchemy question mark not centered diff --git a/components/esm/fogstate.cpp b/components/esm/fogstate.cpp index 444da7ac9..ff20f339f 100644 --- a/components/esm/fogstate.cpp +++ b/components/esm/fogstate.cpp @@ -69,7 +69,7 @@ void ESM::FogState::load (ESMReader &esm) tex.mImageData.resize(imageSize); esm.getExact(&tex.mImageData[0], imageSize); - if (dataFormat < 6) + if (dataFormat < 7) convertFogOfWar(tex.mImageData); mFogTextures.push_back(tex); diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 633f7c9fd..e88c7cd8b 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 6; +int ESM::SavedGame::sCurrentFormat = 7; void ESM::SavedGame::load (ESMReader &esm) { From 77bdd124ee6a9f64ab25b33196f399a4578f7a30 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 12 May 2020 13:51:07 +0300 Subject: [PATCH 077/227] Address akortunov's save loading message complaints Add quotes to the character's name Don't print the full path to save file Use better terminology --- apps/openmw/mwstate/statemanagerimp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a2b28d958..86a26212f 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -232,7 +232,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot // Make sure the animation state held by references is up to date before saving the game. MWBase::Environment::get().getMechanicsManager()->persistAnimationStates(); - Log(Debug::Info) << "Writing saved game '" << description << "' for character " << profile.mPlayerName; + Log(Debug::Info) << "Writing saved game '" << description << "' for character '" << profile.mPlayerName << "'"; // Write to a memory stream first. If there is an exception during the save process, we don't want to trash the // existing save file we are overwriting. @@ -384,7 +384,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str { cleanup(); - Log(Debug::Info) << "Reading saved game " << filepath; + Log(Debug::Info) << "Reading save file " << boost::filesystem::path(filepath).filename().string(); ESM::ESMReader reader; reader.open (filepath); @@ -424,7 +424,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str return; } mTimePlayed = profile.mTimePlayed; - Log(Debug::Info) << "Loading saved game '" << profile.mDescription << "' for character " << profile.mPlayerName; + Log(Debug::Info) << "Loading saved game '" << profile.mDescription << "' for character '" << profile.mPlayerName << "'"; } break; From 58d78fb1261bb56e52a95e55cf5b48d376154c62 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 12 May 2020 18:21:02 +0300 Subject: [PATCH 078/227] Always pass the vertex color to the fragment shader --- files/shaders/objects_fragment.glsl | 3 +-- files/shaders/objects_vertex.glsl | 6 ++---- files/shaders/terrain_fragment.glsl | 3 +-- files/shaders/terrain_vertex.glsl | 6 ++---- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 6f064d0a6..78cc8c1dd 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -59,9 +59,8 @@ varying float linearDepth; #if !PER_PIXEL_LIGHTING centroid varying vec4 lighting; centroid varying vec3 shadowDiffuseLighting; -#else -centroid varying vec4 passColor; #endif +centroid varying vec4 passColor; varying vec3 passViewPos; varying vec3 passNormal; diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index dc8c91b03..bd97b2526 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -45,9 +45,8 @@ varying float linearDepth; #if !PER_PIXEL_LIGHTING centroid varying vec4 lighting; centroid varying vec3 shadowDiffuseLighting; -#else -centroid varying vec4 passColor; #endif +centroid varying vec4 passColor; varying vec3 passViewPos; varying vec3 passNormal; @@ -108,9 +107,8 @@ void main(void) #if !PER_PIXEL_LIGHTING lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting); -#else - passColor = gl_Color; #endif + passColor = gl_Color; passViewPos = viewPos.xyz; passNormal = gl_Normal.xyz; diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index 2dcb4e1eb..477b1bf9e 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -20,9 +20,8 @@ varying float linearDepth; #if !PER_PIXEL_LIGHTING centroid varying vec4 lighting; centroid varying vec3 shadowDiffuseLighting; -#else -centroid varying vec4 passColor; #endif +centroid varying vec4 passColor; varying vec3 passViewPos; varying vec3 passNormal; diff --git a/files/shaders/terrain_vertex.glsl b/files/shaders/terrain_vertex.glsl index 14e291f43..dc4ea802f 100644 --- a/files/shaders/terrain_vertex.glsl +++ b/files/shaders/terrain_vertex.glsl @@ -9,9 +9,8 @@ varying float linearDepth; #if !PER_PIXEL_LIGHTING centroid varying vec4 lighting; centroid varying vec3 shadowDiffuseLighting; -#else -centroid varying vec4 passColor; #endif +centroid varying vec4 passColor; varying vec3 passViewPos; varying vec3 passNormal; @@ -32,9 +31,8 @@ void main(void) #if !PER_PIXEL_LIGHTING lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting); -#else - passColor = gl_Color; #endif + passColor = gl_Color; passNormal = gl_Normal.xyz; passViewPos = viewPos.xyz; From 110e3761bf949409128ad32422dde2fca8eee617 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 12 May 2020 23:25:07 +0300 Subject: [PATCH 079/227] Disable small feature culling for orthographic cameras --- apps/openmw/mwrender/localmap.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index b7552008b..416a753eb 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -181,6 +181,10 @@ osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, f camera->setCullMask(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object | Mask_Static); camera->setNodeMask(Mask_RenderToTexture); + // Disable small feature culling, it's not going to be reliable for this camera + osg::Camera::CullingMode cullingMode = (osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING) & ~(osg::CullStack::SMALL_FEATURE_CULLING); + camera->setCullingMode(cullingMode); + osg::ref_ptr stateset = new osg::StateSet; stateset->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL), osg::StateAttribute::OVERRIDE); From d469e5ef1ac28ab382420bb64a0bab62f0ed7e99 Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Tue, 12 May 2020 22:33:48 +0200 Subject: [PATCH 080/227] Update PR changelog file for 0.47.0 --- CHANGELOG_PR.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index a699a3da7..0a039ec3f 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -13,21 +13,28 @@ The OpenMW team is proud to announce the release of version 0.47.0! Grab it from Check out the release video (***add link***) and the OpenMW-CS release video (***add link***) by the ***add flattering adjective*** Atahualpa, and see below for the full list of changes. Known Issues: -- There's currently no way to redirect the logging output to the command prompt on Windows Release builds -- this will be resolved in version 0.46.0 - To use generic Linux binaries, Qt4 and libpng12 must be installed on your system - On macOS, launching OpenMW from OpenMW-CS requires OpenMW.app and OpenMW-CS.app to be siblings New Features: -- ? +- Dialogue to split item stacks now displays the name of the trapped soul for stacks of soul gems (#5362) New Editor Features: - ? Bug Fixes: -- ? +- NiParticleColorModifier in NIF files is now properly handled which solves issues regarding particle effects, e.g., smoke and fire (#1952, #3676) +- Targetting non-unique actors in scripts is now supported (#2311) +- Guards no longer ignore attacks of invisible players but rather initiate dialogue and flee if the player resists being arrested (#4774) +- Changing the dialogue window without closing it no longer clears the dialogue history in order to allow, e.g., emulation of three-way dialogue via ForceGreeting (#5358) +- Scripts which try to start a non-existent global script now skip that step and continue execution instead of breaking (#5364) +- Selecting already equipped spells or magic items via hotkey no longer triggers the equip sound to play (#5367) +- 'Scale' argument in levelled creature lists is now taken into account when spawning creatures from such lists (#5369) +- 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: -- ? +- Verifier no longer checks for alleged 'race' entries in clothing body parts (#5400) Miscellaneous: -- ? \ No newline at end of file +- Prevent save-game bloating by using an appropriate fog texture format (#5108) +- Ensure that 'Enchantment autocalc" flag is treated as flag in OpenMW-CS and in our esm tools (#5363) \ No newline at end of file From a08a9518c3b4dd89b5a6e829d2d75b8baf903eca Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 13 May 2020 01:06:38 +0300 Subject: [PATCH 081/227] NIF version adjustments Cut down on obscure version numbers Call generateVersion without using a stream object --- components/nif/data.cpp | 2 +- components/nif/niffile.cpp | 2 +- components/nif/niffile.hpp | 4 ---- components/nif/node.hpp | 2 +- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index e46c0e84d..eb707e1bf 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -225,7 +225,7 @@ void NiSkinData::read(NIFStream *nif) trafo.scale = nif->getFloat(); int boneNum = nif->getInt(); - if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFFile::NIFVersion::VER_GAMEBRYO) + if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,1,0,0)) nif->skip(4); // NiSkinPartition link bones.resize(boneNum); diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 2070ee850..13c9ced60 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -144,7 +144,7 @@ void NIFFile::parse(Files::IStreamPtr stream) ver = nif.getUInt(); // 4.0.0.0 is an older, practically identical version of the format. // It's not used by Morrowind assets but Morrowind supports it. - if(ver != nif.generateVersion(4,0,0,0) && ver != VER_MW) + if(ver != NIFStream::generateVersion(4,0,0,0) && ver != VER_MW) fail("Unsupported NIF version: " + printVersion(ver)); // Number of records size_t recNum = nif.getInt(); diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 4d5620a37..9d8edac26 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -79,11 +79,7 @@ public: enum NIFVersion { VER_MW = 0x04000002, // 4.0.0.2. Main Morrowind NIF version. - VER_CI = 0x04020200, // 4.2.2.0. Main Culpa Innata NIF version, also used in Civ4. - VER_ZT2 = 0x0A000100, // 10.0.1.0. Main Zoo Tycoon 2 NIF version, also used in Oblivion and Civ4. VER_OB_OLD = 0x0A000102, // 10.0.1.2. Main older Oblivion NIF version. - VER_GAMEBRYO = 0x0A010000, // 10.1.0.0. Lots of games use it. The first version that has Gamebryo File Format header. - VER_CIV4 = 0x14000004, // 20.0.0.4. Main Civilization IV NIF version. VER_OB = 0x14000005, // 20.0.0.5. Main Oblivion NIF version. VER_BGS = 0x14020007 // 20.2.0.7. Main Fallout 3/4/76/New Vegas and Skyrim/SkyrimSE NIF version. }; diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 4c52cd158..71e66c53a 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -285,7 +285,7 @@ struct NiLODNode : public NiSwitchNode void read(NIFStream *nif) { NiSwitchNode::read(nif); - if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFFile::NIFVersion::VER_ZT2) + if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,0,1,0)) lodCenter = nif->getVector3(); unsigned int numLodLevels = nif->getUInt(); for (unsigned int i=0; i Date: Wed, 13 May 2020 01:25:39 +0300 Subject: [PATCH 082/227] Adjust NiPixelData loading --- components/nif/data.cpp | 14 +++++--------- components/nif/data.hpp | 3 ++- components/nif/nifstream.hpp | 12 ++++++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index eb707e1bf..2c5568397 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -163,11 +163,8 @@ void NiPixelData::read(NIFStream *nif) { fmt = (Format)nif->getUInt(); - rmask = nif->getUInt(); // usually 0xff - gmask = nif->getUInt(); // usually 0xff00 - bmask = nif->getUInt(); // usually 0xff0000 - amask = nif->getUInt(); // usually 0xff000000 or zero - + for (unsigned int i = 0; i < 4; ++i) + colorMask[i] = nif->getUInt(); bpp = nif->getUInt(); // 8 bytes of "Old Fast Compare". Whatever that means. @@ -190,10 +187,9 @@ void NiPixelData::read(NIFStream *nif) } // Read the data - unsigned int dataSize = nif->getUInt(); - data.reserve(dataSize); - for (unsigned i=0; igetChar()); + unsigned int numPixels = nif->getUInt(); + if (numPixels) + nif->getUChars(data, numPixels); } void NiPixelData::post(NIFFile *nif) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index a0d4960e0..39901b584 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -124,7 +124,8 @@ public: }; Format fmt; - unsigned int rmask, gmask, bmask, amask, bpp; + unsigned int colorMask[4]; + unsigned int bpp; NiPalettePtr palette; unsigned int numberOfMipmaps; diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index c78377448..97075c288 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -200,6 +200,18 @@ public: return result; } + void getChars(std::vector &vec, size_t size) + { + vec.resize(size); + readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + } + + void getUChars(std::vector &vec, size_t size) + { + vec.resize(size); + readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + } + void getUShorts(std::vector &vec, size_t size) { vec.resize(size); From 30fc2e3e5e1e1ed4e5435023dcd16eab8347f324 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 16 Oct 2019 23:21:32 +0300 Subject: [PATCH 083/227] Add basic NiPathController support (movement only) --- components/nif/controller.cpp | 10 +++---- components/nif/controller.hpp | 14 ++++++++++ components/nifosg/controller.cpp | 46 ++++++++++++++++++++++++++++++++ components/nifosg/controller.hpp | 19 +++++++++++++ components/nifosg/nifloader.cpp | 22 +++++++++++++++ 5 files changed, 105 insertions(+), 6 deletions(-) diff --git a/components/nif/controller.cpp b/components/nif/controller.cpp index 9a4c1b065..c63c83676 100644 --- a/components/nif/controller.cpp +++ b/components/nif/controller.cpp @@ -123,12 +123,10 @@ namespace Nif { Controller::read(nif); - /* - int = 1 - 2xfloat - short = 0 or 1 - */ - nif->skip(14); + bankDir = nif->getInt(); + maxBankAngle = nif->getFloat(); + smoothing = nif->getFloat(); + followAxis = nif->getShort(); posData.read(nif); floatData.read(nif); } diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 364eff1cd..41dd14fac 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -96,6 +96,20 @@ public: NiPosDataPtr posData; NiFloatDataPtr floatData; + enum Flags + { + Flag_OpenCurve = 0x020, + Flag_AllowFlip = 0x040, + Flag_Bank = 0x080, + Flag_ConstVelocity = 0x100, + Flag_Follow = 0x200, + Flag_FlipFollowAxis = 0x400 + }; + + int bankDir; + float maxBankAngle, smoothing; + short followAxis; + void read(NIFStream *nif); void post(NIFFile *nif); }; diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 100aa234a..a088ead4c 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -521,4 +521,50 @@ void ParticleSystemController::operator() (osg::Node* node, osg::NodeVisitor* nv traverse(node, nv); } +PathController::PathController(const PathController ©, const osg::CopyOp ©op) + : osg::NodeCallback(copy, copyop) + , Controller(copy) + , mPath(copy.mPath) + , mPercent(copy.mPercent) + , mFlags(copy.mFlags) +{ +} + +PathController::PathController(const Nif::NiPathController* ctrl) + : mPath(ctrl->posData->mKeyList, osg::Vec3f()) + , mPercent(ctrl->floatData->mKeyList, 1.f) + , mFlags(ctrl->flags) +{ +} + +float PathController::getPercent(float time) const +{ + float percent = mPercent.interpKey(time); + if (percent < 0.f) + percent = std::fmod(percent, 1.f) + 1.f; + else if (percent > 1.f) + percent = std::fmod(percent, 1.f); + return percent; +} + +void PathController::operator() (osg::Node* node, osg::NodeVisitor* nv) +{ + if (mPath.empty() || mPercent.empty() || !hasInput()) + { + traverse(node, nv); + return; + } + + osg::MatrixTransform* trans = static_cast(node); + osg::Matrix mat = trans->getMatrix(); + + float time = getInputValue(nv); + float percent = getPercent(time); + osg::Vec3f pos(mPath.interpKey(percent)); + mat.setTrans(pos); + trans->setMatrix(mat); + + traverse(node, nv); +} + } diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index ad6ee4d87..3f66013a2 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -342,6 +342,25 @@ namespace NifOsg float mEmitStop; }; + class PathController : public osg::NodeCallback, public SceneUtil::Controller + { + public: + PathController(const Nif::NiPathController* ctrl); + PathController() = default; + PathController(const PathController& copy, const osg::CopyOp& copyop); + + META_Object(NifOsg, PathController) + + virtual void operator() (osg::Node*, osg::NodeVisitor*); + + private: + Vec3Interpolator mPath; + FloatInterpolator mPercent; + int mFlags{0}; + + float getPercent(float time) const; + }; + } #endif diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index be39f7d55..650d8db94 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -752,6 +752,17 @@ namespace NifOsg node->addUpdateCallback(callback); } } + else if (ctrl->recType == Nif::RC_NiPathController) + { + const Nif::NiPathController *path = static_cast(ctrl.getPtr()); + if (!path->posData.empty() && !path->floatData.empty()) + { + osg::ref_ptr callback(new PathController(path)); + + setupController(path, callback, animflags); + node->addUpdateCallback(callback); + } + } else if (ctrl->recType == Nif::RC_NiVisController) { handleVisController(static_cast(ctrl.getPtr()), node, animflags); @@ -780,6 +791,17 @@ namespace NifOsg transformNode->addUpdateCallback(callback); } } + else if (ctrl->recType == Nif::RC_NiPathController) + { + const Nif::NiPathController *path = static_cast(ctrl.getPtr()); + if (!path->posData.empty() && !path->floatData.empty()) + { + osg::ref_ptr callback(new PathController(path)); + + setupController(path, callback, animflags); + transformNode->addUpdateCallback(callback); + } + } else if (ctrl->recType == Nif::RC_NiVisController) { handleVisController(static_cast(ctrl.getPtr()), transformNode, animflags); From fa861f5d8e30ced86296afc42baa6f8a0545b37c Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 13 May 2020 18:20:37 +0200 Subject: [PATCH 084/227] Revert remove of platforms install --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index da9eceb7c..ba73908ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -513,6 +513,11 @@ if(WIN32) INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug) INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) + IF(DESIRED_QT_VERSION MATCHES 5) + INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug) + INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) + ENDIF() + INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/resources" DESTINATION "." CONFIGURATIONS Debug) INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/resources" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) From 14d0ca4cd3e9aa9e329590913d5f8fb55077587d Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 25 Nov 2018 11:42:26 +0300 Subject: [PATCH 085/227] Cast float to btScalar --- .../detournavigator/recastmeshbuilder.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index d96ba2f29..f61368357 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -163,15 +163,15 @@ namespace DetourNavigator transformBoundingBox(transform, aabbMin, aabbMax); - aabbMin.setX(std::max(mBounds.mMin.x(), aabbMin.x())); - aabbMin.setX(std::min(mBounds.mMax.x(), aabbMin.x())); - aabbMin.setY(std::max(mBounds.mMin.y(), aabbMin.y())); - aabbMin.setY(std::min(mBounds.mMax.y(), aabbMin.y())); - - aabbMax.setX(std::max(mBounds.mMin.x(), aabbMax.x())); - aabbMax.setX(std::min(mBounds.mMax.x(), aabbMax.x())); - aabbMax.setY(std::max(mBounds.mMin.y(), aabbMax.y())); - aabbMax.setY(std::min(mBounds.mMax.y(), aabbMax.y())); + aabbMin.setX(std::max(static_cast(mBounds.mMin.x()), aabbMin.x())); + aabbMin.setX(std::min(static_cast(mBounds.mMax.x()), aabbMin.x())); + aabbMin.setY(std::max(static_cast(mBounds.mMin.y()), aabbMin.y())); + aabbMin.setY(std::min(static_cast(mBounds.mMax.y()), aabbMin.y())); + + aabbMax.setX(std::max(static_cast(mBounds.mMin.x()), aabbMax.x())); + aabbMax.setX(std::min(static_cast(mBounds.mMax.x()), aabbMax.x())); + aabbMax.setY(std::max(static_cast(mBounds.mMin.y()), aabbMax.y())); + aabbMax.setY(std::min(static_cast(mBounds.mMax.y()), aabbMax.y())); transformBoundingBox(transform.inverse(), aabbMin, aabbMax); From ef5a5ef43f9a91795339ef3ea97c505c1e9dc28d Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 25 Nov 2018 14:03:28 +0300 Subject: [PATCH 086/227] Print not matched values with full precision --- .../detournavigator/operators.hpp | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/operators.hpp b/apps/openmw_test_suite/detournavigator/operators.hpp index a473632ba..e34d6278a 100644 --- a/apps/openmw_test_suite/detournavigator/operators.hpp +++ b/apps/openmw_test_suite/detournavigator/operators.hpp @@ -20,22 +20,62 @@ namespace DetourNavigator } } +namespace +{ + template + struct Wrapper { + const T& mValue; + }; + + template + inline testing::Message& writeRange(testing::Message& message, const Range& range) + { + message << "{\n"; + for (const auto& v : range) + message << Wrapper::type> {v} << ",\n"; + return message << "}"; + } +} + namespace testing { + template <> + inline testing::Message& Message::operator <<(const osg::Vec3f& value) + { + return (*this) << "osg::Vec3f(" << std::setprecision(std::numeric_limits::max_exponent10) << value.x() + << ", " << std::setprecision(std::numeric_limits::max_exponent10) << value.y() + << ", " << std::setprecision(std::numeric_limits::max_exponent10) << value.z() + << ')'; + } + + template <> + inline testing::Message& Message::operator <<(const Wrapper& value) + { + return (*this) << value.mValue; + } + + template <> + inline testing::Message& Message::operator <<(const Wrapper& value) + { + return (*this) << std::setprecision(std::numeric_limits::max_exponent10) << value.mValue; + } + template <> inline testing::Message& Message::operator <<(const std::deque& value) { - (*this) << "{\n"; - for (const auto& v : value) - { - std::ostringstream stream; - stream << "osg::Vec3f(" - << std::setprecision(std::numeric_limits::max_exponent10) << v.x() << ", " - << std::setprecision(std::numeric_limits::max_exponent10) << v.y() << ", " - << std::setprecision(std::numeric_limits::max_exponent10) << v.z() << ")"; - (*this) << stream.str() << ",\n"; - } - return (*this) << "}"; + return writeRange(*this, value); + } + + template <> + inline testing::Message& Message::operator <<(const std::vector& value) + { + return writeRange(*this, value); + } + + template <> + inline testing::Message& Message::operator <<(const std::vector& value) + { + return writeRange(*this, value); } } From 2d7c3bae6127fc3717d5e3a8e8830215450fb47f Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 25 Nov 2018 11:46:09 +0300 Subject: [PATCH 087/227] Support bullet with double precision --- apps/openmw/mwphysics/heightfield.cpp | 38 ++++++++++++++++++- apps/openmw/mwphysics/heightfield.hpp | 5 +++ .../detournavigator/recastmeshbuilder.cpp | 4 +- .../nifloader/testbulletnifloader.cpp | 36 ++++++++++++++++-- 4 files changed, 77 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwphysics/heightfield.cpp b/apps/openmw/mwphysics/heightfield.cpp index 52aed9c07..e1448116b 100644 --- a/apps/openmw/mwphysics/heightfield.cpp +++ b/apps/openmw/mwphysics/heightfield.cpp @@ -7,12 +7,48 @@ #include +#include + +namespace +{ + template + auto makeHeights(const T* heights, float sqrtVerts) + -> std::enable_if_t::value, std::vector> + { + return {}; + } + + template + auto makeHeights(const T* heights, float sqrtVerts) + -> std::enable_if_t::value, std::vector> + { + return std::vector(heights, heights + static_cast(sqrtVerts * sqrtVerts)); + } + + template + auto getHeights(const T* floatHeights, const std::vector&) + -> std::enable_if_t::value, const btScalar*> + { + return floatHeights; + } + + template + auto getHeights(const T*, const std::vector& btScalarHeights) + -> std::enable_if_t::value, const btScalar*> + { + return btScalarHeights.data(); + } +} + namespace MWPhysics { HeightField::HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject) + : mHeights(makeHeights(heights, sqrtVerts)) { mShape = new btHeightfieldTerrainShape( - sqrtVerts, sqrtVerts, heights, 1, + sqrtVerts, sqrtVerts, + getHeights(heights, mHeights), + 1, minH, maxH, 2, PHY_FLOAT, false ); diff --git a/apps/openmw/mwphysics/heightfield.hpp b/apps/openmw/mwphysics/heightfield.hpp index f248186db..2ba58afff 100644 --- a/apps/openmw/mwphysics/heightfield.hpp +++ b/apps/openmw/mwphysics/heightfield.hpp @@ -3,6 +3,10 @@ #include +#include + +#include + class btCollisionObject; class btHeightfieldTerrainShape; @@ -27,6 +31,7 @@ namespace MWPhysics btHeightfieldTerrainShape* mShape; btCollisionObject* mCollisionObject; osg::ref_ptr mHoldObject; + std::vector mHeights; void operator=(const HeightField&); HeightField(const HeightField&); diff --git a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp index 6c474765d..c86dee6e5 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp @@ -363,11 +363,11 @@ namespace AreaType_ground ); const auto recastMesh = builder.create(mGeneration, mRevision); - EXPECT_EQ(recastMesh->getVertices(), std::vector({ + EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector({ 1.41421353816986083984375, 0, 1.1920928955078125e-07, -1.41421353816986083984375, 0, -1.1920928955078125e-07, 1.1920928955078125e-07, 0, -1.41421353816986083984375, - })); + }))); EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2})); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector({AreaType_ground})); } diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 36f251246..6ec94fd68 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -9,6 +9,8 @@ #include #include +#include + namespace { template @@ -30,6 +32,34 @@ namespace shape.processAllTriangles(&callback, aabbMin, aabbMax); return result; } + + bool isNear(btScalar lhs, btScalar rhs) + { + return std::abs(lhs - rhs) <= 1e-5; + } + + bool isNear(const btVector3& lhs, const btVector3& rhs) + { + return std::equal( + static_cast(lhs), + static_cast(lhs) + 3, + static_cast(rhs), + [] (btScalar lhs, btScalar rhs) { return isNear(lhs, rhs); } + ); + } + + bool isNear(const btMatrix3x3& lhs, const btMatrix3x3& rhs) + { + for (int i = 0; i < 3; ++i) + if (!isNear(lhs[i], rhs[i])) + return false; + return true; + } + + bool isNear(const btTransform& lhs, const btTransform& rhs) + { + return isNear(lhs.getOrigin(), rhs.getOrigin()) && isNear(lhs.getBasis(), rhs.getBasis()); + } } static std::ostream& operator <<(std::ostream& stream, const btVector3& value) @@ -157,7 +187,7 @@ static bool operator ==(const btCompoundShape& lhs, const btCompoundShape& rhs) for (int i = 0; i < lhs.getNumChildShapes(); ++i) { if (!compareObjects(lhs.getChildShape(i), rhs.getChildShape(i)) - || !(lhs.getChildTransform(i) == rhs.getChildTransform(i))) + || !isNear(lhs.getChildTransform(i), rhs.getChildTransform(i))) return false; } return true; @@ -165,13 +195,13 @@ static bool operator ==(const btCompoundShape& lhs, const btCompoundShape& rhs) static bool operator ==(const btBoxShape& lhs, const btBoxShape& rhs) { - return lhs.getLocalScaling() == rhs.getLocalScaling() + return isNear(lhs.getLocalScaling(), rhs.getLocalScaling()) && lhs.getHalfExtentsWithoutMargin() == rhs.getHalfExtentsWithoutMargin(); } static bool operator ==(const btBvhTriangleMeshShape& lhs, const btBvhTriangleMeshShape& rhs) { - return lhs.getLocalScaling() == rhs.getLocalScaling() + return isNear(lhs.getLocalScaling(), rhs.getLocalScaling()) && lhs.usesQuantizedAabbCompression() == rhs.usesQuantizedAabbCompression() && lhs.getOwnsBvh() == rhs.getOwnsBvh() && getTriangles(lhs) == getTriangles(rhs); From 66da72048a5893bc8c9b20f6f7210fd45b6a74bf Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 20 Feb 2019 23:41:16 +0300 Subject: [PATCH 088/227] Update bullet for windows up to 2.87 --- CI/before_script.msvc.sh | 12 ++++++------ appveyor.yml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index d4be543bb..3a0b062e6 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -393,9 +393,9 @@ if [ -z $SKIP_DOWNLOAD ]; then fi # Bullet - download "Bullet 2.86" \ - "https://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" \ - "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" + download "Bullet 2.87" \ + "https://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z" \ + "Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z" # FFmpeg download "FFmpeg 3.2.4" \ @@ -533,15 +533,15 @@ fi cd $DEPS echo # Bullet -printf "Bullet 2.86... " +printf "Bullet 2.87... " { cd $DEPS_INSTALL if [ -d Bullet ]; then printf -- "Exists. (No version checking) " elif [ -z $SKIP_EXTRACT ]; then rm -rf Bullet - eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP - mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet + eval 7z x -y "${DEPS}/Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP + mv "Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}" Bullet fi export BULLET_ROOT="$(real_pwd)/Bullet" echo Done. diff --git a/appveyor.yml b/appveyor.yml index 3057cc725..e2c13ed94 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -30,8 +30,8 @@ configuration: clone_depth: 1 cache: - - C:\projects\openmw\deps\Bullet-2.86-msvc2015-win32.7z - - C:\projects\openmw\deps\Bullet-2.86-msvc2015-win64.7z + - C:\projects\openmw\deps\Bullet-2.87-msvc2015-win32.7z + - C:\projects\openmw\deps\Bullet-2.87-msvc2015-win64.7z - C:\projects\openmw\deps\MyGUI-3.2.2-msvc2015-win32.7z - C:\projects\openmw\deps\MyGUI-3.2.2-msvc2015-win64.7z - C:\projects\openmw\deps\OSG-3.4.1-scrawl-msvc2015-win32.7z From f0e2ee45fae4c3d7060193a28b3fb4479fec7dba Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 13 May 2020 21:17:08 +0200 Subject: [PATCH 089/227] reuse ImplicitRef and ExplicitRef for enable, disable, getdisabled, startscript; move scriptrunning and stopscript --- apps/openmw/mwscript/interpretercontext.cpp | 70 --------- apps/openmw/mwscript/interpretercontext.hpp | 15 -- apps/openmw/mwscript/miscextensions.cpp | 84 +++++++++++ .../mwscript/transformationextensions.cpp | 45 ++++++ components/CMakeLists.txt | 2 +- components/compiler/exprparser.cpp | 92 +----------- components/compiler/extensions0.cpp | 7 + components/compiler/generator.cpp | 133 ------------------ components/compiler/generator.hpp | 14 -- components/compiler/lineparser.cpp | 54 +------ components/compiler/opcodes.hpp | 12 ++ components/compiler/scanner.cpp | 3 - components/compiler/scanner.hpp | 5 +- components/compiler/stringparser.cpp | 9 +- components/interpreter/context.hpp | 15 -- components/interpreter/installopcodes.cpp | 18 --- components/interpreter/miscopcodes.hpp | 72 ---------- components/interpreter/scriptopcodes.hpp | 63 --------- components/interpreter/spatialopcodes.hpp | 43 ------ 19 files changed, 155 insertions(+), 601 deletions(-) delete mode 100644 components/interpreter/scriptopcodes.hpp delete mode 100644 components/interpreter/spatialopcodes.hpp diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 58596a2f4..f9d85375b 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -415,58 +415,6 @@ namespace MWScript return MWBase::Environment::get().getWorld()->getCellName(); } - bool InterpreterContext::isScriptRunning (const std::string& name) const - { - return MWBase::Environment::get().getScriptManager()->getGlobalScripts().isRunning (name); - } - - void InterpreterContext::startScript (const std::string& name, const std::string& targetId) - { - MWWorld::Ptr target; - if (targetId.empty()) - target = getReference(false); - else - target = getReferenceImp(targetId); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, target); - } - - void InterpreterContext::stopScript (const std::string& name) - { - MWBase::Environment::get().getScriptManager()->getGlobalScripts().removeScript (name); - } - - float InterpreterContext::getDistance (const std::string& name, const std::string& id) const - { - // NOTE: id may be empty, indicating an implicit reference - - MWWorld::Ptr ref2 = getReferenceImp(id); - - if (ref2.getContainerStore()) // is the object contained? - { - MWWorld::Ptr container = MWBase::Environment::get().getWorld()->findContainer(ref2); - - if (!container.isEmpty()) - ref2 = container; - else - throw std::runtime_error("failed to find container ptr"); - } - - const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->getPtr(name, false); - - // If the objects are in different worldspaces, return a large value (just like vanilla) - if (!ref.isInCell() || !ref2.isInCell() || ref.getCell()->getCell()->getCellId().mWorldspace != ref2.getCell()->getCell()->getCellId().mWorldspace) - return std::numeric_limits::max(); - - double diff[3]; - - const float* const pos1 = ref.getRefData().getPosition().pos; - const float* const pos2 = ref2.getRefData().getPosition().pos; - for (int i=0; i<3; ++i) - diff[i] = pos1[i] - pos2[i]; - - return static_cast(std::sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])); - } - void InterpreterContext::executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor) { std::shared_ptr action = (ptr.getClass().activate(ptr, actor)); @@ -482,24 +430,6 @@ namespace MWScript return MWBase::Environment::get().getFrameDuration(); } - bool InterpreterContext::isDisabled (const std::string& id) const - { - const MWWorld::Ptr ref = getReferenceImp (id, false); - return !ref.getRefData().isEnabled(); - } - - void InterpreterContext::enable (const std::string& id) - { - MWWorld::Ptr ref = getReferenceImp (id, false); - MWBase::Environment::get().getWorld()->enable (ref); - } - - void InterpreterContext::disable (const std::string& id) - { - MWWorld::Ptr ref = getReferenceImp (id, false); - MWBase::Environment::get().getWorld()->disable (ref); - } - int InterpreterContext::getMemberShort (const std::string& id, const std::string& name, bool global) const { diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 1c465ee3e..7e9f09cdb 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -121,26 +121,11 @@ namespace MWScript virtual std::string getCurrentCellName() const; - virtual bool isScriptRunning (const std::string& name) const; - - virtual void startScript (const std::string& name, const std::string& targetId = ""); - - virtual void stopScript (const std::string& name); - - virtual float getDistance (const std::string& name, const std::string& id = "") const; - ///< @note if \a id is empty, assumes an implicit reference - void executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor); ///< Execute the activation action for this ptr. If ptr is mActivated, mark activation as handled. virtual float getSecondsPassed() const; - virtual bool isDisabled (const std::string& id = "") const; - - virtual void enable (const std::string& id = ""); - - virtual void disable (const std::string& id = ""); - virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const; virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index b7b91cf5c..915b3221c 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -78,6 +78,80 @@ namespace MWScript { namespace Misc { + template + class OpStartScript : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string name = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + MWWorld::Ptr target = R()(runtime, false); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, target); + } + }; + + class OpScriptRunning : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string name = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + runtime.push(MWBase::Environment::get().getScriptManager()->getGlobalScripts().isRunning (name)); + } + }; + + class OpStopScript : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string name = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().removeScript (name); + } + }; + + template + class OpEnable : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + MWBase::Environment::get().getWorld()->enable (ptr); + } + }; + + template + class OpDisable : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + MWBase::Environment::get().getWorld()->disable (ptr); + } + }; + + template + class OpGetDisabled : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push (!ptr.getRefData().isEnabled()); + } + }; + class OpPlayBink : public Interpreter::Opcode0 { public: @@ -1456,6 +1530,16 @@ namespace MWScript void installOpcodes (Interpreter::Interpreter& interpreter) { + interpreter.installSegment5 (Compiler::Misc::opcodeScriptRunning, new OpScriptRunning); + interpreter.installSegment5 (Compiler::Misc::opcodeStartScript, new OpStartScript); + interpreter.installSegment5 (Compiler::Misc::opcodeStartScriptExplicit, new OpStartScript); + interpreter.installSegment5 (Compiler::Misc::opcodeStopScript, new OpStopScript); + interpreter.installSegment5 (Compiler::Misc::opcodeEnable, new OpEnable); + interpreter.installSegment5 (Compiler::Misc::opcodeEnableExplicit, new OpEnable); + interpreter.installSegment5 (Compiler::Misc::opcodeDisable, new OpDisable); + interpreter.installSegment5 (Compiler::Misc::opcodeDisableExplicit, new OpDisable); + interpreter.installSegment5 (Compiler::Misc::opcodeGetDisabled, new OpGetDisabled); + interpreter.installSegment5 (Compiler::Misc::opcodeGetDisabledExplicit, new OpGetDisabled); interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); interpreter.installSegment5 (Compiler::Misc::opcodeOnActivate, new OpOnActivate); interpreter.installSegment5 (Compiler::Misc::opcodeOnActivateExplicit, new OpOnActivate); diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index d28d9c373..3af54b394 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -39,6 +39,49 @@ namespace MWScript } } + template + class OpGetDistance : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string name = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::Ptr from = R()(runtime); + if (from.getContainerStore()) // is the object contained? + { + MWWorld::Ptr container = MWBase::Environment::get().getWorld()->findContainer(from); + + if (!container.isEmpty()) + from = container; + else + throw std::runtime_error("failed to find container ptr"); + } + + const MWWorld::Ptr to = MWBase::Environment::get().getWorld()->getPtr(name, false); + + float distance; + // If the objects are in different worldspaces, return a large value (just like vanilla) + if (!to.isInCell() || !from.isInCell() || to.getCell()->getCell()->getCellId().mWorldspace != from.getCell()->getCell()->getCellId().mWorldspace) + distance = std::numeric_limits::max(); + else + { + double diff[3]; + + const float* const pos1 = to.getRefData().getPosition().pos; + const float* const pos2 = from.getRefData().getPosition().pos; + for (int i=0; i<3; ++i) + diff[i] = pos1[i] - pos2[i]; + + distance = static_cast(std::sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])); + } + + runtime.push(distance); + } + }; + template class OpSetScale : public Interpreter::Opcode0 { @@ -730,6 +773,8 @@ namespace MWScript void installOpcodes (Interpreter::Interpreter& interpreter) { + interpreter.installSegment5(Compiler::Transformation::opcodeGetDistance, new OpGetDistance); + interpreter.installSegment5(Compiler::Transformation::opcodeGetDistanceExplicit, new OpGetDistance); interpreter.installSegment5(Compiler::Transformation::opcodeSetScale,new OpSetScale); interpreter.installSegment5(Compiler::Transformation::opcodeSetScaleExplicit,new OpSetScale); interpreter.installSegment5(Compiler::Transformation::opcodeSetAngle,new OpSetAngle); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index a7d70b4e0..19fb2de89 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -111,7 +111,7 @@ add_component_dir (compiler add_component_dir (interpreter context controlopcodes genericopcodes installopcodes interpreter localopcodes mathopcodes - miscopcodes opcodes runtime scriptopcodes spatialopcodes types defines + miscopcodes opcodes runtime types defines ) add_component_dir (translation diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 20873385b..8e8f063bb 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -372,9 +372,7 @@ namespace Compiler keyword==Scanner::K_elseif || keyword==Scanner::K_while || keyword==Scanner::K_endwhile || keyword==Scanner::K_return || keyword==Scanner::K_messagebox || keyword==Scanner::K_set || - keyword==Scanner::K_to || keyword==Scanner::K_startscript || - keyword==Scanner::K_stopscript || keyword==Scanner::K_enable || - keyword==Scanner::K_disable) + keyword==Scanner::K_to) { return parseName (loc.mLiteral, loc, scanner); } @@ -385,53 +383,6 @@ namespace Compiler { if (mRefOp && mNextOperand) { - if (keyword==Scanner::K_getdisabled) - { - start(); - - mTokenLoc = loc; - - Generator::getDisabled (mCode, mLiterals, mExplicit); - mOperands.push_back ('l'); - mExplicit.clear(); - mRefOp = false; - - std::vector ignore; - parseArguments ("x", scanner, ignore); - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_getdistance) - { - start(); - - mTokenLoc = loc; - parseArguments ("c", scanner); - - Generator::getDistance (mCode, mLiterals, mExplicit); - mOperands.push_back ('f'); - mExplicit.clear(); - mRefOp = false; - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_scriptrunning) - { - start(); - - mTokenLoc = loc; - parseArguments ("c", scanner); - - Generator::scriptRunning (mCode); - mOperands.push_back ('l'); - - mExplicit.clear(); - mRefOp = false; - mNextOperand = false; - return true; - } // check for custom extensions if (const Extensions *extensions = getContext().getExtensions()) @@ -508,32 +459,6 @@ namespace Compiler mNextOperand = false; return true; } - else if (keyword==Scanner::K_scriptrunning) - { - start(); - - mTokenLoc = loc; - parseArguments ("c", scanner); - - Generator::scriptRunning (mCode); - mOperands.push_back ('l'); - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_getdistance) - { - start(); - - mTokenLoc = loc; - parseArguments ("c", scanner); - - Generator::getDistance (mCode, mLiterals, ""); - mOperands.push_back ('f'); - - mNextOperand = false; - return true; - } else if (keyword==Scanner::K_getsecondspassed) { start(); @@ -546,21 +471,6 @@ namespace Compiler mNextOperand = false; return true; } - else if (keyword==Scanner::K_getdisabled) - { - start(); - - mTokenLoc = loc; - - Generator::getDisabled (mCode, mLiterals, ""); - mOperands.push_back ('l'); - - std::vector ignore; - parseArguments ("x", scanner, ignore); - - mNextOperand = false; - return true; - } else { // check for custom extensions diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index f23d2d86e..02a68d891 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -241,6 +241,12 @@ namespace Compiler { void registerExtensions (Extensions& extensions) { + extensions.registerFunction ("scriptrunning", 'l', "c", opcodeScriptRunning); + extensions.registerInstruction ("startscript", "c", opcodeStartScript, opcodeStartScriptExplicit); + extensions.registerInstruction ("stopscript", "c", opcodeStopScript); + extensions.registerInstruction ("enable", "", opcodeEnable, opcodeEnableExplicit); + extensions.registerInstruction ("disable", "", opcodeDisable, opcodeDisableExplicit); + extensions.registerFunction ("getdisabled", 'l', "x", opcodeGetDisabled, opcodeGetDisabledExplicit); extensions.registerFunction ("xbox", 'l', "", opcodeXBox); extensions.registerFunction ("onactivate", 'l', "", opcodeOnActivate, opcodeOnActivateExplicit); extensions.registerInstruction ("activate", "x", opcodeActivate, opcodeActivateExplicit); @@ -533,6 +539,7 @@ namespace Compiler { void registerExtensions (Extensions& extensions) { + extensions.registerFunction("getdistance",'f',"c",opcodeGetDistance,opcodeGetDistanceExplicit); extensions.registerInstruction("setscale","f",opcodeSetScale,opcodeSetScaleExplicit); extensions.registerFunction("getscale",'f',"",opcodeGetScale,opcodeGetScaleExplicit); extensions.registerInstruction("setangle","cf",opcodeSetAngle,opcodeSetAngleExplicit); diff --git a/components/compiler/generator.cpp b/components/compiler/generator.cpp index 7e6437e20..c16df660c 100644 --- a/components/compiler/generator.cpp +++ b/components/compiler/generator.cpp @@ -292,65 +292,10 @@ namespace code.push_back (Compiler::Generator::segment5 (45)); } - void opScriptRunning (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (46)); - } - - void opStartScript (Compiler::Generator::CodeContainer& code, bool targeted) - { - code.push_back (Compiler::Generator::segment5 (targeted ? 71 : 47)); - } - - void opStopScript (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (48)); - } - - void opGetDistance (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (49)); - } - void opGetSecondsPassed (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (50)); } - - void opEnable (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (51)); - } - - void opDisable (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (52)); - } - - void opGetDisabled (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (53)); - } - - void opEnableExplicit (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (54)); - } - - void opDisableExplicit (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (55)); - } - - void opGetDisabledExplicit (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (56)); - } - - void opGetDistanceExplicit (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (57)); - } } namespace Compiler @@ -812,87 +757,9 @@ namespace Compiler opRandom (code); } - void scriptRunning (CodeContainer& code) - { - opScriptRunning (code); - } - - void startScript (CodeContainer& code, Literals& literals, const std::string& id) - { - if (id.empty()) - opStartScript (code, false); - else - { - int index = literals.addString (id); - opPushInt (code, index); - opStartScript (code, true); - } - } - - void stopScript (CodeContainer& code) - { - opStopScript (code); - } - - void getDistance (CodeContainer& code, Literals& literals, const std::string& id) - { - if (id.empty()) - { - opGetDistance (code); - } - else - { - int index = literals.addString (id); - opPushInt (code, index); - opGetDistanceExplicit (code); - } - } - void getSecondsPassed (CodeContainer& code) { opGetSecondsPassed (code); } - - void getDisabled (CodeContainer& code, Literals& literals, const std::string& id) - { - if (id.empty()) - { - opGetDisabled (code); - } - else - { - int index = literals.addString (id); - opPushInt (code, index); - opGetDisabledExplicit (code); - } - } - - void enable (CodeContainer& code, Literals& literals, const std::string& id) - { - if (id.empty()) - { - opEnable (code); - } - else - { - int index = literals.addString (id); - opPushInt (code, index); - opEnableExplicit (code); - } - } - - void disable (CodeContainer& code, Literals& literals, const std::string& id) - { - if (id.empty()) - { - opDisable (code); - } - else - { - int index = literals.addString (id); - opPushInt (code, index); - opDisableExplicit (code); - } - } } } diff --git a/components/compiler/generator.hpp b/components/compiler/generator.hpp index a56d7c1f1..f4386f605 100644 --- a/components/compiler/generator.hpp +++ b/components/compiler/generator.hpp @@ -109,21 +109,7 @@ namespace Compiler void random (CodeContainer& code); - void scriptRunning (CodeContainer& code); - - void startScript (CodeContainer& code, Literals& literals, const std::string& id); - - void stopScript (CodeContainer& code); - - void getDistance (CodeContainer& code, Literals& literals, const std::string& id); - void getSecondsPassed (CodeContainer& code); - - void getDisabled (CodeContainer& code, Literals& literals, const std::string& id); - - void enable (CodeContainer& code, Literals& literals, const std::string& id); - - void disable (CodeContainer& code, Literals& literals, const std::string& id); } } diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 646685b39..eaa833800 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -247,35 +247,6 @@ namespace Compiler if (mState==BeginState || mState==ExplicitState) { - switch (keyword) - { - case Scanner::K_enable: - - Generator::enable (mCode, mLiterals, mExplicit); - mState = PotentialEndState; - return true; - - case Scanner::K_disable: - - Generator::disable (mCode, mLiterals, mExplicit); - mState = PotentialEndState; - return true; - - case Scanner::K_startscript: - - mExprParser.parseArguments ("c", scanner, mCode); - Generator::startScript (mCode, mLiterals, mExplicit); - mState = EndState; - return true; - - case Scanner::K_stopscript: - - mExprParser.parseArguments ("c", scanner, mCode); - Generator::stopScript (mCode); - mState = EndState; - return true; - } - // check for custom extensions if (const Extensions *extensions = getContext().getExtensions()) { @@ -323,21 +294,6 @@ namespace Compiler } } - if (keyword==Scanner::K_getdisabled || keyword==Scanner::K_getdistance) - { - if (mAllowExpression) - { - scanner.putbackKeyword (keyword, loc); - parseExpression (scanner, loc); - } - else - { - getErrorHandler().warning ("Unexpected naked expression", loc); - } - mState = EndState; - return true; - } - if (const Extensions *extensions = getContext().getExtensions()) { char returnType; @@ -416,13 +372,6 @@ namespace Compiler mState = EndState; return true; - case Scanner::K_stopscript: - - mExprParser.parseArguments ("c", scanner, mCode); - Generator::stopScript (mCode); - mState = EndState; - return true; - case Scanner::K_else: getErrorHandler().warning ("Stray else", loc); @@ -487,8 +436,7 @@ namespace Compiler if (mAllowExpression) { if (keyword==Scanner::K_getsquareroot || keyword==Scanner::K_menumode || - keyword==Scanner::K_random || keyword==Scanner::K_scriptrunning || - keyword==Scanner::K_getsecondspassed) + keyword==Scanner::K_random || keyword==Scanner::K_getsecondspassed) { scanner.putbackKeyword (keyword, loc); parseExpression (scanner, loc); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index d3446e688..72e3cea89 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -201,6 +201,16 @@ namespace Compiler namespace Misc { + const int opcodeScriptRunning = 46; + const int opcodeStartScript = 47; + const int opcodeStopScript = 48; + const int opcodeEnable = 51; + const int opcodeDisable = 52; + const int opcodeGetDisabled = 53; + const int opcodeEnableExplicit = 54; + const int opcodeDisableExplicit = 55; + const int opcodeGetDisabledExplicit = 56; + const int opcodeStartScriptExplicit = 71; const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeOnActivateExplicit = 0x2000306; @@ -473,6 +483,8 @@ namespace Compiler namespace Transformation { + const int opcodeGetDistance = 49; + const int opcodeGetDistanceExplicit = 57; const int opcodeSetScale = 0x2000164; const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 83686e4c1..2be679cd2 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -268,10 +268,7 @@ namespace Compiler "getsquareroot", "menumode", "random", - "startscript", "stopscript", "scriptrunning", - "getdistance", "getsecondspassed", - "enable", "disable", "getdisabled", 0 }; diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp index b8d057677..973761898 100644 --- a/components/compiler/scanner.hpp +++ b/components/compiler/scanner.hpp @@ -211,10 +211,7 @@ namespace Compiler K_getsquareroot, K_menumode, K_random, - K_startscript, K_stopscript, K_scriptrunning, - K_getdistance, - K_getsecondspassed, - K_enable, K_disable, K_getdisabled + K_getsecondspassed }; enum special diff --git a/components/compiler/stringparser.cpp b/components/compiler/stringparser.cpp index ad88bb857..a9974297d 100644 --- a/components/compiler/stringparser.cpp +++ b/components/compiler/stringparser.cpp @@ -63,12 +63,9 @@ namespace Compiler keyword==Scanner::K_elseif || keyword==Scanner::K_while || keyword==Scanner::K_endwhile || keyword==Scanner::K_return || keyword==Scanner::K_messagebox || keyword==Scanner::K_set || - keyword==Scanner::K_to || keyword==Scanner::K_startscript || - keyword==Scanner::K_stopscript || keyword==Scanner::K_enable || - keyword==Scanner::K_disable || keyword==Scanner::K_getdisabled || - keyword==Scanner::K_getdistance || keyword==Scanner::K_scriptrunning || - keyword==Scanner::K_getsquareroot || keyword==Scanner::K_menumode || - keyword==Scanner::K_random || keyword==Scanner::K_getsecondspassed) + keyword==Scanner::K_to || keyword==Scanner::K_getsquareroot || + keyword==Scanner::K_menumode || keyword==Scanner::K_random || + keyword==Scanner::K_getsecondspassed) { return parseName (loc.mLiteral, loc, scanner); } diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 88fdae128..30744dcec 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -79,23 +79,8 @@ namespace Interpreter virtual std::string getCurrentCellName() const = 0; - virtual bool isScriptRunning (const std::string& name) const = 0; - - virtual void startScript (const std::string& name, const std::string& targetId = "") = 0; - - virtual void stopScript (const std::string& name) = 0; - - virtual float getDistance (const std::string& name, const std::string& id = "") const - = 0; - virtual float getSecondsPassed() const = 0; - virtual bool isDisabled (const std::string& id = "") const = 0; - - virtual void enable (const std::string& id = "") = 0; - - virtual void disable (const std::string& id = "") = 0; - virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const = 0; virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const = 0; diff --git a/components/interpreter/installopcodes.cpp b/components/interpreter/installopcodes.cpp index 31e911f8b..dfaabe325 100644 --- a/components/interpreter/installopcodes.cpp +++ b/components/interpreter/installopcodes.cpp @@ -8,8 +8,6 @@ #include "mathopcodes.hpp" #include "controlopcodes.hpp" #include "miscopcodes.hpp" -#include "scriptopcodes.hpp" -#include "spatialopcodes.hpp" namespace Interpreter { @@ -100,22 +98,6 @@ namespace Interpreter interpreter.installSegment5 (38, new OpMenuMode); interpreter.installSegment5 (45, new OpRandom); interpreter.installSegment5 (50, new OpGetSecondsPassed); - interpreter.installSegment5 (51, new OpEnable); - interpreter.installSegment5 (52, new OpDisable); - interpreter.installSegment5 (53, new OpGetDisabled); - interpreter.installSegment5 (54, new OpEnableExplicit); - interpreter.installSegment5 (55, new OpDisableExplicit); - interpreter.installSegment5 (56, new OpGetDisabledExplicit); interpreter.installSegment5 (58, new OpReport); - - // script control - interpreter.installSegment5 (46, new OpScriptRunning); - interpreter.installSegment5 (47, new OpStartScript); - interpreter.installSegment5 (48, new OpStopScript); - interpreter.installSegment5 (71, new OpStartScriptExplicit); - - // spacial - interpreter.installSegment5 (49, new OpGetDistance); - interpreter.installSegment5 (57, new OpGetDistanceExplicit); } } diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index a77e0d7d8..e1b1843ef 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -206,78 +206,6 @@ namespace Interpreter } }; - class OpEnable : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - runtime.getContext().enable(); - } - }; - - class OpDisable : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - runtime.getContext().disable(); - } - }; - - class OpGetDisabled : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - runtime.push (runtime.getContext().isDisabled()); - } - }; - - class OpEnableExplicit : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - int index = runtime[0].mInteger; - runtime.pop(); - std::string id = runtime.getStringLiteral (index); - - runtime.getContext().enable (id); - } - }; - - class OpDisableExplicit : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - int index = runtime[0].mInteger; - runtime.pop(); - std::string id = runtime.getStringLiteral (index); - - runtime.getContext().disable (id); - } - }; - - class OpGetDisabledExplicit : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - int index = runtime[0].mInteger; - runtime.pop(); - std::string id = runtime.getStringLiteral (index); - - runtime.push (runtime.getContext().isDisabled (id)); - } - }; - } #endif diff --git a/components/interpreter/scriptopcodes.hpp b/components/interpreter/scriptopcodes.hpp deleted file mode 100644 index c98bcd23e..000000000 --- a/components/interpreter/scriptopcodes.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef INTERPRETER_SCRIPTOPCODES_H_INCLUDED -#define INTERPRETER_SCRIPTOPCODES_H_INCLUDED - -#include "opcodes.hpp" -#include "runtime.hpp" -#include "context.hpp" - -namespace Interpreter -{ - class OpScriptRunning : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - runtime[0].mInteger = runtime.getContext().isScriptRunning (name); - } - }; - - class OpStartScript : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - runtime.getContext().startScript (name); - } - }; - - class OpStartScriptExplicit : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - std::string targetId = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - - runtime.getContext().startScript (name, targetId); - } - }; - - class OpStopScript : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - runtime.getContext().stopScript (name); - } - }; -} - -#endif - diff --git a/components/interpreter/spatialopcodes.hpp b/components/interpreter/spatialopcodes.hpp deleted file mode 100644 index e37df8116..000000000 --- a/components/interpreter/spatialopcodes.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef INTERPRETER_SPATIALOPCODES_H_INCLUDED -#define INTERPRETER_SPATIALOPCODES_H_INCLUDED - -#include "opcodes.hpp" -#include "runtime.hpp" - -namespace Interpreter -{ - class OpGetDistance : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - - Type_Float distance = runtime.getContext().getDistance (name); - - runtime[0].mFloat = distance; - } - }; - - class OpGetDistanceExplicit : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - int index = runtime[0].mInteger; - runtime.pop(); - std::string id = runtime.getStringLiteral (index); - - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - - Type_Float distance = runtime.getContext().getDistance (name, id); - - runtime[0].mFloat = distance; - } - }; -} - -#endif - From 915ffe224169f76355ed0cf547f2ee193e006d4f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 14 May 2020 00:48:28 +0300 Subject: [PATCH 090/227] Handle non-node roots more gracefully (bug #5416) --- CHANGELOG.md | 1 + components/nifbullet/bulletnifloader.cpp | 19 ++++++------- components/nifosg/nifloader.cpp | 36 +++++++++++++----------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b88b5f9d..5ba33d939 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures Bug #5370: Opening an unlocked but trapped door uses the key Bug #5400: Editor: Verifier checks race of non-skin bodyparts + Bug #5416: Junk non-node records before the root node are not handled gracefully Feature #5362: Show the soul gems' trapped soul in count dialog 0.46.0 diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index dafee1b49..b1a970f91 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -132,20 +132,17 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) mStaticMesh.reset(); mAvoidStaticMesh.reset(); - if (nif.numRoots() < 1) + Nif::Node* node; + for (size_t i = 0; i < nif.numRoots(); ++i) { - warn("Found no root nodes in NIF."); - return mShape; + Nif::Record* r = nif.getRoot(i); + assert(r != nullptr); + if ((node = dynamic_cast(r))) + break; } - - Nif::Record *r = nif.getRoot(0); - assert(r != nullptr); - - Nif::Node *node = dynamic_cast(r); - if (node == nullptr) + if (!node) { - warn("First root in file was not a node, but a " + - r->recName + ". Skipping file."); + warn("Found no root nodes in NIF."); return mShape; } diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 650d8db94..e115ad104 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -247,22 +247,23 @@ namespace NifOsg static void loadKf(Nif::NIFFilePtr nif, KeyframeHolder& target) { - if(nif->numRoots() < 1) + const Nif::NiSequenceStreamHelper *seq; + for (size_t i = 0; i < nif->numRoots(); ++i) { - nif->warn("Found no root nodes"); - return; + const Nif::Record *r = nif->getRoot(i); + assert(r != nullptr); + if (r->recType == Nif::RC_NiSequenceStreamHelper) + { + seq = static_cast(r); + break; + } } - const Nif::Record *r = nif->getRoot(0); - assert(r != nullptr); - - if(r->recType != Nif::RC_NiSequenceStreamHelper) + if (!seq) { - nif->warn("First root was not a NiSequenceStreamHelper, but a "+ - r->recName+"."); + nif->warn("Found no NiSequenceStreamHelper root record"); return; } - const Nif::NiSequenceStreamHelper *seq = static_cast(r); Nif::ExtraPtr extra = seq->extra; if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData) @@ -303,15 +304,16 @@ namespace NifOsg osg::ref_ptr load(Nif::NIFFilePtr nif, Resource::ImageManager* imageManager) { - if (nif->numRoots() < 1) + const Nif::Node* nifNode; + for (size_t i = 0; i < nif->numRoots(); ++i) + { + const Nif::Record* r = nif->getRoot(i); + if ((nifNode = dynamic_cast(r))) + break; + } + if (!nifNode) nif->fail("Found no root nodes"); - const Nif::Record* r = nif->getRoot(0); - - const Nif::Node* nifNode = dynamic_cast(r); - if (nifNode == nullptr) - nif->fail("First root was not a node, but a " + r->recName); - osg::ref_ptr textkeys (new TextKeyMapHolder); osg::ref_ptr created = handleNode(nifNode, nullptr, imageManager, std::vector(), 0, false, false, false, &textkeys->mTextKeys); From 30558c2434a75ed5e57473b85b1728b2f3e63b29 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 14 May 2020 10:00:33 +0300 Subject: [PATCH 091/227] Try to resolve CI concerns --- .../nifloader/testbulletnifloader.cpp | 13 ------------- components/nifbullet/bulletnifloader.cpp | 5 +++-- components/nifosg/nifloader.cpp | 10 ++++++---- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 36f251246..7c2797236 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -349,19 +349,6 @@ namespace EXPECT_EQ(*result, expected); } - TEST_F(TestBulletNifLoader, for_root_not_nif_node_should_return_default) - { - StrictMock record; - - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&record)); - const auto result = mLoader.load(mNifFile); - - Resource::BulletShape expected; - - EXPECT_EQ(*result, expected); - } - TEST_F(TestBulletNifLoader, for_default_root_nif_node_should_return_default) { EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index b1a970f91..15834ffad 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -132,8 +132,9 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) mStaticMesh.reset(); mAvoidStaticMesh.reset(); - Nif::Node* node; - for (size_t i = 0; i < nif.numRoots(); ++i) + Nif::Node* node = nullptr; + const size_t numRoots = nif.numRoots(); + for (size_t i = 0; i < numRoots; ++i) { Nif::Record* r = nif.getRoot(i); assert(r != nullptr); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index e115ad104..8d08ebef1 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -247,8 +247,9 @@ namespace NifOsg static void loadKf(Nif::NIFFilePtr nif, KeyframeHolder& target) { - const Nif::NiSequenceStreamHelper *seq; - for (size_t i = 0; i < nif->numRoots(); ++i) + const Nif::NiSequenceStreamHelper *seq = nullptr; + const size_t numRoots = nif->numRoots(); + for (size_t i = 0; i < numRoots; ++i) { const Nif::Record *r = nif->getRoot(i); assert(r != nullptr); @@ -304,8 +305,9 @@ namespace NifOsg osg::ref_ptr load(Nif::NIFFilePtr nif, Resource::ImageManager* imageManager) { - const Nif::Node* nifNode; - for (size_t i = 0; i < nif->numRoots(); ++i) + const Nif::Node* nifNode = nullptr; + const size_t numRoots = nif->numRoots(); + for (size_t i = 0; i < numRoots; ++i) { const Nif::Record* r = nif->getRoot(i); if ((nifNode = dynamic_cast(r))) From b665fed8f29c28767fc4e768447500c768ea8614 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 14 May 2020 10:52:27 +0300 Subject: [PATCH 092/227] Introduce NiGeometry abstraction --- components/nif/data.cpp | 8 ++++---- components/nif/data.hpp | 8 ++++---- components/nif/node.hpp | 11 +++++++---- components/nifosg/nifloader.cpp | 13 ++----------- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 2c5568397..8ae49476b 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -31,7 +31,7 @@ void NiSkinInstance::post(NIFFile *nif) } } -void ShapeData::read(NIFStream *nif) +void NiGeometryData::read(NIFStream *nif) { int verts = nif->getUShort(); @@ -69,7 +69,7 @@ void ShapeData::read(NIFStream *nif) void NiTriShapeData::read(NIFStream *nif) { - ShapeData::read(nif); + NiGeometryData::read(nif); /*int tris =*/ nif->getUShort(); @@ -92,7 +92,7 @@ void NiTriShapeData::read(NIFStream *nif) void NiTriStripsData::read(NIFStream *nif) { - ShapeData::read(nif); + NiGeometryData::read(nif); // Every strip with n points defines n-2 triangles, so this should be unnecessary. /*int tris =*/ nif->getUShort(); @@ -112,7 +112,7 @@ void NiTriStripsData::read(NIFStream *nif) void NiAutoNormalParticlesData::read(NIFStream *nif) { - ShapeData::read(nif); + NiGeometryData::read(nif); // Should always match the number of vertices numParticles = nif->getUShort(); diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 39901b584..33818810a 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -32,7 +32,7 @@ namespace Nif { // Common ancestor for several data classes -class ShapeData : public Record +class NiGeometryData : public Record { public: std::vector vertices, normals; @@ -44,7 +44,7 @@ public: void read(NIFStream *nif); }; -class NiTriShapeData : public ShapeData +class NiTriShapeData : public NiGeometryData { public: // Triangles, three vertex indices per triangle @@ -53,7 +53,7 @@ public: void read(NIFStream *nif); }; -class NiTriStripsData : public ShapeData +class NiTriStripsData : public NiGeometryData { public: // Triangle strips, series of vertex indices. @@ -62,7 +62,7 @@ public: void read(NIFStream *nif); }; -class NiAutoNormalParticlesData : public ShapeData +class NiAutoNormalParticlesData : public NiGeometryData { public: int numParticles; diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 71e66c53a..06a1a3b76 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -128,7 +128,12 @@ struct NiNode : Node } }; -struct NiTriShape : Node +struct NiGeometry : Node +{ + NiSkinInstancePtr skin; +}; + +struct NiTriShape : NiGeometry { /* Possible flags: 0x40 - mesh has no vertex normals ? @@ -138,7 +143,6 @@ struct NiTriShape : Node */ NiTriShapeDataPtr data; - NiSkinInstancePtr skin; void read(NIFStream *nif) { @@ -157,10 +161,9 @@ struct NiTriShape : Node } }; -struct NiTriStrips : Node +struct NiTriStrips : NiGeometry { NiTriStripsDataPtr data; - NiSkinInstancePtr skin; void read(NIFStream *nif) { diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 8d08ebef1..521764274 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -649,11 +649,7 @@ namespace NifOsg const bool isMarker = hasMarkers && !nodeName.compare(0, markerName.size(), markerName); if (!isMarker && nodeName.compare(0, shadowName.size(), shadowName) && nodeName.compare(0, shadowName2.size(), shadowName2)) { - Nif::NiSkinInstancePtr skin; - if (nifNode->recType == Nif::RC_NiTriShape) - skin = static_cast(nifNode)->skin; - else // if (nifNode->recType == Nif::RC_NiTriStrips) - skin = static_cast(nifNode)->skin; + Nif::NiSkinInstancePtr skin = static_cast(nifNode)->skin; if (skin.empty()) handleTriShape(nifNode, node, composite, boundTextures, animflags); @@ -1294,12 +1290,7 @@ namespace NifOsg // Assign bone weights osg::ref_ptr map (new SceneUtil::RigGeometry::InfluenceMap); - Nif::NiSkinInstancePtr skinPtr; - if (nifNode->recType == Nif::RC_NiTriShape) - skinPtr = static_cast(nifNode)->skin; - else - skinPtr = static_cast(nifNode)->skin; - const Nif::NiSkinInstance *skin = skinPtr.getPtr(); + const Nif::NiSkinInstance *skin = static_cast(nifNode)->skin.getPtr(); const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NodeList &bones = skin->bones; for(size_t i = 0;i < bones.length();i++) From e6ca95174a36d21a1ebfa3babe89f2e151ccc142 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 14 May 2020 19:45:35 +0400 Subject: [PATCH 093/227] Optimize characters data in savegame --- components/esm/creaturestats.cpp | 146 +++++++++++++----------------- components/esm/creaturestats.hpp | 16 ++++ components/esm/inventorystate.cpp | 59 ++++++++++-- components/esm/npcstats.cpp | 21 ++++- components/esm/savedgame.cpp | 2 +- 5 files changed, 146 insertions(+), 98 deletions(-) diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index d337b1434..2270bb6dd 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -17,53 +17,60 @@ void ESM::CreatureStats::load (ESMReader &esm) mTradeTime.mHour = 0; esm.getHNOT (mTradeTime, "TIME"); + int flags = 0; mDead = false; - esm.getHNOT (mDead, "DEAD"); - mDeathAnimationFinished = false; - esm.getHNOT (mDeathAnimationFinished, "DFNT"); - - if (esm.getFormat() < 3 && mDead) - mDeathAnimationFinished = true; - mDied = false; - esm.getHNOT (mDied, "DIED"); - mMurdered = false; - esm.getHNOT (mMurdered, "MURD"); - - if (esm.isNextSub("FRHT")) - esm.skipHSub(); // Friendly hits, no longer used - mTalkedTo = false; - esm.getHNOT (mTalkedTo, "TALK"); - mAlarmed = false; - esm.getHNOT (mAlarmed, "ALRM"); - mAttacked = false; - esm.getHNOT (mAttacked, "ATKD"); - - if (esm.isNextSub("HOST")) - esm.skipHSub(); // Hostile, no longer used - - if (esm.isNextSub("ATCK")) - esm.skipHSub(); // attackingOrSpell, no longer used - mKnockdown = false; - esm.getHNOT (mKnockdown, "KNCK"); - mKnockdownOneFrame = false; - esm.getHNOT (mKnockdownOneFrame, "KNC1"); - mKnockdownOverOneFrame = false; - esm.getHNOT (mKnockdownOverOneFrame, "KNCO"); - mHitRecovery = false; - esm.getHNOT (mHitRecovery, "HITR"); - mBlock = false; - esm.getHNOT (mBlock, "BLCK"); + mRecalcDynamicStats = false; + if (esm.getFormat() < 8) + { + esm.getHNOT (mDead, "DEAD"); + esm.getHNOT (mDeathAnimationFinished, "DFNT"); + if (esm.getFormat() < 3 && mDead) + mDeathAnimationFinished = true; + esm.getHNOT (mDied, "DIED"); + esm.getHNOT (mMurdered, "MURD"); + if (esm.isNextSub("FRHT")) + esm.skipHSub(); // Friendly hits, no longer used + esm.getHNOT (mTalkedTo, "TALK"); + esm.getHNOT (mAlarmed, "ALRM"); + esm.getHNOT (mAttacked, "ATKD"); + if (esm.isNextSub("HOST")) + esm.skipHSub(); // Hostile, no longer used + if (esm.isNextSub("ATCK")) + esm.skipHSub(); // attackingOrSpell, no longer used + esm.getHNOT (mKnockdown, "KNCK"); + esm.getHNOT (mKnockdownOneFrame, "KNC1"); + esm.getHNOT (mKnockdownOverOneFrame, "KNCO"); + esm.getHNOT (mHitRecovery, "HITR"); + esm.getHNOT (mBlock, "BLCK"); + } + else + { + esm.getHNOT(flags, "AFLG"); + mDead = flags & Dead; + mDeathAnimationFinished = flags & DeathAnimationFinished; + mDied = flags & Died; + mMurdered = flags & Murdered; + mTalkedTo = flags & TalkedTo; + mAlarmed = flags & Alarmed; + mAttacked = flags & Attacked; + mKnockdown = flags & Knockdown; + mKnockdownOneFrame = flags & KnockdownOneFrame; + mKnockdownOverOneFrame = flags & KnockdownOverOneFrame; + mHitRecovery = flags & HitRecovery; + mBlock = flags & Block; + mRecalcDynamicStats = flags & RecalcDynamicStats; + } mMovementFlags = 0; esm.getHNOT (mMovementFlags, "MOVE"); @@ -78,8 +85,8 @@ void ESM::CreatureStats::load (ESMReader &esm) mLastHitAttemptObject = esm.getHNOString ("LHAT"); - mRecalcDynamicStats = false; - esm.getHNOT (mRecalcDynamicStats, "CALC"); + if (esm.getFormat() < 8) + esm.getHNOT (mRecalcDynamicStats, "CALC"); mDrawState = 0; esm.getHNOT (mDrawState, "DRAW"); @@ -90,9 +97,6 @@ void ESM::CreatureStats::load (ESMReader &esm) mActorId = -1; esm.getHNOT (mActorId, "ACID"); - //mHitAttemptActorId = -1; - //esm.getHNOT(mHitAttemptActorId, "HAID"); - mDeathAnimation = -1; esm.getHNOT (mDeathAnimation, "DANM"); @@ -134,7 +138,6 @@ void ESM::CreatureStats::load (ESMReader &esm) void ESM::CreatureStats::save (ESMWriter &esm) const { - for (int i=0; i<8; ++i) mAttributes[i].save (esm); @@ -147,41 +150,23 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mTradeTime.mDay != 0 || mTradeTime.mHour != 0) esm.writeHNT ("TIME", mTradeTime); - if (mDead) - esm.writeHNT ("DEAD", mDead); - - if (mDeathAnimationFinished) - esm.writeHNT ("DFNT", mDeathAnimationFinished); - - if (mDied) - esm.writeHNT ("DIED", mDied); - - if (mMurdered) - esm.writeHNT ("MURD", mMurdered); - - if (mTalkedTo) - esm.writeHNT ("TALK", mTalkedTo); - - if (mAlarmed) - esm.writeHNT ("ALRM", mAlarmed); - - if (mAttacked) - esm.writeHNT ("ATKD", mAttacked); - - if (mKnockdown) - esm.writeHNT ("KNCK", mKnockdown); - - if (mKnockdownOneFrame) - esm.writeHNT ("KNC1", mKnockdownOneFrame); - - if (mKnockdownOverOneFrame) - esm.writeHNT ("KNCO", mKnockdownOverOneFrame); - - if (mHitRecovery) - esm.writeHNT ("HITR", mHitRecovery); - - if (mBlock) - esm.writeHNT ("BLCK", mBlock); + int flags = 0; + if (mDead) flags |= Dead; + if (mDeathAnimationFinished) flags |= DeathAnimationFinished; + if (mDied) flags |= Died; + if (mMurdered) flags |= Murdered; + if (mTalkedTo) flags |= TalkedTo; + if (mAlarmed) flags |= Alarmed; + if (mAttacked) flags |= Attacked; + if (mKnockdown) flags |= Knockdown; + if (mKnockdownOneFrame) flags |= KnockdownOneFrame; + if (mKnockdownOverOneFrame) flags |= KnockdownOverOneFrame; + if (mHitRecovery) flags |= HitRecovery; + if (mBlock) flags |= Block; + if (mRecalcDynamicStats) flags |= RecalcDynamicStats; + + if (flags) + esm.writeHNT ("AFLG", flags); if (mMovementFlags) esm.writeHNT ("MOVE", mMovementFlags); @@ -195,9 +180,6 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (!mLastHitAttemptObject.empty()) esm.writeHNString ("LHAT", mLastHitAttemptObject); - if (mRecalcDynamicStats) - esm.writeHNT ("CALC", mRecalcDynamicStats); - if (mDrawState) esm.writeHNT ("DRAW", mDrawState); @@ -207,13 +189,10 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mActorId != -1) esm.writeHNT ("ACID", mActorId); - //if (mHitAttemptActorId != -1) - // esm.writeHNT("HAID", mHitAttemptActorId); - if (mDeathAnimation != -1) esm.writeHNT ("DANM", mDeathAnimation); - if (mTimeOfDeath.mHour != 0 && mTimeOfDeath.mDay != 0) + if (mTimeOfDeath.mHour != 0 || mTimeOfDeath.mDay != 0) esm.writeHNT ("DTIM", mTimeOfDeath); mSpells.save(esm); @@ -247,7 +226,6 @@ void ESM::CreatureStats::blank() mTradeTime.mDay = 0; mGoldPool = 0; mActorId = -1; - //mHitAttemptActorId = -1; mHasAiSettings = false; mDead = false; mDeathAnimationFinished = false; diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 7e7e5dac3..8c69553a3 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -40,6 +40,22 @@ namespace ESM int mActorId; //int mHitAttemptActorId; + enum Flags + { + Dead = 0x0001, + DeathAnimationFinished = 0x0002, + Died = 0x0004, + Murdered = 0x0008, + TalkedTo = 0x0010, + Alarmed = 0x0020, + Attacked = 0x0040, + Knockdown = 0x0080, + KnockdownOneFrame = 0x0100, + KnockdownOverOneFrame = 0x0200, + HitRecovery = 0x0400, + Block = 0x0800, + RecalcDynamicStats = 0x1000 + }; bool mDead; bool mDeathAnimationFinished; bool mDied; diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index 73db72b00..fe54762c5 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -5,6 +5,7 @@ void ESM::InventoryState::load (ESMReader &esm) { + // obsolete int index = 0; while (esm.isNextSub ("IOBJ")) { @@ -31,6 +32,22 @@ void ESM::InventoryState::load (ESMReader &esm) ++index; } + + int itemsCount = 0; + esm.getHNOT(itemsCount, "ICNT"); + for (int i = 0; i < itemsCount; i++) + { + ObjectState state; + + state.mRef.loadId(esm, true); + state.load (esm); + + if (state.mCount == 0) + continue; + + mItems.push_back (state); + } + //Next item is Levelled item while (esm.isNextSub("LEVM")) { @@ -72,18 +89,35 @@ void ESM::InventoryState::load (ESMReader &esm) mEquipmentSlots[equipIndex] = slot; } + if (esm.isNextSub("EQIP")) + { + esm.getSubHeader(); + int slotsCount = 0; + esm.getT(slotsCount); + for (int i = 0; i < slotsCount; i++) + { + int equipIndex; + esm.getT(equipIndex); + int slot; + esm.getT(slot); + mEquipmentSlots[equipIndex] = slot; + } + } + mSelectedEnchantItem = -1; esm.getHNOT(mSelectedEnchantItem, "SELE"); } void ESM::InventoryState::save (ESMWriter &esm) const { - for (std::vector::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter) + int itemsCount = static_cast(mItems.size()); + if (itemsCount > 0) { - int unused = 0; - esm.writeHNT ("IOBJ", unused); - - iter->save (esm, true); + esm.writeHNT ("ICNT", itemsCount); + for (const ObjectState& state : mItems) + { + state.save (esm, true); + } } for (std::map, int>::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) @@ -105,12 +139,17 @@ void ESM::InventoryState::save (ESMWriter &esm) const } } - for (std::map::const_iterator it = mEquipmentSlots.begin(); it != mEquipmentSlots.end(); ++it) + int slotsCount = static_cast(mEquipmentSlots.size()); + if (slotsCount > 0) { - esm.startSubRecord("EQUI"); - esm.writeT(it->first); - esm.writeT(it->second); - esm.endRecord("EQUI"); + esm.startSubRecord("EQIP"); + esm.writeT(slotsCount); + for (std::map::const_iterator it = mEquipmentSlots.begin(); it != mEquipmentSlots.end(); ++it) + { + esm.writeT(it->first); + esm.writeT(it->second); + } + esm.endRecord("EQIP"); } if (mSelectedEnchantItem != -1) diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index a12879109..c5fa2a09e 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -35,7 +35,7 @@ void ESM::NpcStats::load (ESMReader &esm) mSkills[i].load (esm); mWerewolfDeprecatedData = false; - if (esm.peekNextSub("STBA")) + if (esm.getFormat() < 8 && esm.peekNextSub("STBA")) { // we have deprecated werewolf skills, stored interleaved // Load into one big vector, then remove every 2nd value @@ -95,7 +95,9 @@ void ESM::NpcStats::load (ESMReader &esm) mLevelProgress = 0; esm.getHNOT (mLevelProgress, "LPRO"); - esm.getHNT (mSkillIncrease, "INCR"); + for (int i = 0; i < 8; ++i) + mSkillIncrease[i] = 0; + esm.getHNOT (mSkillIncrease, "INCR"); for (int i=0; i<3; ++i) mSpecIncreases[i] = 0; @@ -160,8 +162,21 @@ void ESM::NpcStats::save (ESMWriter &esm) const if (mLevelProgress) esm.writeHNT ("LPRO", mLevelProgress); - esm.writeHNT ("INCR", mSkillIncrease); + bool saveSkillIncreases = false; + for (int i = 0; i < 8; ++i) + { + if (mSkillIncrease[i] != 0) + { + saveSkillIncreases = true; + break; + } + } + if (saveSkillIncreases) + esm.writeHNT ("INCR", mSkillIncrease); + if (mSpecIncreases[0] != 0 || + mSpecIncreases[1] != 0 || + mSpecIncreases[2] != 0) esm.writeHNT ("SPEC", mSpecIncreases); for (std::vector::const_iterator iter (mUsedIds.begin()); iter!=mUsedIds.end(); diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index e88c7cd8b..f2ebc7bf0 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 7; +int ESM::SavedGame::sCurrentFormat = 8; void ESM::SavedGame::load (ESMReader &esm) { From 72e5043eda98e95cdce973e027e27f69829cac2f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 14 May 2020 23:43:01 +0300 Subject: [PATCH 094/227] CopyRigVisitor fixes Make sure it copies all relevant drawable parent nodes (e.g. including the node with the environment map effect) Make sure it doesn't copy nodes multiple times --- CHANGELOG.md | 1 + components/sceneutil/attach.cpp | 35 ++++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ba33d939..434b45fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures Bug #5370: Opening an unlocked but trapped door uses the key Bug #5400: Editor: Verifier checks race of non-skin bodyparts + Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work Bug #5416: Junk non-node records before the root node are not handled gracefully Feature #5362: Show the soul gems' trapped soul in count dialog diff --git a/components/sceneutil/attach.cpp b/components/sceneutil/attach.cpp index 5126a3776..b5e5d3844 100644 --- a/components/sceneutil/attach.cpp +++ b/components/sceneutil/attach.cpp @@ -45,23 +45,26 @@ namespace SceneUtil virtual void apply(osg::Drawable& drawable) { - std::string lowerName = Misc::StringUtils::lowerCase(drawable.getName()); - if ((lowerName.size() >= mFilter.size() && lowerName.compare(0, mFilter.size(), mFilter) == 0) - || (lowerName.size() >= mFilter2.size() && lowerName.compare(0, mFilter2.size(), mFilter2) == 0)) + if (!filterMatches(drawable.getName())) + return; + + osg::Node* node = &drawable; + if (!node) + return; + while (node->getNumParents()) { - osg::Node* node = &drawable; - while (node && node->getNumParents() && !node->getStateSet()) - node = node->getParent(0); - if (node) - mToCopy.push_back(node); + osg::Group* parent = node->getParent(0); + if (!parent || !filterMatches(parent->getName())) + break; + node = parent; } + mToCopy.emplace(node); } void doCopy() { - for (std::vector >::iterator it = mToCopy.begin(); it != mToCopy.end(); ++it) + for (const osg::ref_ptr& node : mToCopy) { - osg::ref_ptr node = *it; if (node->getNumParents() > 1) Log(Debug::Error) << "Error CopyRigVisitor: node has multiple parents"; while (node->getNumParents()) @@ -73,8 +76,16 @@ namespace SceneUtil } private: - typedef std::vector > NodeVector; - NodeVector mToCopy; + + bool filterMatches(const std::string& name) const + { + std::string lowerName = Misc::StringUtils::lowerCase(name); + return (lowerName.size() >= mFilter.size() && lowerName.compare(0, mFilter.size(), mFilter) == 0) + || (lowerName.size() >= mFilter2.size() && lowerName.compare(0, mFilter2.size(), mFilter2) == 0); + } + + using NodeSet = std::set>; + NodeSet mToCopy; osg::ref_ptr mParent; std::string mFilter; From 78b1bbe1305fed99e0f64a9b8185268c484dd251 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 15 May 2020 10:34:49 +0300 Subject: [PATCH 095/227] Remove unnecessary null check --- components/sceneutil/attach.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/sceneutil/attach.cpp b/components/sceneutil/attach.cpp index b5e5d3844..c438e705d 100644 --- a/components/sceneutil/attach.cpp +++ b/components/sceneutil/attach.cpp @@ -49,8 +49,6 @@ namespace SceneUtil return; osg::Node* node = &drawable; - if (!node) - return; while (node->getNumParents()) { osg::Group* parent = node->getParent(0); From e827d9c04f3646cc4eaf262985a2886b4a98164e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 15 May 2020 12:38:22 +0400 Subject: [PATCH 096/227] Disable physics profiler, if Bullet was compiled without profiling support --- apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 21e84613c..1d2dc185b 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -15,6 +15,9 @@ #include #include +// For BT_NO_PROFILE +#include + #include #include @@ -2202,7 +2205,9 @@ namespace MWGui void WindowManager::toggleDebugWindow() { +#ifndef BT_NO_PROFILE mDebugWindow->setVisible(!mDebugWindow->isVisible()); +#endif } void WindowManager::cycleSpell(bool next) From add42830d9e482bb22466a284cacf8909f0f7e29 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 15 May 2020 12:39:04 +0400 Subject: [PATCH 097/227] Add a flag to use double-precision functions from Bullet --- CMakeLists.txt | 2 +- apps/openmw/CMakeLists.txt | 4 ++++ components/CMakeLists.txt | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba73908ac..4cc9ccd55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ option(BUILD_NIFTEST "Build nif file tester" ON) option(BUILD_DOCS "Build documentation." OFF ) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF) +option(BULLET_USE_DOUBLES "Use double precision for Bullet" OFF) if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD) set(USE_QT FALSE) @@ -904,4 +905,3 @@ if (DOXYGEN_FOUND) WORKING_DIRECTORY ${OpenMW_BINARY_DIR} COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM) endif () - diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 34824ea33..2107b74fe 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -15,6 +15,10 @@ set(GAME_HEADER engine.hpp ) +if (BULLET_USE_DOUBLES) + add_definitions(-DBT_USE_DOUBLE_PRECISION) +endif() + source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index a7d70b4e0..859c79920 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -285,3 +285,7 @@ endif() # Make the variable accessible for other subdirectories set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) + +if (BULLET_USE_DOUBLES) + add_definitions(-DBT_USE_DOUBLE_PRECISION) +endif() From 8265ebc4846059f4fa4a85234ff4e73d73584797 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 15 May 2020 22:24:48 +0300 Subject: [PATCH 098/227] Fix spell school calculation --- apps/openmw/mwmechanics/spellutil.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index cce07f9e3..def3bbbc8 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -101,6 +101,9 @@ namespace MWMechanics float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap, bool checkMagicka) { + // NB: Base chance is calculated here because the effective school pointer must be filled + float baseChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool); + bool godmode = actor == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); CreatureStats& stats = actor.getClass().getCreatureStats(actor); @@ -124,7 +127,7 @@ namespace MWMechanics return 100; float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude(); - float castChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool) + castBonus; + float castChance = baseChance + castBonus; castChance *= stats.getFatigueTerm(); return std::max(0.f, cap ? std::min(100.f, castChance) : castChance); From 3ebbe14a62474acc421f8081d884318968a95e36 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 18 Sep 2019 10:21:25 +0400 Subject: [PATCH 099/227] Avoid zero division --- apps/openmw/mwmechanics/actors.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9cfada560..3bbd8cf29 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -774,6 +774,9 @@ namespace MWMechanics if (visitor.mRemainingTime > 0) { double timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + if(timeScale == 0.0) + timeScale = 1; + restoreHours = std::max(0.0, hours - visitor.mRemainingTime * timeScale / 3600.f); } else if (visitor.mRemainingTime == -1) @@ -1935,7 +1938,11 @@ namespace MWMechanics void Actors::rest(double hours, bool sleep) { - float duration = hours * 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + float duration = hours * 3600.f; + float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + if (timeScale != 0.f) + duration /= timeScale; + const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); From b5833f3c59e3dc19481ff7b2f3d4a7c3ca17fed4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 18 Sep 2019 09:43:32 +0400 Subject: [PATCH 100/227] Use real time to update spell effects instead of game timestamps (bug #5165) --- CHANGELOG.md | 3 +- apps/openmw/mwmechanics/activespells.cpp | 51 ++++++------------------ apps/openmw/mwmechanics/activespells.hpp | 5 +-- apps/openmw/mwmechanics/actors.cpp | 2 + apps/openmw/mwmechanics/spellcasting.cpp | 3 ++ components/esm/activespells.cpp | 14 ++++++- components/esm/activespells.hpp | 2 +- components/esm/savedgame.cpp | 2 +- 8 files changed, 36 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 434b45fb3..138ef1707 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Bug #3676: NiParticleColorModifier isn't applied properly Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #5108: Savegame bloating due to inefficient fog textures format + Bug #5165: Active spells should use real time intead of timestamps Bug #5358: ForceGreeting always resets the dialogue window completely Bug #5363: Enchantment autocalc not always 0/1 Bug #5364: Script fails/stops if trying to startscript an unknown script @@ -188,8 +189,8 @@ Bug #5158: Objects without a name don't fallback to their ID Bug #5159: NiMaterialColorController can only control the diffuse color Bug #5161: Creature companions can't be activated when they are knocked down - Bug #5164: Faction owned items handling is incorrect Bug #5163: UserData is not copied during node cloning + Bug #5164: Faction owned items handling is incorrect Bug #5166: Scripts still should be executed after player's death Bug #5167: Player can select and cast spells before magic menu is enabled Bug #5168: Force1stPerson and Force3rdPerson commands are not really force view change diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 57b009689..4b4b40607 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -12,14 +12,12 @@ namespace MWMechanics { - void ActiveSpells::update() const + void ActiveSpells::update(float duration) const { bool rebuild = false; - MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp(); - // Erase no longer active spells and effects - if (mLastUpdate!=now) + if (duration > 0) { TContainer::iterator iter (mSpells.begin()); while (iter!=mSpells.end()) @@ -34,21 +32,20 @@ namespace MWMechanics std::vector& effects = iter->second.mEffects; for (std::vector::iterator effectIt = effects.begin(); effectIt != effects.end();) { - MWWorld::TimeStamp start = iter->second.mTimeStamp; - MWWorld::TimeStamp end = start + static_cast(effectIt->mDuration)*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); - if (end <= now) + if (effectIt->mTimeLeft <= 0) { effectIt = effects.erase(effectIt); rebuild = true; } else + { + effectIt->mTimeLeft -= duration; ++effectIt; + } } ++iter; } } - - mLastUpdate = now; } if (mSpellsChanged) @@ -63,24 +60,15 @@ namespace MWMechanics void ActiveSpells::rebuildEffects() const { - MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp(); - mEffects = MagicEffects(); for (TIterator iter (begin()); iter!=end(); ++iter) { - const MWWorld::TimeStamp& start = iter->second.mTimeStamp; - const std::vector& effects = iter->second.mEffects; for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) { - double duration = effectIt->mDuration; - MWWorld::TimeStamp end = start; - end += duration * - MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); - - if (end>now) + if (effectIt->mTimeLeft > 0) mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude)); } } @@ -88,12 +76,11 @@ namespace MWMechanics ActiveSpells::ActiveSpells() : mSpellsChanged (false) - , mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp()) {} const MagicEffects& ActiveSpells::getMagicEffects() const { - update(); + update(0.f); return mEffects; } @@ -116,19 +103,14 @@ namespace MWMechanics for (std::vector::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) { - if (iter->mDuration > duration) - duration = iter->mDuration; + if (iter->mTimeLeft > duration) + duration = iter->mTimeLeft; } - double scaledDuration = duration * - MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); - - double usedUp = MWBase::Environment::get().getWorld()->getTimeStamp() - iterator->second.mTimeStamp; - - if (usedUp>=scaledDuration) + if (duration < 0) return 0; - return scaledDuration-usedUp; + return duration; } bool ActiveSpells::isSpellActive(const std::string& id) const @@ -152,7 +134,6 @@ namespace MWMechanics TContainer::iterator it(mSpells.find(id)); ActiveSpellParams params; - params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp(); params.mEffects = effects; params.mDisplayName = displayName; params.mCasterActorId = casterActorId; @@ -211,19 +192,15 @@ namespace MWMechanics { for (TContainer::const_iterator it = begin(); it != end(); ++it) { - float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); - for (std::vector::const_iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end(); ++effectIt) { std::string name = it->second.mDisplayName; - float remainingTime = effectIt->mDuration + - static_cast(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale; float magnitude = effectIt->mMagnitude; if (magnitude) - visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration); + visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, effectIt->mTimeLeft, effectIt->mDuration); } } } @@ -316,7 +293,6 @@ namespace MWMechanics params.mEffects = it->second.mEffects; params.mCasterActorId = it->second.mCasterActorId; params.mDisplayName = it->second.mDisplayName; - params.mTimeStamp = it->second.mTimeStamp.toEsm(); state.mSpells.insert (std::make_pair(it->first, params)); } @@ -331,7 +307,6 @@ namespace MWMechanics params.mEffects = it->second.mEffects; params.mCasterActorId = it->second.mCasterActorId; params.mDisplayName = it->second.mDisplayName; - params.mTimeStamp = MWWorld::TimeStamp(it->second.mTimeStamp); mSpells.insert (std::make_pair(it->first, params)); mSpellsChanged = true; diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index a19c8a51d..ddfa56ecf 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -44,15 +44,14 @@ namespace MWMechanics TIterator end() const; + void update(float duration) const; + private: mutable TContainer mSpells; mutable MagicEffects mEffects; mutable bool mSpellsChanged; - mutable MWWorld::TimeStamp mLastUpdate; - void update() const; - void rebuildEffects() const; /// Add any effects that are in "from" and not in "addTo" to "addTo" diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 3bbd8cf29..223d9fc34 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1622,6 +1622,8 @@ namespace MWMechanics player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); } + iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); + // For dead actors we need to remove looping spell particles if (iter->first.getClass().getCreatureStats(iter->first).isDead()) ctrl->updateContinuousVfx(); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 044a4338e..769934986 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -183,6 +183,7 @@ namespace MWMechanics effect.mEffectId = effectIt->mEffectID; effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; effect.mMagnitude = magnitude; + effect.mTimeLeft = 0.f; // Avoid applying absorb effects if the caster is the target // We still need the spell to be added @@ -225,6 +226,8 @@ namespace MWMechanics { effect.mDuration = hasDuration ? static_cast(effectIt->mDuration) : 1.f; + effect.mTimeLeft = effect.mDuration; + targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude)); // add to list of active effects, to apply in next frame diff --git a/components/esm/activespells.cpp b/components/esm/activespells.cpp index 4f51a619e..46558ceb7 100644 --- a/components/esm/activespells.cpp +++ b/components/esm/activespells.cpp @@ -16,7 +16,6 @@ namespace ESM esm.writeHNT ("CAST", params.mCasterActorId); esm.writeHNString ("DISP", params.mDisplayName); - esm.writeHNT ("TIME", params.mTimeStamp); for (std::vector::const_iterator effectIt = params.mEffects.begin(); effectIt != params.mEffects.end(); ++effectIt) { @@ -25,12 +24,15 @@ namespace ESM esm.writeHNT ("ARG_", effectIt->mArg); esm.writeHNT ("MAGN", effectIt->mMagnitude); esm.writeHNT ("DURA", effectIt->mDuration); + esm.writeHNT ("LEFT", effectIt->mTimeLeft); } } } void ActiveSpells::load(ESMReader &esm) { + int format = esm.getFormat(); + while (esm.isNextSub("ID__")) { std::string spellId = esm.getHString(); @@ -38,7 +40,10 @@ namespace ESM ActiveSpellParams params; esm.getHNT (params.mCasterActorId, "CAST"); params.mDisplayName = esm.getHNString ("DISP"); - esm.getHNT (params.mTimeStamp, "TIME"); + + // spell casting timestamp, no longer used + if (esm.isNextSub("TIME")) + esm.skipHSub(); while (esm.isNextSub("MGEF")) { @@ -48,6 +53,11 @@ namespace ESM esm.getHNOT(effect.mArg, "ARG_"); esm.getHNT (effect.mMagnitude, "MAGN"); esm.getHNT (effect.mDuration, "DURA"); + if (format < 9) + effect.mTimeLeft = effect.mDuration; + else + esm.getHNT (effect.mTimeLeft, "LEFT"); + params.mEffects.push_back(effect); } mSpells.insert(std::make_pair(spellId, params)); diff --git a/components/esm/activespells.hpp b/components/esm/activespells.hpp index d9e9a8c63..20b2f652d 100644 --- a/components/esm/activespells.hpp +++ b/components/esm/activespells.hpp @@ -21,6 +21,7 @@ namespace ESM float mMagnitude; int mArg; // skill or attribute float mDuration; + float mTimeLeft; }; // format 0, saved games only @@ -29,7 +30,6 @@ namespace ESM struct ActiveSpellParams { std::vector mEffects; - ESM::TimeStamp mTimeStamp; std::string mDisplayName; int mCasterActorId; }; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index f2ebc7bf0..6696ed478 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 8; +int ESM::SavedGame::sCurrentFormat = 9; void ESM::SavedGame::load (ESMReader &esm) { From 71350c6dff5aa7b1f6ca2ef07530001e497f9680 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 15:52:33 +0200 Subject: [PATCH 101/227] Remove redundant variable --- apps/openmw/mwmechanics/aiwander.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index ff213b219..f8df955cf 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -276,8 +276,7 @@ namespace MWMechanics completeManualWalking(actor, storage); } - AiWanderStorage::WanderState& wanderState = storage.mState; - if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid) + if (storage.mState == AiWanderStorage::Wander_MoveNow && storage.mCanWanderAlongPathGrid) { // Construct a new path if there isn't one if(!mPathFinder.isPathConstructed()) @@ -293,7 +292,7 @@ namespace MWMechanics completeManualWalking(actor, storage); } - if (wanderState == AiWanderStorage::Wander_Walking + if (storage.mState == AiWanderStorage::Wander_Walking && (isDestinationHidden(actor, mPathFinder.getPath().back()) || isAreaOccupiedByOtherActor(actor, mPathFinder.getPath().back()))) completeManualWalking(actor, storage); From e616188265e6e78f8dae74ab87c3e251d34c85a2 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 16:07:17 +0200 Subject: [PATCH 102/227] Do not allow wandering actor to have empty path --- apps/openmw/mwmechanics/aiwander.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index f8df955cf..1e06be6fd 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -293,7 +293,8 @@ namespace MWMechanics } if (storage.mState == AiWanderStorage::Wander_Walking - && (isDestinationHidden(actor, mPathFinder.getPath().back()) + && (mPathFinder.getPathSize() == 0 + || isDestinationHidden(actor, mPathFinder.getPath().back()) || isAreaOccupiedByOtherActor(actor, mPathFinder.getPath().back()))) completeManualWalking(actor, storage); From 489a92de9532b1b0ae08de90b7a14a8d86578a92 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 16:26:39 +0200 Subject: [PATCH 103/227] Check for hidden path only for actors wandering manually Actors who doesn't wander over pathgrid. --- apps/openmw/mwmechanics/aiwander.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 1e06be6fd..53e80f54e 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -292,7 +292,8 @@ namespace MWMechanics completeManualWalking(actor, storage); } - if (storage.mState == AiWanderStorage::Wander_Walking + if (storage.mIsWanderingManually + && storage.mState == AiWanderStorage::Wander_Walking && (mPathFinder.getPathSize() == 0 || isDestinationHidden(actor, mPathFinder.getPath().back()) || isAreaOccupiedByOtherActor(actor, mPathFinder.getPath().back()))) From b8513e03181c319b05edfe1bf130dc919b137d76 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 14:53:42 +0200 Subject: [PATCH 104/227] Remove unused arguments --- apps/openmw/mwmechanics/aiwander.cpp | 20 ++++++++++---------- apps/openmw/mwmechanics/aiwander.hpp | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 53e80f54e..8b90eb1d7 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -165,7 +165,7 @@ namespace MWMechanics * actors will enter combat (i.e. no longer wandering) and different pathfinding * will kick in. */ - bool AiWander::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) + bool AiWander::execute (const MWWorld::Ptr& actor, CharacterController& /*characterController*/, AiState& state, float duration) { MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor); if (cStats.isDead() || cStats.getHealth().getCurrent() <= 0) @@ -206,7 +206,7 @@ namespace MWMechanics { if (storage.mState == AiWanderStorage::Wander_Walking) { - stopWalking(actor, storage, false); + stopWalking(actor, false); mObstacleCheck.clear(); storage.setState(AiWanderStorage::Wander_IdleNow); } @@ -230,7 +230,7 @@ namespace MWMechanics if (mDistance <= 0) storage.mCanWanderAlongPathGrid = false; - if (isPackageCompleted(actor, storage)) + if (isPackageCompleted(actor)) { // Reset package so it can be used again mRemainingDuration=mDuration; @@ -315,14 +315,14 @@ namespace MWMechanics return actor.getRefData().getPosition().asVec3(); } - bool AiWander::isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage) + bool AiWander::isPackageCompleted(const MWWorld::Ptr& actor) { if (mDuration) { // End package if duration is complete if (mRemainingDuration <= 0) { - stopWalking(actor, storage); + stopWalking(actor); return true; } } @@ -395,7 +395,7 @@ namespace MWMechanics } void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) { - stopWalking(actor, storage); + stopWalking(actor); mObstacleCheck.clear(); storage.setState(AiWanderStorage::Wander_IdleNow); } @@ -460,7 +460,7 @@ namespace MWMechanics // Is there no destination or are we there yet? if ((!mPathFinder.isPathConstructed()) || pathTo(actor, osg::Vec3f(mPathFinder.getPath().back()), duration, DESTINATION_TOLERANCE)) { - stopWalking(actor, storage); + stopWalking(actor); storage.setState(AiWanderStorage::Wander_ChooseAction); } else @@ -518,7 +518,7 @@ namespace MWMechanics storage.mTrimCurrentNode = true; trimAllowedNodes(storage.mAllowedNodes, mPathFinder); mObstacleCheck.clear(); - stopWalking(actor, storage); + stopWalking(actor); storage.setState(AiWanderStorage::Wander_MoveNow); } @@ -529,7 +529,7 @@ namespace MWMechanics if (storage.mStuckCount >= getCountBeforeReset(actor)) // something has gone wrong, reset { mObstacleCheck.clear(); - stopWalking(actor, storage); + stopWalking(actor); storage.setState(AiWanderStorage::Wander_ChooseAction); storage.mStuckCount = 0; } @@ -609,7 +609,7 @@ namespace MWMechanics return TypeIdWander; } - void AiWander::stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage, bool clearPath) + void AiWander::stopWalking(const MWWorld::Ptr& actor, bool clearPath) { if (clearPath) { diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 376be3a25..405799c1f 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -118,7 +118,7 @@ namespace MWMechanics private: // NOTE: mDistance and mDuration must be set already void init(); - void stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage, bool clearPath = true); + void stopWalking(const MWWorld::Ptr& actor, bool clearPath = true); /// Have the given actor play an idle animation /// @return Success or error @@ -133,7 +133,7 @@ namespace MWMechanics void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage); bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos); - bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage); + bool isPackageCompleted(const MWWorld::Ptr& actor); void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance); bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination); void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage); From 131f2557b16da7c6b7bc4dfa0f181004731ce958 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 14:57:20 +0200 Subject: [PATCH 105/227] Split functions to remove redundant clearPath argument --- apps/openmw/mwmechanics/aiwander.cpp | 18 ++++++++++-------- apps/openmw/mwmechanics/aiwander.hpp | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 8b90eb1d7..e37039403 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -89,6 +89,11 @@ namespace MWMechanics const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())); return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, actor); } + + void stopMovement(const MWWorld::Ptr& actor) + { + actor.getClass().getMovementSettings(actor).mPosition[1] = 0; + } } AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): @@ -206,7 +211,7 @@ namespace MWMechanics { if (storage.mState == AiWanderStorage::Wander_Walking) { - stopWalking(actor, false); + stopMovement(actor); mObstacleCheck.clear(); storage.setState(AiWanderStorage::Wander_IdleNow); } @@ -609,14 +614,11 @@ namespace MWMechanics return TypeIdWander; } - void AiWander::stopWalking(const MWWorld::Ptr& actor, bool clearPath) + void AiWander::stopWalking(const MWWorld::Ptr& actor) { - if (clearPath) - { - mPathFinder.clearPath(); - mHasDestination = false; - } - actor.getClass().getMovementSettings(actor).mPosition[1] = 0; + mPathFinder.clearPath(); + mHasDestination = false; + stopMovement(actor); } bool AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 405799c1f..716c695ea 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -118,7 +118,7 @@ namespace MWMechanics private: // NOTE: mDistance and mDuration must be set already void init(); - void stopWalking(const MWWorld::Ptr& actor, bool clearPath = true); + void stopWalking(const MWWorld::Ptr& actor); /// Have the given actor play an idle animation /// @return Success or error From 256c9917a415d22d5525beb6ba2f6571a10bf6cd Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 14:58:50 +0200 Subject: [PATCH 106/227] Make AiWander::isPackageCompleted const --- apps/openmw/mwmechanics/aiwander.cpp | 18 +++++------------- apps/openmw/mwmechanics/aiwander.hpp | 2 +- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index e37039403..f04477243 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -235,8 +235,9 @@ namespace MWMechanics if (mDistance <= 0) storage.mCanWanderAlongPathGrid = false; - if (isPackageCompleted(actor)) + if (isPackageCompleted()) { + stopWalking(actor); // Reset package so it can be used again mRemainingDuration=mDuration; init(); @@ -320,19 +321,10 @@ namespace MWMechanics return actor.getRefData().getPosition().asVec3(); } - bool AiWander::isPackageCompleted(const MWWorld::Ptr& actor) + bool AiWander::isPackageCompleted() const { - if (mDuration) - { - // End package if duration is complete - if (mRemainingDuration <= 0) - { - stopWalking(actor); - return true; - } - } - // if get here, not yet completed - return false; + // End package if duration is complete + return mDuration && mRemainingDuration <= 0; } /* diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 716c695ea..8154ea995 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -133,7 +133,7 @@ namespace MWMechanics void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage); bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos); - bool isPackageCompleted(const MWWorld::Ptr& actor); + inline bool isPackageCompleted() const; void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance); bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination); void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage); From d86669843e15a0b4a72aa7ea0cf25e9ccde507fd Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 17:07:42 +0200 Subject: [PATCH 107/227] Remove unseud pointTolerance argument --- apps/openmw/mwmechanics/aiwander.cpp | 9 +++------ apps/openmw/mwmechanics/aiwander.hpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 2 +- apps/openmw/mwmechanics/pathfinding.hpp | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index f04477243..597453409 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -463,7 +463,7 @@ namespace MWMechanics else { // have not yet reached the destination - evadeObstacles(actor, duration, storage); + evadeObstacles(actor, storage); } } @@ -494,15 +494,12 @@ namespace MWMechanics storage.setState(AiWanderStorage::Wander_IdleNow); } - void AiWander::evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage) + void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage) { if (mUsePathgrid) { const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); - const float actorTolerance = 2 * actor.getClass().getSpeed(actor) * duration - + 1.2 * std::max(halfExtents.x(), halfExtents.y()); - const float pointTolerance = std::max(MIN_TOLERANCE, actorTolerance); - mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor), pointTolerance); + mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor)); } if (mObstacleCheck.isEvading()) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 8154ea995..6e69b6c79 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -126,7 +126,7 @@ namespace MWMechanics bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); short unsigned getRandomIdle(); void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos); - void evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); + void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage); void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage); void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index a7bba5b63..b072f55f8 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -361,7 +361,7 @@ namespace MWMechanics } void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, - const DetourNavigator::Flags flags, const float pointTolerance) + const DetourNavigator::Flags flags) { if (mPath.empty()) return; diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 06b4aa10d..cb33471ca 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -97,7 +97,7 @@ namespace MWMechanics const DetourNavigator::Flags flags); void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, - const DetourNavigator::Flags flags, const float pointTolerance); + const DetourNavigator::Flags flags); /// Remove front point if exist and within tolerance void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance); From 38daa83ff6f73defbeeb3c86a4744ca5ba66c796 Mon Sep 17 00:00:00 2001 From: psi29a Date: Sat, 16 May 2020 21:55:14 +0000 Subject: [PATCH 108/227] Merge branch 'ninja' into 'master' Enable Windows Ninja builds See merge request OpenMW/openmw!202 (cherry picked from commit e0b352323226ff11e230f6489e826df332fa681a) c1e673ce Unify path conversion functions fdf0fdbb Fix NMake with MSVC 2019 bdd4a814 Activate MSVC during CMake setup for NMake eae41050 Support sourcing c0d28a0e Warn that MSVC environment will need to be activated bd16ad62 Ninja 7d57e6e2 Support MSVC 2015 3679d329 Check MSVC activated correctly ed4b73b8 Fix post-2015 Visual Studio 4ffa116a Print message when it's necessary instead of when it isn't c6e09461 Add instructions for using VS' non-.sln support d9bb6e63 Activate MSVC later 9ca26358 Create batch script to activate correct MSVC 61df647d Provide scripts to activate selected MSVC in existing shell without kerfuffle --- CI/ActivateMSVC.ps1 | 26 +++++++ CI/activate_msvc.sh | 76 +++++++++++++++++++ CI/before_script.msvc.sh | 153 ++++++++++++++++++++++++++++++++++----- 3 files changed, 236 insertions(+), 19 deletions(-) create mode 100644 CI/ActivateMSVC.ps1 create mode 100644 CI/activate_msvc.sh diff --git a/CI/ActivateMSVC.ps1 b/CI/ActivateMSVC.ps1 new file mode 100644 index 000000000..ca78ef588 --- /dev/null +++ b/CI/ActivateMSVC.ps1 @@ -0,0 +1,26 @@ +& "${env:COMSPEC}" /c ActivateMSVC.bat "&&" set | ForEach-Object { + $name, $value = $_ -split '=', 2 + Set-Content env:\"$name" $value +} + +$MissingTools = $false +$tools = "cl", "link", "rc", "mt", "awooga" +$descriptions = "MSVC Compiler", "MSVC Linker", "MS Windows Resource Compiler", "MS Windows Manifest Tool", "A made up command" +for ($i = 0; $i -lt $tools.Length; $i++) { + $present = $true + try { + Get-Command $tools[$i] *>&1 | Out-Null + $present = $present -and $? + } catch { + $present = $false + } + if (!$present) { + Write-Warning "$($tools[$i]) ($($descriptions[$i])) missing." + $MissingTools = $true + } +} + +if ($MissingTools) { + Write-Error "Some build tools were unavailable after activating MSVC in the shell. It's likely that your Visual Studio $MSVC_DISPLAY_YEAR installation needs repairing." + exit 1 +} \ No newline at end of file diff --git a/CI/activate_msvc.sh b/CI/activate_msvc.sh new file mode 100644 index 000000000..0764cd02f --- /dev/null +++ b/CI/activate_msvc.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +set -euo pipefail + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + echo "Error: Script not sourced." + echo "You must source this script for it to work, i.e. " + echo "source ./activate_msvc.sh" + echo "or" + echo ". ./activate_msvc.sh" + exit 1 +fi + +command -v unixPathAsWindows >/dev/null 2>&1 || function unixPathAsWindows { + if command -v cygpath >/dev/null 2>&1; then + cygpath -w $1 + else + echo "$1" | sed "s,^/\([^/]\)/,\\1:/," | sed "s,/,\\\\,g" + fi +} + +function windowsSystemPathAsUnix { + if command -v cygpath >/dev/null 2>&1; then + cygpath -u -p $1 + else + IFS=';' read -r -a paths <<< "$1" + declare -a convertedPaths + for entry in paths; do + convertedPaths+=(windowsPathAsUnix $entry) + done + convertedPath=printf ":%s" ${convertedPaths[@]} + echo ${convertedPath:1} + fi +} + +# capture CMD environment so we know what's been changed +declare -A originalCmdEnv +originalIFS="$IFS" +IFS=$'\n\r' +for pair in $(cmd //c "set"); do + IFS='=' read -r -a separatedPair <<< "${pair}" + originalCmdEnv["${separatedPair[0]}"]="${separatedPair[1]}" +done + +# capture CMD environment in a shell with MSVC activated +cmdEnv="$(cmd //c "$(unixPathAsWindows "$(dirname "${BASH_SOURCE[0]}")")\ActivateMSVC.bat" "&&" set)" + +declare -A cmdEnvChanges +for pair in $cmdEnv; do + if [ -n "$pair" ]; then + IFS='=' read -r -a separatedPair <<< "${pair}" + key="${separatedPair[0]}" + value="${separatedPair[1]}" + if ! [ ${originalCmdEnv[$key]+_} ] || [ "${originalCmdEnv[$key]}" != "$value" ]; then + if [ $key != 'PATH' ] && [ $key != 'path' ] && [ $key != 'Path' ]; then + export "$key=$value" + else + export PATH=$(windowsSystemPathAsUnix $value) + fi + fi + fi +done + +MISSINGTOOLS=0 + +command -v cl >/dev/null 2>&1 || { echo "Error: cl (MSVC Compiler) missing."; MISSINGTOOLS=1; } +command -v link >/dev/null 2>&1 || { echo "Error: link (MSVC Linker) missing."; MISSINGTOOLS=1; } +command -v rc >/dev/null 2>&1 || { echo "Error: rc (MS Windows Resource Compiler) missing."; MISSINGTOOLS=1; } +command -v mt >/dev/null 2>&1 || { echo "Error: mt (MS Windows Manifest Tool) missing."; MISSINGTOOLS=1; } + +if [ $MISSINGTOOLS -ne 0 ]; then + echo "Some build tools were unavailable after activating MSVC in the shell. It's likely that your Visual Studio $MSVC_DISPLAY_YEAR installation needs repairing." + return 1 +fi + +IFS="$originalIFS" \ No newline at end of file diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 3a0b062e6..bf115d315 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -1,25 +1,49 @@ #!/bin/bash # set -x # turn-on for debugging +function wrappedExit { + if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + exit $1 + else + return $1 + fi +} + MISSINGTOOLS=0 command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; } command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; } if [ $MISSINGTOOLS -ne 0 ]; then - exit 1 + wrappedExit 1 fi WORKINGDIR="$(pwd)" case "$WORKINGDIR" in *[[:space:]]*) echo "Error: Working directory contains spaces." - exit 1 + wrappedExit 1 ;; esac set -euo pipefail +function windowsPathAsUnix { + if command -v cygpath >/dev/null 2>&1; then + cygpath -u $1 + else + echo "$1" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1," + fi +} + +function unixPathAsWindows { + if command -v cygpath >/dev/null 2>&1; then + cygpath -w $1 + else + echo "$1" | sed "s,^/\([^/]\)/,\\1:/," | sed "s,/,\\\\,g" + fi +} + APPVEYOR=${APPVEYOR:-} CI=${CI:-} STEP=${STEP:-} @@ -32,12 +56,16 @@ KEEP="" UNITY_BUILD="" VS_VERSION="" NMAKE="" +NINJA="" PLATFORM="" CONFIGURATION="" TEST_FRAMEWORK="" GOOGLE_INSTALL_ROOT="" INSTALL_PREFIX="." +ACTIVATE_MSVC="" +SINGLE_CONFIG="" + while [ $# -gt 0 ]; do ARGSTR=$1 shift @@ -45,7 +73,7 @@ while [ $# -gt 0 ]; do if [ ${ARGSTR:0:1} != "-" ]; then echo "Unknown argument $ARGSTR" echo "Try '$0 -h'" - exit 1 + wrappedExit 1 fi for (( i=1; i<${#ARGSTR}; i++ )); do @@ -72,6 +100,9 @@ while [ $# -gt 0 ]; do n ) NMAKE=true ;; + + N ) + NINJA=true ;; p ) PLATFORM=$1 @@ -111,25 +142,31 @@ Options: -v <2013/2015/2017/2019> Choose the Visual Studio version to use. -n - Produce NMake makefiles instead of a Visual Studio solution. + Produce NMake makefiles instead of a Visual Studio solution. Cannout be used with -N. + -N + Produce Ninja (multi-config if CMake is new enough to support it) files instead of a Visual Studio solution. Cannot be used with -n. -V Run verbosely -i CMake install prefix EOF - exit 0 + wrappedExit 0 ;; * ) echo "Unknown argument $ARG." echo "Try '$0 -h'" - exit 1 ;; + wrappedExit 1 ;; esac done done -if [ -n "$NMAKE" ]; then - command -v nmake -? >/dev/null 2>&1 || { echo "Error: nmake (NMake) is not on the path. Make sure you have the necessary environment variables set for command-line C++ development (for example, by starting from a Developer Command Prompt)."; exit 1; } +if [ -n "$NMAKE" ] || [ -n "$NINJA" ]; then + if [ -n "$NMAKE" ] && [ -n "$NINJA" ]; then + echo "Cannout run in NMake and Ninja mode at the same time." + wrappedExit 1 + fi + ACTIVATE_MSVC=true fi if [ -z $VERBOSE ]; then @@ -139,7 +176,7 @@ fi if [ -z $APPVEYOR ]; then echo "Running prebuild outside of Appveyor." - DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,") + DIR=$(windowsPathAsUnix "${BASH_SOURCE[0]}") cd $(dirname "$DIR")/.. else echo "Running prebuild in Appveyor." @@ -322,7 +359,7 @@ case $PLATFORM in * ) echo "Unknown platform $PLATFORM." - exit 1 + wrappedExit 1 ;; esac @@ -349,9 +386,18 @@ fi if [ -n "$NMAKE" ]; then GENERATOR="NMake Makefiles" + SINGLE_CONFIG=true fi -if [ $MSVC_REAL_VER -ge 16 ]; then +if [ -n "$NINJA" ]; then + GENERATOR="Ninja Multi-Config" + if ! cmake -E capabilities | grep -F "$GENERATOR" > /dev/null; then + SINGLE_CONFIG=true + GENERATOR="Ninja" + fi +fi + +if [ $MSVC_REAL_VER -ge 16 ] && [ -z "$NMAKE" ] && [ -z "$NINJA" ]; then if [ $BITS -eq 64 ]; then add_cmake_opts "-G\"$GENERATOR\" -A x64" else @@ -361,7 +407,7 @@ else add_cmake_opts "-G\"$GENERATOR\"" fi -if [ -n "$NMAKE" ]; then +if [ -n "$SINGLE_CONFIG" ]; then add_cmake_opts "-DCMAKE_BUILD_TYPE=${BUILD_CONFIG}" fi @@ -456,7 +502,13 @@ cd .. #/.. BUILD_DIR="MSVC${MSVC_DISPLAY_YEAR}_${BITS}" if [ -n "$NMAKE" ]; then - BUILD_DIR="${BUILD_DIR}_NMake_${BUILD_CONFIG}" + BUILD_DIR="${BUILD_DIR}_NMake" +elif [ -n "$NINJA" ]; then + BUILD_DIR="${BUILD_DIR}_Ninja" +fi + +if [ -n "$SINGLE_CONFIG" ]; then + BUILD_DIR="${BUILD_DIR}_${BUILD_CONFIG}" fi if [ -z $KEEP ]; then @@ -494,10 +546,10 @@ fi # We work around this by installing to root of the current working drive and then move it to our deps # get the current working drive's root, we'll install to that temporarily CWD_DRIVE_ROOT="$(powershell -command '(get-location).Drive.Root')Boost_temp" - CWD_DRIVE_ROOT_BASH=$(echo "$CWD_DRIVE_ROOT" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,") + CWD_DRIVE_ROOT_BASH=$(windowsPathAsUnix "$CWD_DRIVE_ROOT") if [ -d CWD_DRIVE_ROOT_BASH ]; then printf "Cannot continue, ${CWD_DRIVE_ROOT_BASH} aka ${CWD_DRIVE_ROOT} already exists. Please remove before re-running. "; - exit 1; + wrappedExit 1; fi if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION ${BOOST_VER_SDK}" Boost/boost/version.hpp > /dev/null; then @@ -695,7 +747,7 @@ fi else SUFFIX="" fi - DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,") + DIR=$(windowsPathAsUnix "${QT_SDK}") add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll" echo Done. @@ -806,14 +858,15 @@ fi #if [ -z $CI ]; then echo "- Copying Runtime DLLs..." DLL_PREFIX="" - if [ -z $NMAKE ]; then + if [ -z $SINGLE_CONFIG ]; then mkdir -p $BUILD_CONFIG DLL_PREFIX="$BUILD_CONFIG/" fi for DLL in $RUNTIME_DLLS; do TARGET="$(basename "$DLL")" if [[ "$DLL" == *":"* ]]; then - IFS=':'; SPLIT=( ${DLL} ); unset IFS + originalIFS="$IFS" + IFS=':'; SPLIT=( ${DLL} ); IFS=$originalIFS DLL=${SPLIT[0]} TARGET=${SPLIT[1]} fi @@ -836,6 +889,42 @@ fi done echo #fi + +if ! [ -z $ACTIVATE_MSVC ]; then + echo -n "- Activating MSVC in the current shell... " + command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; } + + MSVC_INSTALLATION_PATH=$(vswhere -legacy -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) + if [ $MSVC_REAL_VER -ge 15 ]; then + echo "@\"${MSVC_INSTALLATION_PATH}\Common7\Tools\VsDevCmd.bat\" -no_logo -arch=$([ $BITS -eq 64 ] && echo "amd64" || echo "x86") -host_arch=$([ $(uname -m) == 'x86_64' ] && echo "amd64" || echo "x86")" > ActivateMSVC.bat + else + if [ $(uname -m) == 'x86_64' ]; then + if [ $BITS -eq 64 ]; then + compiler=amd64 + else + compiler=amd64_x86 + fi + else + if [ $BITS -eq 64 ]; then + compiler=x86_amd64 + else + compiler=x86 + fi + fi + echo "@\"${MSVC_INSTALLATION_PATH}\VC\vcvarsall.bat\" $compiler" > ActivateMSVC.bat + fi + + cp "../CI/activate_msvc.sh" . + sed -i "s/\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g" activate_msvc.sh + source ./activate_msvc.sh + + cp "../CI/ActivateMSVC.ps1" . + sed -i "s/\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g" ActivateMSVC.ps1 + + echo "done." + echo +fi + if [ -z $VERBOSE ]; then printf -- "- Configuring... " else @@ -846,8 +935,34 @@ RET=$? if [ -z $VERBOSE ]; then if [ $RET -eq 0 ]; then echo Done. + if [ -n $ACTIVATE_MSVC ]; then + echo + echo "Note: you must manually activate MSVC for the shell in which you want to do the build." + echo + echo "Some scripts have been created in the build directory to do so in an existing shell." + echo "Bash: source activate_msvc.sh" + echo "CMD: ActivateMSVC.bat" + echo "PowerShell: ActivateMSVC.ps1" + echo + echo "You may find options to launch a Development/Native Tools/Cross Tools shell in your start menu or Visual Studio." + echo + if [ $(uname -m) == 'x86_64' ]; then + if [ $BITS -eq 64 ]; then + inheritEnvironments=msvc_x64_x64 + else + inheritEnvironments=msvc_x64 + fi + else + if [ $BITS -eq 64 ]; then + inheritEnvironments=msvc_x86_x64 + else + inheritEnvironments=msvc_x86 + fi + fi + echo "In Visual Studio 15.3 (2017 Update 3) or later, try setting '\"inheritEnvironments\": [ \"$inheritEnvironments\" ]' in CMakeSettings.json to build in the IDE." + fi else echo Failed. fi fi -exit $RET +wrappedExit $RET From 7b781d88909214120ae2f5655e12d68cbc08ec2c Mon Sep 17 00:00:00 2001 From: "Alexander \"Ananace\" Olofsson" Date: Sun, 17 May 2020 01:12:04 +0200 Subject: [PATCH 109/227] Windows CI dependency upgrade (#2847) * Windows CI: Use OSG 3.4-experimental for 0.46 * Update compiled Windows CI dependencies Only built and pushed so far, still need to try making full OpenMW builds with them as well. * Update missed Bullet version number * MyGUI uses RelWithDebInfo for Release builds now * Update Windows CI dependencies, switch Qt install * Fix aqt retrieval and setup * Make aqt install output slightly nicer * Bump to Qt 5.15 for VS2019 support * Fix FFmpeg and Qt install parts * Fix OSG plugin DLL copying * Add CMake flag for double-precision bullet * Roll back 2019 to Boost 1.71 for CI * Move aqt into unpack step, to allow manual install --- CI/before_script.msvc.sh | 211 ++++++++++++++++++++++++--------------- 1 file changed, 132 insertions(+), 79 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 3a0b062e6..c1786889a 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -5,6 +5,7 @@ MISSINGTOOLS=0 command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; } command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; } +command -v python >/dev/null 2>&1 || { echo "Warning: Python is not on the path, automatic Qt installation impossible."; } if [ $MISSINGTOOLS -ne 0 ]; then exit 1 @@ -32,11 +33,15 @@ KEEP="" UNITY_BUILD="" VS_VERSION="" NMAKE="" +PDBS="" PLATFORM="" CONFIGURATION="" TEST_FRAMEWORK="" GOOGLE_INSTALL_ROOT="" INSTALL_PREFIX="." +BULLET_DOUBLE="" +BULLET_DBL="" +BULLET_DBL_DISPLAY="Single precision" while [ $# -gt 0 ]; do ARGSTR=$1 @@ -57,6 +62,9 @@ while [ $# -gt 0 ]; do d ) SKIP_DOWNLOAD=true ;; + D ) + BULLET_DOUBLE=true ;; + e ) SKIP_EXTRACT=true ;; @@ -77,6 +85,9 @@ while [ $# -gt 0 ]; do PLATFORM=$1 shift ;; + P ) + PDBS=true ;; + c ) CONFIGURATION=$1 shift ;; @@ -96,6 +107,8 @@ Options: Set the configuration, can also be set with environment variable CONFIGURATION. -d Skip checking the downloads. + -D + Use double-precision Bullet -e Skip extracting dependencies. -h @@ -112,6 +125,8 @@ Options: Choose the Visual Studio version to use. -n Produce NMake makefiles instead of a Visual Studio solution. + -P + Download debug symbols where available -V Run verbosely -i @@ -264,6 +279,7 @@ case $VS_VERSION in MSVC_REAL_VER="16" MSVC_VER="14.2" MSVC_YEAR="2015" + MSVC_REAL_YEAR="2019" MSVC_DISPLAY_YEAR="2019" BOOST_VER="1.71.0" BOOST_VER_URL="1_71_0" @@ -276,6 +292,7 @@ case $VS_VERSION in MSVC_REAL_VER="15" MSVC_VER="14.1" MSVC_YEAR="2015" + MSVC_REAL_YEAR="2017" MSVC_DISPLAY_YEAR="2017" BOOST_VER="1.67.0" BOOST_VER_URL="1_67_0" @@ -288,6 +305,7 @@ case $VS_VERSION in MSVC_REAL_VER="14" MSVC_VER="14.0" MSVC_YEAR="2015" + MSVC_REAL_YEAR="2015" MSVC_DISPLAY_YEAR="2015" BOOST_VER="1.67.0" BOOST_VER_URL="1_67_0" @@ -295,15 +313,8 @@ case $VS_VERSION in ;; 12|12.0|2013 ) - GENERATOR="Visual Studio 12 2013" - TOOLSET="vc120" - MSVC_REAL_VER="12" - MSVC_VER="12.0" - MSVC_YEAR="2013" - MSVC_DISPLAY_YEAR="2013" - BOOST_VER="1.58.0" - BOOST_VER_URL="1_58_0" - BOOST_VER_SDK="105800" + echo "Visual Studio 2013 is no longer supported" + exit 1 ;; esac @@ -369,6 +380,12 @@ if ! [ -z $UNITY_BUILD ]; then add_cmake_opts "-DOPENMW_UNITY_BUILD=True" fi +if [ -n "$BULLET_DOUBLE" ]; then + BULLET_DBL="-double" + BULLET_DBL_DISPLAY="Double precision" + add_cmake_opts "-DBULLET_USE_DOUBLES=True" +fi + echo echo "===================================" echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}" @@ -393,45 +410,54 @@ if [ -z $SKIP_DOWNLOAD ]; then fi # Bullet - download "Bullet 2.87" \ - "https://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z" \ - "Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z" + download "Bullet 2.89 (${BULLET_DBL_DISPLAY})" \ + "https://rgw.ctrl-c.liu.se/openmw/Deps/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" \ + "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" # FFmpeg - download "FFmpeg 3.2.4" \ - "https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-3.2.4-win${BITS}-shared.zip" \ - "ffmpeg-3.2.4-win${BITS}.zip" \ - "https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-3.2.4-win${BITS}-dev.zip" \ - "ffmpeg-3.2.4-dev-win${BITS}.zip" + download "FFmpeg 4.2.2" \ + "https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-4.2.2-win${BITS}-shared.zip" \ + "ffmpeg-4.2.2-win${BITS}.zip" \ + "https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-4.2.2-win${BITS}-dev.zip" \ + "ffmpeg-4.2.2-dev-win${BITS}.zip" # MyGUI - download "MyGUI 3.2.2" \ - "https://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" \ - "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" + download "MyGUI 3.4.0" \ + "https://rgw.ctrl-c.liu.se/openmw/Deps/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \ + "MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" + + if [ -n "$PDBS" ]; then + download "MyGUI symbols" \ + "https://rgw.ctrl-c.liu.se/openmw/Deps/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \ + "MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" + fi # OpenAL - download "OpenAL-Soft 1.19.1" \ - "http://openal-soft.org/openal-binaries/openal-soft-1.19.1-bin.zip" \ - "OpenAL-Soft-1.19.1.zip" + download "OpenAL-Soft 1.20.1" \ + "http://openal-soft.org/openal-binaries/openal-soft-1.20.1-bin.zip" \ + "OpenAL-Soft-1.20.1.zip" # OSG - download "OpenSceneGraph 3.4.1-scrawl" \ - "https://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \ - "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" + download "OpenSceneGraph 3.6.5" \ + "https://rgw.ctrl-c.liu.se/openmw/Deps/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \ + "OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" + + if [ -n "$PDBS" ]; then + download "OpenSceneGraph symbols" \ + "https://rgw.ctrl-c.liu.se/openmw/Deps/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \ + "OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" + fi # Qt if [ -z $APPVEYOR ]; then - if [ $BITS == "64" ]; then - QT_SUFFIX="_64" - else - QT_SUFFIX="" + if [ "${MSVC_REAL_YEAR}" = "2015" ] && [ "${BITS}" = "32" ]; then + echo "Qt no longer provides MSVC2015 Win32 packages, switch to 64-bit or a newer Visual Studio. Sorry." + exit 1 fi - download "Qt 5.7.0" \ - "https://download.qt.io/new_archive/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \ - "qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \ - "https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \ - "qt-5-install.qs" + download "AQt installer" \ + "https://files.pythonhosted.org/packages/f3/bb/aee972f08deecca31bfc46b5aedfad1ce6c7f3aaf1288d685e4a914b53ac/aqtinstall-0.8-py2.py3-none-any.whl" \ + "aqtinstall-0.8-py2.py3-none-any.whl" fi # SDL2 @@ -533,15 +559,15 @@ fi cd $DEPS echo # Bullet -printf "Bullet 2.87... " +printf "Bullet 2.89 (${BULLET_DBL_DISPLAY})... " { cd $DEPS_INSTALL if [ -d Bullet ]; then printf -- "Exists. (No version checking) " elif [ -z $SKIP_EXTRACT ]; then rm -rf Bullet - eval 7z x -y "${DEPS}/Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP - mv "Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}" Bullet + eval 7z x -y "${DEPS}/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" $STRIP + mv "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}" Bullet fi export BULLET_ROOT="$(real_pwd)/Bullet" echo Done. @@ -549,21 +575,21 @@ printf "Bullet 2.87... " cd $DEPS echo # FFmpeg -printf "FFmpeg 3.2.4... " +printf "FFmpeg 4.2.2... " { cd $DEPS_INSTALL - if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then + if [ -d FFmpeg ] && grep "4.2.2" FFmpeg/README.txt > /dev/null; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then rm -rf FFmpeg - eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP - eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-win${BITS}.zip" $STRIP - mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg - cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/ - rm -rf "ffmpeg-3.2.4-win${BITS}-dev" + eval 7z x -y "${DEPS}/ffmpeg-4.2.2-win${BITS}.zip" $STRIP + eval 7z x -y "${DEPS}/ffmpeg-4.2.2-dev-win${BITS}.zip" $STRIP + mv "ffmpeg-4.2.2-win${BITS}-shared" FFmpeg + cp -r "ffmpeg-4.2.2-win${BITS}-dev/"* FFmpeg/ + rm -rf "ffmpeg-4.2.2-win${BITS}-dev" fi export FFMPEG_HOME="$(real_pwd)/FFmpeg" - add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll + add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-58,avformat-58,avutil-56,swresample-3,swscale-5}.dll if [ $BITS -eq 32 ]; then add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\"" fi @@ -572,62 +598,66 @@ printf "FFmpeg 3.2.4... " cd $DEPS echo # MyGUI -printf "MyGUI 3.2.2... " +printf "MyGUI 3.4.0... " { cd $DEPS_INSTALL if [ -d MyGUI ] && \ grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ - grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ - grep "MYGUI_VERSION_PATCH 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null + grep "MYGUI_VERSION_MINOR 4" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ + grep "MYGUI_VERSION_PATCH 0" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then rm -rf MyGUI - eval 7z x -y "${DEPS}/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP - mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI + eval 7z x -y "${DEPS}/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP + [ -n "$PDBS" ] && eval 7z x -y "${DEPS}/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP + mv "MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}" MyGUI fi export MYGUI_HOME="$(real_pwd)/MyGUI" if [ $CONFIGURATION == "Debug" ]; then SUFFIX="_d" + MYGUI_CONFIGURATION="Debug" else SUFFIX="" + MYGUI_CONFIGURATION="RelWithDebInfo" fi - add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll" + add_runtime_dlls "$(pwd)/MyGUI/bin/${MYGUI_CONFIGURATION}/MyGUIEngine${SUFFIX}.dll" echo Done. } cd $DEPS echo # OpenAL -printf "OpenAL-Soft 1.19.1... " +printf "OpenAL-Soft 1.20.1... " { - if [ -d openal-soft-1.19.1-bin ]; then + if [ -d openal-soft-1.20.1-bin ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then - rm -rf openal-soft-1.19.1-bin - eval 7z x -y OpenAL-Soft-1.19.1.zip $STRIP + rm -rf openal-soft-1.20.1-bin + eval 7z x -y OpenAL-Soft-1.20.1.zip $STRIP fi - OPENAL_SDK="$(real_pwd)/openal-soft-1.19.1-bin" + OPENAL_SDK="$(real_pwd)/openal-soft-1.20.1-bin" add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \ -DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib" - add_runtime_dlls "$(pwd)/openal-soft-1.19.1-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll" + add_runtime_dlls "$(pwd)/openal-soft-1.20.1-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll" echo Done. } cd $DEPS echo # OSG -printf "OSG 3.4.1-scrawl... " +printf "OSG 3.6.5... " { cd $DEPS_INSTALL if [ -d OSG ] && \ grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \ - grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \ - grep "OPENSCENEGRAPH_PATCH_VERSION 1" OSG/include/osg/Version > /dev/null + grep "OPENSCENEGRAPH_MINOR_VERSION 6" OSG/include/osg/Version > /dev/null && \ + grep "OPENSCENEGRAPH_PATCH_VERSION 5" OSG/include/osg/Version > /dev/null then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then rm -rf OSG - eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP - mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG + eval 7z x -y "${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP + [ -n "$PDBS" ] && eval 7z x -y "${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP + mv "OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}" OSG fi OSG_SDK="$(real_pwd)/OSG" add_cmake_opts -DOSG_DIR="$OSG_SDK" @@ -636,17 +666,17 @@ printf "OSG 3.4.1-scrawl... " else SUFFIX="" fi - add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \ + add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng}${SUFFIX}.dll \ "$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll - add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,freetype,jpeg,osg,png,tga}${SUFFIX}.dll - add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll + add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_"{bmp,dds,freetype,jpeg,osg,png,tga}${SUFFIX}.dll + add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll echo Done. } cd $DEPS echo # Qt if [ -z $APPVEYOR ]; then - printf "Qt 5.7.0... " + printf "Qt 5.15.0... " else printf "Qt 5.13 AppVeyor... " fi @@ -658,21 +688,44 @@ fi fi if [ -z $APPVEYOR ]; then cd $DEPS_INSTALL - QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}" - if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then + QT_SDK="$(real_pwd)/Qt/5.15.0/msvc${MSVC_YEAR}${SUFFIX}" + + if [ -d 'Qt/5.15.0' ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then + pushd "$DEPS" > /dev/null + if ! [ -d 'aqt-venv' ]; then + echo " Creating Virtualenv for aqt..." + eval python -m venv aqt-venv $STRIP + fi + if [ -d 'aqt-venv/bin' ]; then + VENV_BIN_DIR='bin' + elif [ -d 'aqt-venv/Scripts' ]; then + VENV_BIN_DIR='Scripts' + else + echo "Error: Failed to create virtualenv." + exit 1 + fi + + if ! [ -e "aqt-venv/${VENV_BIN_DIR}/aqt" ]; then + echo " Installing aqt wheel into virtualenv..." + eval "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall-0.8-py2.py3-none-any.whl $STRIP + fi + popd > /dev/null + rm -rf Qt - cp "${DEPS}/qt-5-install.qs" qt-install.qs - sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs - sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs - printf -- "(Installation might take a while) " - "${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent - mv qt-install.qs Qt/ - echo Done. + + mkdir Qt + cd Qt + + eval "${DEPS}/aqt-venv/${VENV_BIN_DIR}/aqt" install 5.15.0 windows desktop "win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}" $STRIP + printf " Cleaning up extraneous data... " - rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs} + rm -rf Qt/{aqtinstall.log,Tools} + + echo Done. fi + cd $QT_SDK add_cmake_opts -DDESIRED_QT_VERSION=5 \ -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ @@ -822,10 +875,10 @@ fi done echo echo "- OSG Plugin DLLs..." - mkdir -p ${DLL_PREFIX}osgPlugins-3.4.1 + mkdir -p ${DLL_PREFIX}osgPlugins-3.6.5 for DLL in $OSG_PLUGINS; do echo " $(basename $DLL)." - cp "$DLL" ${DLL_PREFIX}osgPlugins-3.4.1 + cp "$DLL" ${DLL_PREFIX}osgPlugins-3.6.5 done echo echo "- Qt Platform DLLs..." From 2e09e96f5df38dd149b69713b94df531226d37b4 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 May 2020 15:19:07 +0200 Subject: [PATCH 110/227] Fix msvc dir for Qt Otherwise it fails with: Qt 5.15.0... Exists. CI/before_script.msvc.sh: line 781: cd: MSVC2019_64_Ninja/deps/Qt/5.15.0/msvc2015_64: No such file or directory --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 3da7eb0f9..2f40aef9c 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -740,7 +740,7 @@ fi fi if [ -z $APPVEYOR ]; then cd $DEPS_INSTALL - QT_SDK="$(real_pwd)/Qt/5.15.0/msvc${MSVC_YEAR}${SUFFIX}" + QT_SDK="$(real_pwd)/Qt/5.15.0/msvc${MSVC_REAL_YEAR}${SUFFIX}" if [ -d 'Qt/5.15.0' ]; then printf "Exists. " From e3cce0949e4fb4e97d423b81b8d7d933cbe8eb93 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 May 2020 17:24:47 +0200 Subject: [PATCH 111/227] Replace condition that may lead to UB by assert If mPackages is empty it means package is a pointer to a deleted object at line . We can assume it couldn't happen because execute is always called next for this object at line 289. --- apps/openmw/mwmechanics/aisequence.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 5f3931fcf..201701634 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -265,16 +265,15 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } } - if (!mPackages.empty()) - { - if (nearestDist < std::numeric_limits::max() && mPackages.begin() != itActualCombat) - { - // move combat package with nearest target to the front - mPackages.splice(mPackages.begin(), mPackages, itActualCombat); - } + assert(!mPackages.empty()); - package = mPackages.front(); + if (nearestDist < std::numeric_limits::max() && mPackages.begin() != itActualCombat) + { + // move combat package with nearest target to the front + mPackages.splice(mPackages.begin(), mPackages, itActualCombat); } + + package = mPackages.front(); } try From ca93f8ee39483cfb3fd4ee3f55527a65373a2b70 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 May 2020 17:38:10 +0200 Subject: [PATCH 112/227] Compare initialized iterator Comparsion of untilialized iterator is UB. Initialize with packages end. --- apps/openmw/mwmechanics/aisequence.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 201701634..269468090 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -223,7 +223,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac // if active package is combat one, choose nearest target if (packageTypeId == AiPackage::TypeIdCombat) { - std::list::iterator itActualCombat; + auto itActualCombat = mPackages.end(); float nearestDist = std::numeric_limits::max(); osg::Vec3f vActorPos = actor.getRefData().getPosition().asVec3(); @@ -269,6 +269,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac if (nearestDist < std::numeric_limits::max() && mPackages.begin() != itActualCombat) { + assert(itActualCombat != mPackages.end()); // move combat package with nearest target to the front mPackages.splice(mPackages.begin(), mPackages, itActualCombat); } From 3b5ce71d71fafb3a4a1d49b79ac209fae38bafd8 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 19:05:52 +0200 Subject: [PATCH 113/227] Remove redundant explicit dtor definition for AiPackage --- apps/openmw/mwmechanics/aipackage.cpp | 2 -- apps/openmw/mwmechanics/aipackage.hpp | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 114e011ce..dca882a3b 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -24,8 +24,6 @@ #include -MWMechanics::AiPackage::~AiPackage() {} - MWMechanics::AiPackage::AiPackage() : mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild mTargetActorRefId(""), diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index ec0715e52..a09362baa 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -56,8 +56,7 @@ namespace MWMechanics ///Default constructor AiPackage(); - ///Default Deconstructor - virtual ~AiPackage(); + virtual ~AiPackage() = default; ///Clones the package virtual AiPackage *clone() const = 0; From f566ab03ab253f396de906d0bf55f39e038546eb Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 18:56:02 +0200 Subject: [PATCH 114/227] Mark overriden AiPackage methods as final --- apps/openmw/mwmechanics/aiactivate.hpp | 10 +++++----- apps/openmw/mwmechanics/aiavoiddoor.hpp | 14 +++++++------- apps/openmw/mwmechanics/aibreathe.hpp | 14 +++++++------- apps/openmw/mwmechanics/aicast.hpp | 16 ++++++++-------- apps/openmw/mwmechanics/aicombat.hpp | 18 +++++++++--------- apps/openmw/mwmechanics/aiescort.hpp | 18 +++++++++--------- apps/openmw/mwmechanics/aiface.hpp | 14 +++++++------- apps/openmw/mwmechanics/aifollow.hpp | 22 +++++++++++----------- apps/openmw/mwmechanics/aipursue.hpp | 16 ++++++++-------- apps/openmw/mwmechanics/aitravel.hpp | 18 +++++++++--------- apps/openmw/mwmechanics/aiwander.hpp | 20 ++++++++++---------- 11 files changed, 90 insertions(+), 90 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 8de4be69f..4cc9f3036 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -19,7 +19,7 @@ namespace MWMechanics { /// \brief Causes actor to walk to activatable object and activate it /** Will activate when close to object **/ - class AiActivate : public AiPackage + class AiActivate final : public AiPackage { public: /// Constructor @@ -28,11 +28,11 @@ namespace MWMechanics AiActivate(const ESM::AiSequence::AiActivate* activate); - virtual AiActivate *clone() const; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); - virtual int getTypeId() const; + AiActivate *clone() const final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + int getTypeId() const final; - virtual void writeState(ESM::AiSequence::AiSequence& sequence) const; + void writeState(ESM::AiSequence::AiSequence& sequence) const final; private: std::string mObjectId; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index 4c8be29eb..39a78192b 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -16,22 +16,22 @@ namespace MWMechanics /// \brief AiPackage to have an actor avoid an opening door /** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it **/ - class AiAvoidDoor : public AiPackage + class AiAvoidDoor final : public AiPackage { public: /// Avoid door until the door is fully open AiAvoidDoor(const MWWorld::ConstPtr& doorPtr); - virtual AiAvoidDoor *clone() const; + AiAvoidDoor *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual unsigned int getPriority() const; + unsigned int getPriority() const final; - virtual bool canCancel() const { return false; } - virtual bool shouldCancelPreviousAi() const { return false; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } private: float mDuration; diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp index 263ab8c2b..daa2782c2 100644 --- a/apps/openmw/mwmechanics/aibreathe.hpp +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -7,21 +7,21 @@ namespace MWMechanics { /// \brief AiPackage to have an actor resurface to breathe // The AI will go up if lesser than half breath left - class AiBreathe : public AiPackage + class AiBreathe final : public AiPackage { public: AiBreathe(); - virtual AiBreathe *clone() const; + AiBreathe *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual unsigned int getPriority() const; + unsigned int getPriority() const final; - virtual bool canCancel() const { return false; } - virtual bool shouldCancelPreviousAi() const { return false; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } }; } #endif diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index 7128fe7a2..6b10370c6 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -11,22 +11,22 @@ namespace MWWorld namespace MWMechanics { /// AiPackage which makes an actor to cast given spell. - class AiCast : public AiPackage { + class AiCast final : public AiPackage { public: AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false); - virtual AiPackage *clone() const; + AiPackage *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual MWWorld::Ptr getTarget() const; + MWWorld::Ptr getTarget() const final; - virtual unsigned int getPriority() const; + unsigned int getPriority() const final; - virtual bool canCancel() const { return false; } - virtual bool shouldCancelPreviousAi() const { return false; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } private: std::string mTargetId; diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index f89e71678..049857e71 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -91,7 +91,7 @@ namespace MWMechanics }; /// \brief Causes the actor to fight another actor - class AiCombat : public AiPackage + class AiCombat final : public AiPackage { public: ///Constructor @@ -102,21 +102,21 @@ namespace MWMechanics void init(); - virtual AiCombat *clone() const; + AiCombat *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual unsigned int getPriority() const; + unsigned int getPriority() const final; ///Returns target ID - MWWorld::Ptr getTarget() const; + MWWorld::Ptr getTarget() const final; - virtual void writeState(ESM::AiSequence::AiSequence &sequence) const; + void writeState(ESM::AiSequence::AiSequence &sequence) const final; - virtual bool canCancel() const { return false; } - virtual bool shouldCancelPreviousAi() const { return false; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } private: /// Returns true if combat should end diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index e4319b425..5b49807a2 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -16,7 +16,7 @@ namespace AiSequence namespace MWMechanics { /// \brief AI Package to have an NPC lead the player to a specific point - class AiEscort : public AiPackage + class AiEscort final : public AiPackage { public: /// Implementation of AiEscort @@ -30,21 +30,21 @@ namespace MWMechanics AiEscort(const ESM::AiSequence::AiEscort* escort); - virtual AiEscort *clone() const; + AiEscort *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual bool useVariableSpeed() const { return true;} + bool useVariableSpeed() const final { return true; } - virtual bool sideWithTarget() const { return true; } + bool sideWithTarget() const final { return true; } - void writeState(ESM::AiSequence::AiSequence &sequence) const; + void writeState(ESM::AiSequence::AiSequence &sequence) const final; - void fastForward(const MWWorld::Ptr& actor, AiState& state); + void fastForward(const MWWorld::Ptr& actor, AiState& state) final; - virtual osg::Vec3f getDestination() const { return osg::Vec3f(mX, mY, mZ); } + osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } private: std::string mCellId; diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index 099e5d237..98d9ea04b 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -6,20 +6,20 @@ namespace MWMechanics { /// AiPackage which makes an actor face a certain direction. - class AiFace : public AiPackage { + class AiFace final : public AiPackage { public: AiFace(float targetX, float targetY); - virtual AiPackage *clone() const; + AiPackage *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual unsigned int getPriority() const; + unsigned int getPriority() const final; - virtual bool canCancel() const { return false; } - virtual bool shouldCancelPreviousAi() const { return false; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } private: float mTargetX, mTargetY; diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 24263bbc0..fc4b7fc0b 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -39,7 +39,7 @@ namespace MWMechanics /// \brief AiPackage for an actor to follow another actor/the PC /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely **/ - class AiFollow : public AiPackage + class AiFollow final : public AiPackage { public: AiFollow(const std::string &actorId, float duration, float x, float y, float z); @@ -53,30 +53,30 @@ namespace MWMechanics AiFollow(const ESM::AiSequence::AiFollow* follow); - virtual bool sideWithTarget() const { return true; } - virtual bool followTargetThroughDoors() const { return true; } - virtual bool shouldCancelPreviousAi() const { return !mCommanded; } + bool sideWithTarget() const final { return true; } + bool followTargetThroughDoors() const final { return true; } + bool shouldCancelPreviousAi() const final { return !mCommanded; } - virtual AiFollow *clone() const; + AiFollow *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual bool useVariableSpeed() const { return true;} + bool useVariableSpeed() const final { return true; } /// Returns the actor being followed std::string getFollowedActor(); - virtual void writeState (ESM::AiSequence::AiSequence& sequence) const; + void writeState (ESM::AiSequence::AiSequence& sequence) const final; bool isCommanded() const; int getFollowIndex() const; - void fastForward(const MWWorld::Ptr& actor, AiState& state); + void fastForward(const MWWorld::Ptr& actor, AiState& state) final; - virtual osg::Vec3f getDestination() const + osg::Vec3f getDestination() const final { MWWorld::Ptr target = getTarget(); if (target.isEmpty()) diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index ea83a10e5..3f2c2923e 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -17,7 +17,7 @@ namespace MWMechanics /** Used for arresting players. Causes the actor to run to the pursued actor and activate them, to arrest them. Note that while very similar to AiActivate, it will ONLY activate when evry close to target (Not also when the path is completed). **/ - class AiPursue : public AiPackage + class AiPursue final : public AiPackage { public: ///Constructor @@ -26,16 +26,16 @@ namespace MWMechanics AiPursue(const ESM::AiSequence::AiPursue* pursue); - virtual AiPursue *clone() const; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); - virtual int getTypeId() const; + AiPursue *clone() const final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + int getTypeId() const final; - MWWorld::Ptr getTarget() const; + MWWorld::Ptr getTarget() const final; - virtual void writeState (ESM::AiSequence::AiSequence& sequence) const; + void writeState (ESM::AiSequence::AiSequence& sequence) const final; - virtual bool canCancel() const { return false; } - virtual bool shouldCancelPreviousAi() const { return false; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } }; } #endif diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index e7895462f..43b6c9d16 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -14,7 +14,7 @@ namespace AiSequence namespace MWMechanics { /// \brief Causes the AI to travel to the specified point - class AiTravel : public AiPackage + class AiTravel final : public AiPackage { public: /// Default constructor @@ -22,21 +22,21 @@ namespace MWMechanics AiTravel(const ESM::AiSequence::AiTravel* travel); /// Simulates the passing of time - virtual void fastForward(const MWWorld::Ptr& actor, AiState& state); + void fastForward(const MWWorld::Ptr& actor, AiState& state) final; - void writeState(ESM::AiSequence::AiSequence &sequence) const; + void writeState(ESM::AiSequence::AiSequence &sequence) const final; - virtual AiTravel *clone() const; + AiTravel *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual bool useVariableSpeed() const { return true;} + bool useVariableSpeed() const final { return true; } - virtual bool alwaysActive() const { return true; } + bool alwaysActive() const final { return true; } - virtual osg::Vec3f getDestination() const { return osg::Vec3f(mX, mY, mZ); } + osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } private: float mX; diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 6e69b6c79..f6e7f6d0c 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -78,7 +78,7 @@ namespace MWMechanics }; /// \brief Causes the Actor to wander within a specified range - class AiWander : public AiPackage + class AiWander final : public AiPackage { public: /// Constructor @@ -91,23 +91,23 @@ namespace MWMechanics AiWander (const ESM::AiSequence::AiWander* wander); - virtual AiPackage *clone() const; + AiPackage *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual bool useVariableSpeed() const { return true;} + bool useVariableSpeed() const final { return true; } - virtual void writeState(ESM::AiSequence::AiSequence &sequence) const; + void writeState(ESM::AiSequence::AiSequence &sequence) const final; - virtual void fastForward(const MWWorld::Ptr& actor, AiState& state); + void fastForward(const MWWorld::Ptr& actor, AiState& state) final; - bool getRepeat() const; + bool getRepeat() const final; - osg::Vec3f getDestination(const MWWorld::Ptr& actor) const; + osg::Vec3f getDestination(const MWWorld::Ptr& actor) const final; - virtual osg::Vec3f getDestination() const + osg::Vec3f getDestination() const final { if (!mHasDestination) return osg::Vec3f(0, 0, 0); From d48eead0384b64ef878d3629556b662d55093c7a Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 May 2020 22:34:11 +0200 Subject: [PATCH 115/227] Check type id of current package If package is changed the following usage of it is not consistent. --- apps/openmw/mwmechanics/aisequence.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 269468090..d33a0a3f8 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -275,6 +275,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } package = mPackages.front(); + packageTypeId = package->getTypeId(); } try From 3875b837bcf2ff8a264916f045c1a03655e309a3 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 17 May 2020 22:34:54 +0200 Subject: [PATCH 116/227] make MenuMode, Random, GetSecondsPassed regular functions --- apps/openmw/mwscript/interpretercontext.cpp | 10 ----- apps/openmw/mwscript/interpretercontext.hpp | 4 -- apps/openmw/mwscript/miscextensions.cpp | 42 +++++++++++++++++++++ components/compiler/exprparser.cpp | 37 ------------------ components/compiler/extensions0.cpp | 3 ++ components/compiler/generator.cpp | 30 --------------- components/compiler/generator.hpp | 6 --- components/compiler/lineparser.cpp | 12 ------ components/compiler/opcodes.hpp | 3 ++ components/compiler/scanner.cpp | 3 -- components/compiler/scanner.hpp | 5 +-- components/compiler/stringparser.cpp | 4 +- components/interpreter/context.hpp | 4 -- components/interpreter/installopcodes.cpp | 3 -- components/interpreter/miscopcodes.hpp | 39 ------------------- 15 files changed, 50 insertions(+), 155 deletions(-) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index f9d85375b..62febb33d 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -192,11 +192,6 @@ namespace MWScript { } - bool InterpreterContext::menuMode() - { - return MWBase::Environment::get().getWindowManager()->isGuiMode(); - } - int InterpreterContext::getGlobalShort (const std::string& name) const { return MWBase::Environment::get().getWorld()->getGlobalInt (name); @@ -425,11 +420,6 @@ namespace MWScript } } - float InterpreterContext::getSecondsPassed() const - { - return MWBase::Environment::get().getFrameDuration(); - } - int InterpreterContext::getMemberShort (const std::string& id, const std::string& name, bool global) const { diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 7e9f09cdb..e7c2790ce 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -77,8 +77,6 @@ namespace MWScript virtual void report (const std::string& message); ///< By default, do nothing. - virtual bool menuMode(); - virtual int getGlobalShort (const std::string& name) const; virtual int getGlobalLong (const std::string& name) const; @@ -124,8 +122,6 @@ namespace MWScript void executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor); ///< Execute the activation action for this ptr. If ptr is mActivated, mark activation as handled. - virtual float getSecondsPassed() const; - virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const; virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 915b3221c..89bf44701 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include @@ -78,6 +80,33 @@ namespace MWScript { namespace Misc { + class OpMenuMode : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.push (MWBase::Environment::get().getWindowManager()->isGuiMode()); + } + }; + + class OpRandom : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + Interpreter::Type_Integer limit = runtime[0].mInteger; + runtime.pop(); + + if (limit<0) + throw std::runtime_error ( + "random: argument out of range (Don't be so negative!)"); + + runtime.push (static_cast(::Misc::Rng::rollDice(limit))); // [o, limit) + } + }; + template class OpStartScript : public Interpreter::Opcode0 { @@ -116,6 +145,16 @@ namespace MWScript } }; + class OpGetSecondsPassed : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.push (MWBase::Environment::get().getFrameDuration()); + } + }; + template class OpEnable : public Interpreter::Opcode0 { @@ -1530,10 +1569,13 @@ namespace MWScript void installOpcodes (Interpreter::Interpreter& interpreter) { + interpreter.installSegment5 (Compiler::Misc::opcodeMenuMode, new OpMenuMode); + interpreter.installSegment5 (Compiler::Misc::opcodeRandom, new OpRandom); interpreter.installSegment5 (Compiler::Misc::opcodeScriptRunning, new OpScriptRunning); interpreter.installSegment5 (Compiler::Misc::opcodeStartScript, new OpStartScript); interpreter.installSegment5 (Compiler::Misc::opcodeStartScriptExplicit, new OpStartScript); interpreter.installSegment5 (Compiler::Misc::opcodeStopScript, new OpStopScript); + interpreter.installSegment5 (Compiler::Misc::opcodeGetSecondsPassed, new OpGetSecondsPassed); interpreter.installSegment5 (Compiler::Misc::opcodeEnable, new OpEnable); interpreter.installSegment5 (Compiler::Misc::opcodeEnableExplicit, new OpEnable); interpreter.installSegment5 (Compiler::Misc::opcodeDisable, new OpDisable); diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 8e8f063bb..8c8fe640e 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -434,43 +434,6 @@ namespace Compiler mNextOperand = false; return true; } - else if (keyword==Scanner::K_menumode) - { - start(); - - mTokenLoc = loc; - - Generator::menuMode (mCode); - mOperands.push_back ('l'); - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_random) - { - start(); - - mTokenLoc = loc; - parseArguments ("l", scanner); - - Generator::random (mCode); - mOperands.push_back ('f'); - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_getsecondspassed) - { - start(); - - mTokenLoc = loc; - - Generator::getSecondsPassed (mCode); - mOperands.push_back ('f'); - - mNextOperand = false; - return true; - } else { // check for custom extensions diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 02a68d891..c67af29c2 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -241,9 +241,12 @@ namespace Compiler { void registerExtensions (Extensions& extensions) { + extensions.registerFunction ("menumode", 'l', "", opcodeMenuMode); + extensions.registerFunction ("random", 'f', "l", opcodeRandom); extensions.registerFunction ("scriptrunning", 'l', "c", opcodeScriptRunning); extensions.registerInstruction ("startscript", "c", opcodeStartScript, opcodeStartScriptExplicit); extensions.registerInstruction ("stopscript", "c", opcodeStopScript); + extensions.registerFunction ("getsecondspassed", 'f', "", opcodeGetSecondsPassed); extensions.registerInstruction ("enable", "", opcodeEnable, opcodeEnableExplicit); extensions.registerInstruction ("disable", "", opcodeDisable, opcodeDisableExplicit); extensions.registerFunction ("getdisabled", 'l', "x", opcodeGetDisabled, opcodeGetDisabledExplicit); diff --git a/components/compiler/generator.cpp b/components/compiler/generator.cpp index c16df660c..2787488c2 100644 --- a/components/compiler/generator.cpp +++ b/components/compiler/generator.cpp @@ -222,11 +222,6 @@ namespace code.push_back (Compiler::Generator::segment5 (37)); } - void opMenuMode (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (38)); - } - void opStoreGlobalShort (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (39)); @@ -286,16 +281,6 @@ namespace { code.push_back (Compiler::Generator::segment5 (global ? 70 : 64)); } - - void opRandom (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (45)); - } - - void opGetSecondsPassed (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (50)); - } } namespace Compiler @@ -590,11 +575,6 @@ namespace Compiler } } - void menuMode (CodeContainer& code) - { - opMenuMode (code); - } - void assignToGlobal (CodeContainer& code, Literals& literals, char localType, const std::string& name, const CodeContainer& value, char valueType) { @@ -751,15 +731,5 @@ namespace Compiler assert (0); } } - - void random (CodeContainer& code) - { - opRandom (code); - } - - void getSecondsPassed (CodeContainer& code) - { - opGetSecondsPassed (code); - } } } diff --git a/components/compiler/generator.hpp b/components/compiler/generator.hpp index f4386f605..55bba2a75 100644 --- a/components/compiler/generator.hpp +++ b/components/compiler/generator.hpp @@ -91,8 +91,6 @@ namespace Compiler void compare (CodeContainer& code, char op, char valueType1, char valueType2); - void menuMode (CodeContainer& code); - void assignToGlobal (CodeContainer& code, Literals& literals, char localType, const std::string& name, const CodeContainer& value, char valueType); @@ -106,10 +104,6 @@ namespace Compiler void fetchMember (CodeContainer& code, Literals& literals, char memberType, const std::string& name, const std::string& id, bool global); ///< \param global Member of a global script instead of a script of a reference. - - void random (CodeContainer& code); - - void getSecondsPassed (CodeContainer& code); } } diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index eaa833800..326b5f9f6 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -433,18 +433,6 @@ namespace Compiler return true; } - if (mAllowExpression) - { - if (keyword==Scanner::K_getsquareroot || keyword==Scanner::K_menumode || - keyword==Scanner::K_random || keyword==Scanner::K_getsecondspassed) - { - scanner.putbackKeyword (keyword, loc); - parseExpression (scanner, loc); - mState = EndState; - return true; - } - } - return Parser::parseKeyword (keyword, loc, scanner); } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 72e3cea89..363e8bb85 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -201,9 +201,12 @@ namespace Compiler namespace Misc { + const int opcodeMenuMode = 38; + const int opcodeRandom = 45; const int opcodeScriptRunning = 46; const int opcodeStartScript = 47; const int opcodeStopScript = 48; + const int opcodeGetSecondsPassed = 50; const int opcodeEnable = 51; const int opcodeDisable = 52; const int opcodeGetDisabled = 53; diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 2be679cd2..9f2865868 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -266,9 +266,6 @@ namespace Compiler "messagebox", "set", "to", "getsquareroot", - "menumode", - "random", - "getsecondspassed", 0 }; diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp index 973761898..b6321a92d 100644 --- a/components/compiler/scanner.hpp +++ b/components/compiler/scanner.hpp @@ -208,10 +208,7 @@ namespace Compiler K_return, K_messagebox, K_set, K_to, - K_getsquareroot, - K_menumode, - K_random, - K_getsecondspassed + K_getsquareroot }; enum special diff --git a/components/compiler/stringparser.cpp b/components/compiler/stringparser.cpp index a9974297d..1bacf7941 100644 --- a/components/compiler/stringparser.cpp +++ b/components/compiler/stringparser.cpp @@ -63,9 +63,7 @@ namespace Compiler keyword==Scanner::K_elseif || keyword==Scanner::K_while || keyword==Scanner::K_endwhile || keyword==Scanner::K_return || keyword==Scanner::K_messagebox || keyword==Scanner::K_set || - keyword==Scanner::K_to || keyword==Scanner::K_getsquareroot || - keyword==Scanner::K_menumode || keyword==Scanner::K_random || - keyword==Scanner::K_getsecondspassed) + keyword==Scanner::K_to || keyword==Scanner::K_getsquareroot) { return parseName (loc.mLiteral, loc, scanner); } diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 30744dcec..862018bdc 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -35,8 +35,6 @@ namespace Interpreter virtual void report (const std::string& message) = 0; - virtual bool menuMode() = 0; - virtual int getGlobalShort (const std::string& name) const = 0; virtual int getGlobalLong (const std::string& name) const = 0; @@ -79,8 +77,6 @@ namespace Interpreter virtual std::string getCurrentCellName() const = 0; - virtual float getSecondsPassed() const = 0; - virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const = 0; virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const = 0; diff --git a/components/interpreter/installopcodes.cpp b/components/interpreter/installopcodes.cpp index dfaabe325..afee36bc2 100644 --- a/components/interpreter/installopcodes.cpp +++ b/components/interpreter/installopcodes.cpp @@ -95,9 +95,6 @@ namespace Interpreter // misc interpreter.installSegment3 (0, new OpMessageBox); - interpreter.installSegment5 (38, new OpMenuMode); - interpreter.installSegment5 (45, new OpRandom); - interpreter.installSegment5 (50, new OpGetSecondsPassed); interpreter.installSegment5 (58, new OpReport); } } diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index e1b1843ef..07bd84ec9 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -11,7 +11,6 @@ #include "runtime.hpp" #include "defines.hpp" -#include #include namespace Interpreter @@ -168,44 +167,6 @@ namespace Interpreter } }; - class OpMenuMode : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - runtime.push (runtime.getContext().menuMode()); - } - }; - - class OpRandom : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - Type_Integer limit = runtime[0].mInteger; - - if (limit<0) - throw std::runtime_error ( - "random: argument out of range (Don't be so negative!)"); - - runtime[0].mFloat = static_cast(Misc::Rng::rollDice(limit)); // [o, limit) - } - }; - - class OpGetSecondsPassed : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - Type_Float duration = runtime.getContext().getSecondsPassed(); - - runtime.push (duration); - } - }; - } #endif From a4fbcb8a1092eb57664f4576e74f11bb92680ec2 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 May 2020 22:37:44 +0200 Subject: [PATCH 117/227] Remember package iterator to erase it from list without find call --- apps/openmw/mwmechanics/aisequence.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index d33a0a3f8..00d44202a 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -212,7 +212,8 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac return; } - MWMechanics::AiPackage* package = mPackages.front(); + auto packageIt = mPackages.begin(); + MWMechanics::AiPackage* package = *packageIt; if (!package->alwaysActive() && outOfRange) return; @@ -274,7 +275,8 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac mPackages.splice(mPackages.begin(), mPackages, itActualCombat); } - package = mPackages.front(); + packageIt = mPackages.begin(); + package = *packageIt; packageTypeId = package->getTypeId(); } @@ -290,9 +292,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } // To account for the rare case where AiPackage::execute() queued another AI package // (e.g. AiPursue executing a dialogue script that uses startCombat) - std::list::iterator toRemove = - std::find(mPackages.begin(), mPackages.end(), package); - mPackages.erase(toRemove); + mPackages.erase(packageIt); delete package; if (isActualAiPackage(packageTypeId)) mDone = true; From 087d12589af1582d0d3b814c8f11155bf2a92dcd Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Mon, 18 May 2020 16:37:18 +0100 Subject: [PATCH 118/227] Ignore VS Code files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 45c87a2c5..1a164592a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ files/windows/*.aps ## qt-creator CMakeLists.txt.user* .vs +.vscode ## resources data From 08e5d93c9bebeaa4930b8293f8f5dc8787d49b87 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Mon, 18 May 2020 17:36:07 +0100 Subject: [PATCH 119/227] Print MSVC activation info in verbose mode, too. --- CI/before_script.msvc.sh | 54 +++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 2f40aef9c..62f4ee4a3 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -988,34 +988,36 @@ RET=$? if [ -z $VERBOSE ]; then if [ $RET -eq 0 ]; then echo Done. - if [ -n $ACTIVATE_MSVC ]; then - echo - echo "Note: you must manually activate MSVC for the shell in which you want to do the build." - echo - echo "Some scripts have been created in the build directory to do so in an existing shell." - echo "Bash: source activate_msvc.sh" - echo "CMD: ActivateMSVC.bat" - echo "PowerShell: ActivateMSVC.ps1" - echo - echo "You may find options to launch a Development/Native Tools/Cross Tools shell in your start menu or Visual Studio." - echo - if [ $(uname -m) == 'x86_64' ]; then - if [ $BITS -eq 64 ]; then - inheritEnvironments=msvc_x64_x64 - else - inheritEnvironments=msvc_x64 - fi - else - if [ $BITS -eq 64 ]; then - inheritEnvironments=msvc_x86_x64 - else - inheritEnvironments=msvc_x86 - fi - fi - echo "In Visual Studio 15.3 (2017 Update 3) or later, try setting '\"inheritEnvironments\": [ \"$inheritEnvironments\" ]' in CMakeSettings.json to build in the IDE." - fi else echo Failed. fi fi + +if [ -n $ACTIVATE_MSVC ]; then + echo + echo "Note: you must manually activate MSVC for the shell in which you want to do the build." + echo + echo "Some scripts have been created in the build directory to do so in an existing shell." + echo "Bash: source activate_msvc.sh" + echo "CMD: ActivateMSVC.bat" + echo "PowerShell: ActivateMSVC.ps1" + echo + echo "You may find options to launch a Development/Native Tools/Cross Tools shell in your start menu or Visual Studio." + echo + if [ $(uname -m) == 'x86_64' ]; then + if [ $BITS -eq 64 ]; then + inheritEnvironments=msvc_x64_x64 + else + inheritEnvironments=msvc_x64 + fi + else + if [ $BITS -eq 64 ]; then + inheritEnvironments=msvc_x86_x64 + else + inheritEnvironments=msvc_x86 + fi + fi + echo "In Visual Studio 15.3 (2017 Update 3) or later, try setting '\"inheritEnvironments\": [ \"$inheritEnvironments\" ]' in CMakeSettings.json to build in the IDE." +fi + wrappedExit $RET From d7a920a04b1a82fd9d074eb99787164cc959d54c Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 13:45:02 +0200 Subject: [PATCH 120/227] Env variable to write OSG stats into file --- apps/openmw/engine.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 0fd6a1cb5..330d0e194 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -1,6 +1,7 @@ #include "engine.hpp" #include +#include #include @@ -738,6 +739,14 @@ void OMW::Engine::go() mEnvironment.getWindowManager()->executeInConsole(mStartupScript); } + std::ofstream stats; + if (const auto path = std::getenv("OPENMW_OSG_STATS_FILE")) + { + stats.open(path, std::ios_base::out); + if (!stats) + Log(Debug::Warning) << "Failed to open file for stats: " << path; + } + // Start the main rendering loop osg::Timer frameTimer; double simulationTime = 0.0; @@ -768,6 +777,12 @@ void OMW::Engine::go() simulationTime += dt; } + if (stats) + { + const auto frameNumber = mViewer->getFrameStamp()->getFrameNumber(); + mViewer->getViewerStats()->report(stats, frameNumber); + } + mEnvironment.limitFrameRate(frameTimer.time_s()); } From 103188b61dcb39221438bb0c309e2ab7a8160316 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 May 2020 22:10:36 +0200 Subject: [PATCH 121/227] Derive all AI package classes from template to support CRTP features --- apps/openmw/mwmechanics/aiactivate.hpp | 4 ++-- apps/openmw/mwmechanics/aiavoiddoor.cpp | 2 +- apps/openmw/mwmechanics/aiavoiddoor.hpp | 4 ++-- apps/openmw/mwmechanics/aibreathe.cpp | 6 ------ apps/openmw/mwmechanics/aibreathe.hpp | 6 ++---- apps/openmw/mwmechanics/aicast.hpp | 4 ++-- apps/openmw/mwmechanics/aicombat.hpp | 4 ++-- apps/openmw/mwmechanics/aiescort.hpp | 4 ++-- apps/openmw/mwmechanics/aiface.hpp | 4 ++-- apps/openmw/mwmechanics/aifollow.hpp | 4 ++-- apps/openmw/mwmechanics/aipursue.hpp | 4 ++-- apps/openmw/mwmechanics/aitravel.hpp | 4 ++-- apps/openmw/mwmechanics/aiwander.hpp | 4 ++-- apps/openmw/mwmechanics/typedaipackage.hpp | 14 ++++++++++++++ 14 files changed, 37 insertions(+), 31 deletions(-) create mode 100644 apps/openmw/mwmechanics/typedaipackage.hpp diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 4cc9f3036..cd7a8b422 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIACTIVATE_H #define GAME_MWMECHANICS_AIACTIVATE_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include @@ -19,7 +19,7 @@ namespace MWMechanics { /// \brief Causes actor to walk to activatable object and activate it /** Will activate when close to object **/ - class AiActivate final : public AiPackage + class AiActivate final : public TypedAiPackage { public: /// Constructor diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index c476c9b57..38ae98139 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -16,7 +16,7 @@ static const int MAX_DIRECTIONS = 4; MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr) -: AiPackage(), mDuration(1), mDoorPtr(doorPtr), mDirection(0) +: mDuration(1), mDoorPtr(doorPtr), mDirection(0) { } diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index 39a78192b..312870a2e 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H #define GAME_MWMECHANICS_AIAVOIDDOOR_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include @@ -16,7 +16,7 @@ namespace MWMechanics /// \brief AiPackage to have an actor avoid an opening door /** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it **/ - class AiAvoidDoor final : public AiPackage + class AiAvoidDoor final : public TypedAiPackage { public: /// Avoid door until the door is fully open diff --git a/apps/openmw/mwmechanics/aibreathe.cpp b/apps/openmw/mwmechanics/aibreathe.cpp index 4955f683c..a919808dd 100644 --- a/apps/openmw/mwmechanics/aibreathe.cpp +++ b/apps/openmw/mwmechanics/aibreathe.cpp @@ -11,12 +11,6 @@ #include "movement.hpp" #include "steering.hpp" -MWMechanics::AiBreathe::AiBreathe() -: AiPackage() -{ - -} - bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get().find("fHoldBreathTime")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp index daa2782c2..a9ab56c70 100644 --- a/apps/openmw/mwmechanics/aibreathe.hpp +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -1,17 +1,15 @@ #ifndef GAME_MWMECHANICS_AIBREATHE_H #define GAME_MWMECHANICS_AIBREATHE_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" namespace MWMechanics { /// \brief AiPackage to have an actor resurface to breathe // The AI will go up if lesser than half breath left - class AiBreathe final : public AiPackage + class AiBreathe final : public TypedAiPackage { public: - AiBreathe(); - AiBreathe *clone() const final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index 6b10370c6..26eca15ef 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AICAST_H #define GAME_MWMECHANICS_AICAST_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" namespace MWWorld { @@ -11,7 +11,7 @@ namespace MWWorld namespace MWMechanics { /// AiPackage which makes an actor to cast given spell. - class AiCast final : public AiPackage { + class AiCast final : public TypedAiPackage { public: AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false); diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 049857e71..610e402e4 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AICOMBAT_H #define GAME_MWMECHANICS_AICOMBAT_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include "../mwworld/cellstore.hpp" // for Doors @@ -91,7 +91,7 @@ namespace MWMechanics }; /// \brief Causes the actor to fight another actor - class AiCombat final : public AiPackage + class AiCombat final : public TypedAiPackage { public: ///Constructor diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 5b49807a2..651abb2dd 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIESCORT_H #define GAME_MWMECHANICS_AIESCORT_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include @@ -16,7 +16,7 @@ namespace AiSequence namespace MWMechanics { /// \brief AI Package to have an NPC lead the player to a specific point - class AiEscort final : public AiPackage + class AiEscort final : public TypedAiPackage { public: /// Implementation of AiEscort diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index 98d9ea04b..9defaf2f2 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -1,12 +1,12 @@ #ifndef GAME_MWMECHANICS_AIFACE_H #define GAME_MWMECHANICS_AIFACE_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" namespace MWMechanics { /// AiPackage which makes an actor face a certain direction. - class AiFace final : public AiPackage { + class AiFace final : public TypedAiPackage { public: AiFace(float targetX, float targetY); diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index fc4b7fc0b..3d22af9b1 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIFOLLOW_H #define GAME_MWMECHANICS_AIFOLLOW_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include @@ -39,7 +39,7 @@ namespace MWMechanics /// \brief AiPackage for an actor to follow another actor/the PC /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely **/ - class AiFollow final : public AiPackage + class AiFollow final : public TypedAiPackage { public: AiFollow(const std::string &actorId, float duration, float x, float y, float z); diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 3f2c2923e..5f1976dd5 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIPURSUE_H #define GAME_MWMECHANICS_AIPURSUE_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" namespace ESM { @@ -17,7 +17,7 @@ namespace MWMechanics /** Used for arresting players. Causes the actor to run to the pursued actor and activate them, to arrest them. Note that while very similar to AiActivate, it will ONLY activate when evry close to target (Not also when the path is completed). **/ - class AiPursue final : public AiPackage + class AiPursue final : public TypedAiPackage { public: ///Constructor diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 43b6c9d16..c1420987a 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AITRAVEL_H #define GAME_MWMECHANICS_AITRAVEL_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" namespace ESM { @@ -14,7 +14,7 @@ namespace AiSequence namespace MWMechanics { /// \brief Causes the AI to travel to the specified point - class AiTravel final : public AiPackage + class AiTravel final : public TypedAiPackage { public: /// Default constructor diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index f6e7f6d0c..f887fae67 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIWANDER_H #define GAME_MWMECHANICS_AIWANDER_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include @@ -78,7 +78,7 @@ namespace MWMechanics }; /// \brief Causes the Actor to wander within a specified range - class AiWander final : public AiPackage + class AiWander final : public TypedAiPackage { public: /// Constructor diff --git a/apps/openmw/mwmechanics/typedaipackage.hpp b/apps/openmw/mwmechanics/typedaipackage.hpp new file mode 100644 index 000000000..a298bce71 --- /dev/null +++ b/apps/openmw/mwmechanics/typedaipackage.hpp @@ -0,0 +1,14 @@ +#ifndef GAME_MWMECHANICS_TYPEDAIPACKAGE_H +#define GAME_MWMECHANICS_TYPEDAIPACKAGE_H + +#include "aipackage.hpp" + +namespace MWMechanics +{ + template + struct TypedAiPackage : public AiPackage + { + }; +} + +#endif From 8e0934cbd89f6f8aa5091e00bc2ef9917dc98f38 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 May 2020 22:00:44 +0200 Subject: [PATCH 122/227] Single AI package clone definition --- apps/openmw/mwmechanics/aiactivate.cpp | 5 ----- apps/openmw/mwmechanics/aiactivate.hpp | 1 - apps/openmw/mwmechanics/aiavoiddoor.cpp | 5 ----- apps/openmw/mwmechanics/aiavoiddoor.hpp | 2 -- apps/openmw/mwmechanics/aibreathe.cpp | 5 ----- apps/openmw/mwmechanics/aibreathe.hpp | 2 -- apps/openmw/mwmechanics/aicast.cpp | 5 ----- apps/openmw/mwmechanics/aicast.hpp | 2 -- apps/openmw/mwmechanics/aicombat.cpp | 5 ----- apps/openmw/mwmechanics/aicombat.hpp | 2 -- apps/openmw/mwmechanics/aiescort.cpp | 6 ------ apps/openmw/mwmechanics/aiescort.hpp | 2 -- apps/openmw/mwmechanics/aiface.cpp | 5 ----- apps/openmw/mwmechanics/aiface.hpp | 2 -- apps/openmw/mwmechanics/aifollow.cpp | 5 ----- apps/openmw/mwmechanics/aifollow.hpp | 2 -- apps/openmw/mwmechanics/aipursue.cpp | 4 ---- apps/openmw/mwmechanics/aipursue.hpp | 1 - apps/openmw/mwmechanics/aitravel.cpp | 5 ----- apps/openmw/mwmechanics/aitravel.hpp | 2 -- apps/openmw/mwmechanics/aiwander.cpp | 5 ----- apps/openmw/mwmechanics/aiwander.hpp | 2 -- apps/openmw/mwmechanics/typedaipackage.hpp | 4 ++++ 23 files changed, 4 insertions(+), 75 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index c412a7ea1..6764eba23 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -18,11 +18,6 @@ namespace MWMechanics { } - AiActivate *MWMechanics::AiActivate::clone() const - { - return new AiActivate(*this); - } - bool AiActivate::execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index cd7a8b422..5a9e3d6d8 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -28,7 +28,6 @@ namespace MWMechanics AiActivate(const ESM::AiSequence::AiActivate* activate); - AiActivate *clone() const final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index 38ae98139..9cdb8d90b 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -72,11 +72,6 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont return false; } -MWMechanics::AiAvoidDoor *MWMechanics::AiAvoidDoor::clone() const -{ - return new AiAvoidDoor(*this); -} - int MWMechanics::AiAvoidDoor::getTypeId() const { return TypeIdAvoidDoor; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index 312870a2e..cc02c4de1 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -22,8 +22,6 @@ namespace MWMechanics /// Avoid door until the door is fully open AiAvoidDoor(const MWWorld::ConstPtr& doorPtr); - AiAvoidDoor *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aibreathe.cpp b/apps/openmw/mwmechanics/aibreathe.cpp index a919808dd..5cb81b3d8 100644 --- a/apps/openmw/mwmechanics/aibreathe.cpp +++ b/apps/openmw/mwmechanics/aibreathe.cpp @@ -32,11 +32,6 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro return true; } -MWMechanics::AiBreathe *MWMechanics::AiBreathe::clone() const -{ - return new AiBreathe(*this); -} - int MWMechanics::AiBreathe::getTypeId() const { return TypeIdBreathe; diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp index a9ab56c70..6e3bb912a 100644 --- a/apps/openmw/mwmechanics/aibreathe.hpp +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -10,8 +10,6 @@ namespace MWMechanics class AiBreathe final : public TypedAiPackage { public: - AiBreathe *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aicast.cpp b/apps/openmw/mwmechanics/aicast.cpp index b7c9bac3b..de61851cd 100644 --- a/apps/openmw/mwmechanics/aicast.cpp +++ b/apps/openmw/mwmechanics/aicast.cpp @@ -18,11 +18,6 @@ MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spel mDistance = action.getCombatRange(isRanged); } -MWMechanics::AiPackage *MWMechanics::AiCast::clone() const -{ - return new AiCast(*this); -} - bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration) { MWWorld::Ptr target; diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index 26eca15ef..21b629f24 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -15,8 +15,6 @@ namespace MWMechanics public: AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false); - AiPackage *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index ee1b9cecd..4a3c7aee6 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -421,11 +421,6 @@ namespace MWMechanics return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); } - AiCombat *MWMechanics::AiCombat::clone() const - { - return new AiCombat(*this); - } - void AiCombat::writeState(ESM::AiSequence::AiSequence &sequence) const { std::unique_ptr combat(new ESM::AiSequence::AiCombat()); diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 610e402e4..2ef0298fc 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -102,8 +102,6 @@ namespace MWMechanics void init(); - AiCombat *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 03951d279..5e1d38331 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -55,12 +55,6 @@ namespace MWMechanics mDuration = 0; } - - AiEscort *MWMechanics::AiEscort::clone() const - { - return new AiEscort(*this); - } - bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { // If AiEscort has ran for as long or longer then the duration specified diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 651abb2dd..9f5ac2f42 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -30,8 +30,6 @@ namespace MWMechanics AiEscort(const ESM::AiSequence::AiEscort* escort); - AiEscort *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aiface.cpp b/apps/openmw/mwmechanics/aiface.cpp index b99a8c1f4..0bfd00c87 100644 --- a/apps/openmw/mwmechanics/aiface.cpp +++ b/apps/openmw/mwmechanics/aiface.cpp @@ -9,11 +9,6 @@ MWMechanics::AiFace::AiFace(float targetX, float targetY) { } -MWMechanics::AiPackage *MWMechanics::AiFace::clone() const -{ - return new AiFace(*this); -} - bool MWMechanics::AiFace::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& /*characterController*/, MWMechanics::AiState& /*state*/, float /*duration*/) { osg::Vec3f dir = osg::Vec3f(mTargetX, mTargetY, 0) - actor.getRefData().getPosition().asVec3(); diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index 9defaf2f2..ce1c9847b 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -10,8 +10,6 @@ namespace MWMechanics public: AiFace(float targetX, float targetY); - AiPackage *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 09e8d0ecd..bffa238d5 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -201,11 +201,6 @@ std::string AiFollow::getFollowedActor() return mTargetActorRefId; } -AiFollow *MWMechanics::AiFollow::clone() const -{ - return new AiFollow(*this); -} - int AiFollow::getTypeId() const { return TypeIdFollow; diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 3d22af9b1..39a10294b 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -57,8 +57,6 @@ namespace MWMechanics bool followTargetThroughDoors() const final { return true; } bool shouldCancelPreviousAi() const final { return !mCommanded; } - AiFollow *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 52b2866bb..76b4ac0c9 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -27,10 +27,6 @@ AiPursue::AiPursue(const ESM::AiSequence::AiPursue *pursue) mTargetActorId = pursue->mTargetActorId; } -AiPursue *MWMechanics::AiPursue::clone() const -{ - return new AiPursue(*this); -} bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { if(actor.getClass().getCreatureStats(actor).isDead()) diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 5f1976dd5..6898a8dd3 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -26,7 +26,6 @@ namespace MWMechanics AiPursue(const ESM::AiSequence::AiPursue* pursue); - AiPursue *clone() const final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index dba70316b..94c782c02 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -36,11 +36,6 @@ namespace MWMechanics { } - AiTravel *MWMechanics::AiTravel::clone() const - { - return new AiTravel(*this); - } - bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { auto& stats = actor.getClass().getCreatureStats(actor); diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index c1420987a..beaf2819f 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -26,8 +26,6 @@ namespace MWMechanics void writeState(ESM::AiSequence::AiSequence &sequence) const final; - AiTravel *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 597453409..158a62c1d 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -115,11 +115,6 @@ namespace MWMechanics mDuration = 0; } - AiPackage * MWMechanics::AiWander::clone() const - { - return new AiWander(*this); - } - /* * AiWander high level states (0.29.0). Not entirely accurate in some cases * e.g. non-NPC actors do not greet and some creatures may be moving even in diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index f887fae67..bb5872eef 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -91,8 +91,6 @@ namespace MWMechanics AiWander (const ESM::AiSequence::AiWander* wander); - AiPackage *clone() const final; - bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/typedaipackage.hpp b/apps/openmw/mwmechanics/typedaipackage.hpp index a298bce71..2801accc4 100644 --- a/apps/openmw/mwmechanics/typedaipackage.hpp +++ b/apps/openmw/mwmechanics/typedaipackage.hpp @@ -8,6 +8,10 @@ namespace MWMechanics template struct TypedAiPackage : public AiPackage { + virtual TypedAiPackage *clone() const override + { + return new T(*static_cast(this)); + } }; } From a59e25e093544b1d544a0e78c73a35d26f4083b5 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 21 May 2020 15:24:06 +0200 Subject: [PATCH 123/227] Optimize MWRender::Animation::hasAnimation Use a set to check for group start existence. Reduce time taken from 2.6% to 0.08% and MWMechanics::MechanicsManager::update from 7% to 5% in relative CPU time usage for a scene with ~100 actors. --- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/character.hpp | 3 +- apps/openmw/mwrender/animation.cpp | 50 ++++++--------- apps/openmw/mwrender/animation.hpp | 11 ++-- components/nifosg/nifloader.cpp | 3 +- components/nifosg/nifloader.hpp | 3 +- components/nifosg/textkeymap.hpp | 87 +++++++++++++++++++++++++++ 7 files changed, 117 insertions(+), 42 deletions(-) create mode 100644 components/nifosg/textkeymap.hpp diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 92eec1af6..ac8d417f3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -942,7 +942,7 @@ void split(const std::string &s, char delim, std::vector &elems) { } } -void CharacterController::handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, const std::multimap &map) +void CharacterController::handleTextKey(const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, const NifOsg::TextKeyMap& map) { const std::string &evt = key->second; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 614beca1c..aa3c035ba 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -238,8 +238,7 @@ public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); - virtual void handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, - const std::multimap& map); + virtual void handleTextKey(const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, const NifOsg::TextKeyMap& map); // Be careful when to call this, see comment in Actors void updateContinuousVfx(); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8f3641795..77157377b 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -151,20 +151,8 @@ namespace } }; - NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname) - { - NifOsg::TextKeyMap::const_iterator iter(keys.begin()); - for(;iter != keys.end();++iter) - { - if(iter->second.compare(0, groupname.size(), groupname) == 0 && - iter->second.compare(groupname.size(), 2, ": ") == 0) - break; - } - return iter; - } - - float calcAnimVelocity(const std::multimap& keys, - NifOsg::KeyframeController *nonaccumctrl, const osg::Vec3f& accum, const std::string &groupname) + float calcAnimVelocity(const NifOsg::TextKeyMap& keys, NifOsg::KeyframeController *nonaccumctrl, + const osg::Vec3f& accum, const std::string &groupname) { const std::string start = groupname+": start"; const std::string loopstart = groupname+": loop start"; @@ -179,7 +167,7 @@ namespace // but the animation velocity calculation uses the second one. // As result the animation velocity calculation is not correct, and this incorrect velocity must be replicated, // because otherwise the Creature's Speed (dagoth uthol) would not be sufficient to move fast enough. - NifOsg::TextKeyMap::const_reverse_iterator keyiter(keys.rbegin()); + auto keyiter = keys.rbegin(); while(keyiter != keys.rend()) { if(keyiter->second == start || keyiter->second == loopstart) @@ -553,7 +541,7 @@ namespace MWRender ControllerMap mControllerMap[Animation::sNumBlendMasks]; - const std::multimap& getTextKeys() const; + const NifOsg::TextKeyMap& getTextKeys() const; }; void UpdateVfxCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) @@ -702,7 +690,7 @@ namespace MWRender return 0; } - const std::multimap &Animation::AnimSource::getTextKeys() const + const NifOsg::TextKeyMap &Animation::AnimSource::getTextKeys() const { return mKeyframes->mTextKeys; } @@ -825,7 +813,7 @@ namespace MWRender for(;iter != mAnimSources.end();++iter) { const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); - if(findGroupStart(keys, anim) != keys.end()) + if (keys.hasGroupStart(anim)) return true; } @@ -838,7 +826,7 @@ namespace MWRender { const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); - NifOsg::TextKeyMap::const_iterator found = findGroupStart(keys, groupname); + const auto found = keys.findGroupStart(groupname); if(found != keys.end()) return found->first; } @@ -851,7 +839,7 @@ namespace MWRender { const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); - for(NifOsg::TextKeyMap::const_iterator iterKey(keys.begin()); iterKey != keys.end(); ++iterKey) + for(auto iterKey = keys.begin(); iterKey != keys.end(); ++iterKey) { if(iterKey->second.compare(0, textKey.size(), textKey) == 0) return iterKey->first; @@ -861,8 +849,8 @@ namespace MWRender return -1.f; } - void Animation::handleTextKey(AnimState &state, const std::string &groupname, const std::multimap::const_iterator &key, - const std::multimap& map) + void Animation::handleTextKey(AnimState &state, const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, + const NifOsg::TextKeyMap& map) { const std::string &evt = key->second; @@ -939,7 +927,7 @@ namespace MWRender if (state.mPlaying) { - NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.getTime())); + auto textkey = textkeys.lowerBound(state.getTime()); while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, groupname, textkey, textkeys); @@ -955,7 +943,7 @@ namespace MWRender if(state.getTime() >= state.mLoopStopTime) break; - NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.getTime())); + auto textkey = textkeys.lowerBound(state.getTime()); while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, groupname, textkey, textkeys); @@ -974,7 +962,7 @@ namespace MWRender { // Look for text keys in reverse. This normally wouldn't matter, but for some reason undeadwolf_2.nif has two // separate walkforward keys, and the last one is supposed to be used. - NifOsg::TextKeyMap::const_reverse_iterator groupend(keys.rbegin()); + auto groupend = keys.rbegin(); for(;groupend != keys.rend();++groupend) { if(groupend->second.compare(0, groupname.size(), groupname) == 0 && @@ -983,7 +971,7 @@ namespace MWRender } std::string starttag = groupname+": "+start; - NifOsg::TextKeyMap::const_reverse_iterator startkey(groupend); + auto startkey = groupend; while(startkey != keys.rend() && startkey->second != starttag) ++startkey; if(startkey == keys.rend() && start == "loop start") @@ -997,7 +985,7 @@ namespace MWRender return false; const std::string stoptag = groupname+": "+stop; - NifOsg::TextKeyMap::const_reverse_iterator stopkey(groupend); + auto stopkey = groupend; while(stopkey != keys.rend() // We have to ignore extra garbage at the end. // The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop". @@ -1030,7 +1018,7 @@ namespace MWRender const std::string loopstarttag = groupname+": loop start"; const std::string loopstoptag = groupname+": loop stop"; - NifOsg::TextKeyMap::const_reverse_iterator key(groupend); + auto key = groupend; for (; key != startkey && key != keys.rend(); ++key) { if (key->first > state.getTime()) @@ -1201,7 +1189,7 @@ namespace MWRender for(;animsrc != mAnimSources.rend();++animsrc) { const NifOsg::TextKeyMap &keys = (*animsrc)->getTextKeys(); - if(findGroupStart(keys, groupname) != keys.end()) + if (keys.hasGroupStart(groupname)) break; } if(animsrc == mAnimSources.rend()) @@ -1280,7 +1268,7 @@ namespace MWRender } const NifOsg::TextKeyMap &textkeys = state.mSource->getTextKeys(); - NifOsg::TextKeyMap::const_iterator textkey(textkeys.upper_bound(state.getTime())); + auto textkey = textkeys.upperBound(state.getTime()); float timepassed = duration * state.mSpeedMult; while(state.mPlaying) @@ -1316,7 +1304,7 @@ namespace MWRender state.setTime(state.mLoopStartTime); state.mPlaying = true; - textkey = textkeys.lower_bound(state.getTime()); + textkey = textkeys.lowerBound(state.getTime()); while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, stateiter->first, textkey, textkeys); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 7b1e1d3e9..dddde7d9a 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -5,6 +5,7 @@ #include #include +#include namespace ESM { @@ -147,8 +148,8 @@ public: class TextKeyListener { public: - virtual void handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, - const std::multimap& map) = 0; + virtual void handleTextKey(const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, + const NifOsg::TextKeyMap& map) = 0; virtual ~TextKeyListener() = default; }; @@ -296,12 +297,12 @@ protected: * the marker is not found, or if the markers are the same, it returns * false. */ - bool reset(AnimState &state, const std::multimap &keys, + bool reset(AnimState &state, const NifOsg::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, bool loopfallback); - void handleTextKey(AnimState &state, const std::string &groupname, const std::multimap::const_iterator &key, - const std::multimap& map); + void handleTextKey(AnimState &state, const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, + const NifOsg::TextKeyMap& map); /** Sets the root model of the object. * diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 521764274..e3c6d823f 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -157,7 +157,8 @@ namespace nextpos = std::distance(str.begin(), ++last); } std::string result = str.substr(pos, nextpos-pos); - textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::lowerCase(result))); + Misc::StringUtils::lowerCaseInPlace(result); + textkeys.emplace(tk->list[i].time, std::move(result)); pos = nextpos; } diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index 4de9027b8..49a78ad5f 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -7,6 +7,7 @@ #include #include "controller.hpp" +#include "textkeymap.hpp" namespace osg { @@ -20,8 +21,6 @@ namespace Resource namespace NifOsg { - typedef std::multimap TextKeyMap; - struct TextKeyMapHolder : public osg::Object { public: diff --git a/components/nifosg/textkeymap.hpp b/components/nifosg/textkeymap.hpp new file mode 100644 index 000000000..49e1e461e --- /dev/null +++ b/components/nifosg/textkeymap.hpp @@ -0,0 +1,87 @@ +#ifndef OPENMW_COMPONENTS_NIFOSG_TEXTKEYMAP +#define OPENMW_COMPONENTS_NIFOSG_TEXTKEYMAP + +#include +#include +#include +#include + +namespace NifOsg +{ + class TextKeyMap + { + public: + using ConstIterator = std::multimap::const_iterator; + + auto begin() const noexcept + { + return mTextKeyByTime.begin(); + } + + auto end() const noexcept + { + return mTextKeyByTime.end(); + } + + auto rbegin() const noexcept + { + return mTextKeyByTime.rbegin(); + } + + auto rend() const noexcept + { + return mTextKeyByTime.rend(); + } + + auto lowerBound(float time) const + { + return mTextKeyByTime.lower_bound(time); + } + + auto upperBound(float time) const + { + return mTextKeyByTime.upper_bound(time); + } + + void emplace(float time, std::string&& textKey) + { + const auto separator = textKey.find(": "); + if (separator != std::string::npos) + mGroups.emplace(textKey.substr(0, separator)); + + mTextKeyByTime.emplace(time, std::move(textKey)); + } + + bool empty() const noexcept + { + return mTextKeyByTime.empty(); + } + + auto findGroupStart(const std::string &groupName) const + { + return std::find_if(mTextKeyByTime.begin(), mTextKeyByTime.end(), IsGroupStart{groupName}); + } + + bool hasGroupStart(const std::string &groupName) const + { + return mGroups.count(groupName) > 0; + } + + private: + struct IsGroupStart + { + const std::string &mGroupName; + + bool operator ()(const std::multimap::value_type& value) const + { + return value.second.compare(0, mGroupName.size(), mGroupName) == 0 && + value.second.compare(mGroupName.size(), 2, ": ") == 0; + } + }; + + std::set mGroups; + std::multimap mTextKeyByTime; + }; +} + +#endif From 9326111f4c10c278888fb402092a7de131f0eefa Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 21 May 2020 17:23:32 +0200 Subject: [PATCH 124/227] Use vector for Animation::mActiveControllers Container is only used to add elements, iterate over all of them and clear. Multimap adds overhead for all of these operations without any benefits. Reduce Animation::resetActiveGroups CPU time usage by 50%. --- apps/openmw/mwrender/animation.cpp | 8 ++++---- apps/openmw/mwrender/animation.hpp | 5 +++-- apps/openmw/mwrender/npcanimation.cpp | 2 +- apps/openmw/mwrender/weaponanimation.cpp | 4 ++-- apps/openmw/mwrender/weaponanimation.hpp | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8f3641795..aad9458d4 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1064,7 +1064,7 @@ namespace MWRender void Animation::resetActiveGroups() { // remove all previous external controllers from the scene graph - for (ControllerMap::iterator it = mActiveControllers.begin(); it != mActiveControllers.end(); ++it) + for (auto it = mActiveControllers.begin(); it != mActiveControllers.end(); ++it) { osg::Node* node = it->first; node->removeUpdateCallback(it->second); @@ -1103,7 +1103,7 @@ namespace MWRender osg::ref_ptr node = getNodeMap().at(it->first); // this should not throw, we already checked for the node existing in addAnimSource node->addUpdateCallback(it->second); - mActiveControllers.insert(std::make_pair(node, it->second)); + mActiveControllers.emplace_back(node, it->second); if (blendMask == 0 && node == mAccumRoot) { @@ -1116,7 +1116,7 @@ namespace MWRender mResetAccumRootCallback->setAccumulate(mAccumulate); } mAccumRoot->addUpdateCallback(mResetAccumRootCallback); - mActiveControllers.insert(std::make_pair(mAccumRoot, mResetAccumRootCallback)); + mActiveControllers.emplace_back(mAccumRoot, mResetAccumRootCallback); } } } @@ -1850,7 +1850,7 @@ namespace MWRender { mHeadController = new RotateController(mObjectRoot.get()); node->addUpdateCallback(mHeadController); - mActiveControllers.insert(std::make_pair(node, mHeadController)); + mActiveControllers.emplace_back(node, mHeadController); } } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 7b1e1d3e9..ad10a5575 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -6,6 +6,8 @@ #include #include +#include + namespace ESM { struct Light; @@ -246,8 +248,7 @@ protected: // Keep track of controllers that we added to our scene graph. // We may need to rebuild these controllers when the active animation groups / sources change. - typedef std::multimap, osg::ref_ptr > ControllerMap; - ControllerMap mActiveControllers; + std::vector, osg::ref_ptr>> mActiveControllers; std::shared_ptr mAnimationTimePtr[sNumBlendMasks]; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 41fadd98e..ec825ca2f 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -956,7 +956,7 @@ void NpcAnimation::addControllers() osg::MatrixTransform* node = found->second.get(); mFirstPersonNeckController = new NeckController(mObjectRoot.get()); node->addUpdateCallback(mFirstPersonNeckController); - mActiveControllers.emplace(node, mFirstPersonNeckController); + mActiveControllers.emplace_back(node, mFirstPersonNeckController); } } else if (mViewMode == VM_Normal) diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 24c92bc32..2af5fdb41 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -168,7 +168,7 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) } void WeaponAnimation::addControllers(const std::map >& nodes, - std::multimap, osg::ref_ptr > &map, osg::Node* objectRoot) + std::vector, osg::ref_ptr>> &map, osg::Node* objectRoot) { for (int i=0; i<2; ++i) { @@ -180,7 +180,7 @@ void WeaponAnimation::addControllers(const std::mapsecond; mSpineControllers[i] = new RotateController(objectRoot); node->addUpdateCallback(mSpineControllers[i]); - map.insert(std::make_pair(node, mSpineControllers[i])); + map.emplace_back(node, mSpineControllers[i]); } } } diff --git a/apps/openmw/mwrender/weaponanimation.hpp b/apps/openmw/mwrender/weaponanimation.hpp index ece0beaa6..a1988703c 100644 --- a/apps/openmw/mwrender/weaponanimation.hpp +++ b/apps/openmw/mwrender/weaponanimation.hpp @@ -41,7 +41,7 @@ namespace MWRender /// Add WeaponAnimation-related controllers to \a nodes and store the added controllers in \a map. void addControllers(const std::map >& nodes, - std::multimap, osg::ref_ptr >& map, osg::Node* objectRoot); + std::vector, osg::ref_ptr>>& map, osg::Node* objectRoot); void deleteControllers(); From 904b245d30df0a7895af913ac79a656703274f24 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 22 May 2020 14:26:02 +0300 Subject: [PATCH 125/227] Re-enable non-biped creature headtracking (bug #5424) --- CHANGELOG.md | 1 + apps/openmw/mwrender/animation.cpp | 43 ++++++++++++++---------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 138ef1707..3fc9e827c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Bug #5400: Editor: Verifier checks race of non-skin bodyparts Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work Bug #5416: Junk non-node records before the root node are not handled gracefully + Bug #5424: Creatures do not headtrack player Feature #5362: Show the soul gems' trapped soul in count dialog 0.46.0 diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index df2000221..8f8e8c233 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1815,33 +1815,30 @@ namespace MWRender { mHeadController = nullptr; - if (mPtr.getClass().isBipedal(mPtr)) - { - NodeMap::const_iterator found = getNodeMap().find("bip01 head"); - if (found != getNodeMap().end()) - { - osg::MatrixTransform* node = found->second; + NodeMap::const_iterator found = getNodeMap().find("bip01 head"); + if (found == getNodeMap().end()) + return; - bool foundKeyframeCtrl = false; - osg::Callback* cb = node->getUpdateCallback(); - while (cb) - { - if (dynamic_cast(cb)) - { - foundKeyframeCtrl = true; - break; - } - cb = cb->getNestedCallback(); - } + osg::MatrixTransform* node = found->second; - if (foundKeyframeCtrl) - { - mHeadController = new RotateController(mObjectRoot.get()); - node->addUpdateCallback(mHeadController); - mActiveControllers.emplace_back(node, mHeadController); - } + bool foundKeyframeCtrl = false; + osg::Callback* cb = node->getUpdateCallback(); + while (cb) + { + if (dynamic_cast(cb)) + { + foundKeyframeCtrl = true; + break; } + cb = cb->getNestedCallback(); } + + if (!foundKeyframeCtrl) + return; + + mHeadController = new RotateController(mObjectRoot.get()); + node->addUpdateCallback(mHeadController); + mActiveControllers.emplace_back(node, mHeadController); } void Animation::setHeadPitch(float pitchRadians) From c9c9599ec54c3e06947e859d9e2227fe5009750e Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 22 May 2020 14:04:26 +0300 Subject: [PATCH 126/227] Improve GetDistance and object search warnings (bug #5427) Allow GetDistance to work with a non-existent object argument or with inventory items that belong to a container store that doesn't exist --- CHANGELOG.md | 1 + .../mwscript/transformationextensions.cpp | 20 ++++++++++++++++--- apps/openmw/mwworld/worldimp.cpp | 5 ++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fc9e827c..285301a1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work Bug #5416: Junk non-node records before the root node are not handled gracefully Bug #5424: Creatures do not headtrack player + Bug #5427: GetDistance unknown ID error is misleading Feature #5362: Show the soul gems' trapped soul in count dialog 0.46.0 diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 3af54b394..791d054f3 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -46,10 +46,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { + MWWorld::Ptr from = R()(runtime); std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWWorld::Ptr from = R()(runtime); if (from.getContainerStore()) // is the object contained? { MWWorld::Ptr container = MWBase::Environment::get().getWorld()->findContainer(from); @@ -57,10 +57,24 @@ namespace MWScript if (!container.isEmpty()) from = container; else - throw std::runtime_error("failed to find container ptr"); + { + std::string error = "Failed to find the container of object '" + from.getCellRef().getRefId() + "'"; + runtime.getContext().report(error); + Log(Debug::Error) << error; + runtime.push(0.f); + return; + } } - const MWWorld::Ptr to = MWBase::Environment::get().getWorld()->getPtr(name, false); + const MWWorld::Ptr to = MWBase::Environment::get().getWorld()->searchPtr(name, false); + if (to.isEmpty()) + { + std::string error = "Failed to find an instance of object '" + name + "'"; + runtime.getContext().report(error); + Log(Debug::Error) << error; + runtime.push(0.f); + return; + } float distance; // If the objects are in different worldspaces, return a large value (just like vanilla) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8caec9876..ba4a6467a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -741,7 +741,10 @@ namespace MWWorld Ptr ret = searchPtr(name, activeOnly); if (!ret.isEmpty()) return ret; - throw std::runtime_error ("unknown ID: " + name); + std::string error = "failed to find an instance of object '" + name + "'"; + if (activeOnly) + error += " in active cells"; + throw std::runtime_error(error); } Ptr World::searchPtrViaActorId (int actorId) From 69df6098e5d118fecc9e7d3a1a800462fd3643d8 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 22 May 2020 00:11:23 +0200 Subject: [PATCH 127/227] Report frame number, number of actors and objects to stats --- apps/openmw/engine.cpp | 4 +++- apps/openmw/mwbase/environment.cpp | 6 ++++++ apps/openmw/mwbase/environment.hpp | 7 +++++++ apps/openmw/mwbase/mechanicsmanager.hpp | 3 +++ apps/openmw/mwbase/world.hpp | 3 +++ apps/openmw/mwmechanics/actors.hpp | 1 + .../mwmechanics/mechanicsmanagerimp.cpp | 7 +++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ apps/openmw/mwmechanics/objects.hpp | 5 +++++ apps/openmw/mwphysics/physicssystem.cpp | 8 ++++++++ apps/openmw/mwphysics/physicssystem.hpp | 3 +++ apps/openmw/mwworld/worldimp.cpp | 6 ++++++ apps/openmw/mwworld/worldimp.hpp | 3 +++ components/resource/stats.cpp | 20 ++++++++++++++----- 14 files changed, 72 insertions(+), 6 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 330d0e194..110efedc0 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -203,12 +203,14 @@ bool OMW::Engine::frame(float frametime) if (stats->collectStats("resource")) { + stats->setAttribute(frameNumber, "FrameNumber", frameNumber); + mResourceSystem->reportStats(frameNumber, stats); stats->setAttribute(frameNumber, "WorkQueue", mWorkQueue->getNumItems()); stats->setAttribute(frameNumber, "WorkThread", mWorkQueue->getNumActiveThreads()); - mEnvironment.getWorld()->getNavigator()->reportStats(frameNumber, *stats); + mEnvironment.reportStats(frameNumber, *stats); } } diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 5d01525b9..c70debda1 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -198,3 +198,9 @@ const MWBase::Environment& MWBase::Environment::get() assert (sThis); return *sThis; } + +void MWBase::Environment::reportStats(unsigned int frameNumber, osg::Stats& stats) const +{ + mMechanicsManager->reportStats(frameNumber, stats); + mWorld->reportStats(frameNumber, stats); +} diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 9163b21f3..80e6a6243 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -1,6 +1,11 @@ #ifndef GAME_BASE_ENVIRONMENT_H #define GAME_BASE_ENVIRONMENT_H +namespace osg +{ + class Stats; +} + namespace MWBase { class World; @@ -97,6 +102,8 @@ namespace MWBase static const Environment& get(); ///< Return instance of this class. + + void reportStats(unsigned int frameNumber, osg::Stats& stats) const; }; } diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 411f5fab1..cca789a40 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -11,6 +11,7 @@ namespace osg { + class Stats; class Vec3f; } @@ -269,6 +270,8 @@ namespace MWBase virtual bool isAttackPreparing(const MWWorld::Ptr& ptr) = 0; virtual bool isRunning(const MWWorld::Ptr& ptr) = 0; virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0; + + virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0; }; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 84f9b4984..4bd80132d 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -21,6 +21,7 @@ namespace osg class Matrixf; class Quat; class Image; + class Stats; } namespace Loading @@ -629,6 +630,8 @@ namespace MWBase virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0; virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0; + + virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index eb705fd68..4e952d1c8 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -70,6 +70,7 @@ namespace MWMechanics PtrActorMap::const_iterator begin() { return mActors.begin(); } PtrActorMap::const_iterator end() { return mActors.end(); } + std::size_t size() const { return mActors.size(); } void notifyDied(const MWWorld::Ptr &actor); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 25b33c486..5ca7b3cdd 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1,5 +1,7 @@ #include "mechanicsmanagerimp.hpp" +#include + #include #include @@ -1944,4 +1946,9 @@ namespace MWMechanics mActors.cleanupSummonedCreature(caster.getClass().getCreatureStats(caster), creatureActorId); } + void MechanicsManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const + { + stats.setAttribute(frameNumber, "Mechanics Actors", mActors.size()); + stats.setAttribute(frameNumber, "Mechanics Objects", mObjects.size()); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 640bd3bdd..83d1b236f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -240,6 +240,8 @@ namespace MWMechanics virtual bool isRunning(const MWWorld::Ptr& ptr) override; virtual bool isSneaking(const MWWorld::Ptr& ptr) override; + virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; + private: bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set &playerFollowers); diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp index 1bcf646a4..5160114a3 100644 --- a/apps/openmw/mwmechanics/objects.hpp +++ b/apps/openmw/mwmechanics/objects.hpp @@ -52,6 +52,11 @@ namespace MWMechanics void persistAnimationStates(); void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector& out); + + std::size_t size() const + { + return mObjects.size(); + } }; } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index a205abeb4..808b1e75a 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1,6 +1,7 @@ #include "physicssystem.hpp" #include +#include #include #include @@ -883,4 +884,11 @@ namespace MWPhysics mCollisionWorld->getBroadphase()->aabbTest(aabbMin, aabbMax, callback); return callback.getResult(); } + + void PhysicsSystem::reportStats(unsigned int frameNumber, osg::Stats& stats) const + { + stats.setAttribute(frameNumber, "Physics Actors", mActors.size()); + stats.setAttribute(frameNumber, "Physics Objects", mObjects.size()); + stats.setAttribute(frameNumber, "Physics HeightFields", mHeightFields.size()); + } } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 0f2ecc092..8b09722af 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -17,6 +17,7 @@ namespace osg { class Group; class Object; + class Stats; } namespace MWRender @@ -186,6 +187,8 @@ namespace MWPhysics bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const; + void reportStats(unsigned int frameNumber, osg::Stats& stats) const; + private: void updateWater(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8caec9876..c217df5e7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3947,4 +3947,10 @@ namespace MWWorld { return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore); } + + void World::reportStats(unsigned int frameNumber, osg::Stats& stats) const + { + mNavigator->reportStats(frameNumber, stats); + mPhysics->reportStats(frameNumber, stats); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7b6d2afdc..942788499 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -20,6 +20,7 @@ namespace osg { class Group; + class Stats; } namespace osgViewer @@ -732,6 +733,8 @@ namespace MWWorld bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override; bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const override; + + void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; }; } diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 59d65e889..0dd52ffb6 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -262,7 +262,6 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) stateset->setAttribute(new osg::PolygonMode(), osg::StateAttribute::PROTECTED); #endif - osg::Vec3 pos(_statsWidth-420.f, _statsHeight-500.0f,0.0f); osg::Vec4 backgroundColor(0.0, 0.0, 0.0f, 0.3); osg::Vec4 staticTextColor(1.0, 1.0, 0.0f, 1.0); osg::Vec4 dynamicTextColor(1.0, 1.0, 1.0f, 1.0); @@ -277,6 +276,8 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) _switch->addChild(group, false); static const std::vector statNames({ + "FrameNumber", + "", "Compiling", "WorkQueue", "WorkThread", @@ -302,16 +303,25 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) "NavMesh CacheSize", "NavMesh UsedTiles", "NavMesh CachedTiles", + "", + "Mechanics Actors", + "Mechanics Objects", + "", + "Physics Actors", + "Physics Objects", + "Physics HeightFields", }); static const auto longest = std::max_element(statNames.begin(), statNames.end(), [] (const std::string& lhs, const std::string& rhs) { return lhs.size() < rhs.size(); }); - const int numLines = statNames.size(); const float statNamesWidth = 13 * _characterSize + 2 * backgroundMargin; + const float statTextWidth = 7 * _characterSize + 2 * backgroundMargin; + const float statHeight = statNames.size() * _characterSize + 2 * backgroundMargin; + osg::Vec3 pos(_statsWidth - statNamesWidth - backgroundSpacing - statTextWidth, statHeight, 0.0f); group->addChild(createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0), statNamesWidth, - numLines * _characterSize + 2 * backgroundMargin, + statHeight, backgroundColor)); osg::ref_ptr staticText = new osgText::Text; @@ -335,8 +345,8 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) pos.x() += statNamesWidth + backgroundSpacing; group->addChild(createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0), - 7 * _characterSize + 2 * backgroundMargin, - numLines * _characterSize + 2 * backgroundMargin, + statTextWidth, + statHeight, backgroundColor)); osg::ref_ptr statsText = new osgText::Text; From e0ecbc08dfdb2bee437eaaf4ded3b9e8d32b9d9b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 22 May 2020 18:31:10 +0300 Subject: [PATCH 128/227] Give new opcodes to old functions made custom --- apps/openmw/mwscript/docs/vmformat.txt | 17 +++++++++++++- components/compiler/opcodes.hpp | 30 ++++++++++++------------ components/interpreter/docs/vmformat.txt | 20 +++------------- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 5a333a5b7..ccc579b30 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -464,5 +464,20 @@ op 0x200030d: RepairedOnMe, explicit op 0x200030e: TestCells op 0x200030f: TestInteriorCells op 0x2000310: ToggleRecastMesh +op 0x2000311: MenuMode +op 0x2000312: Random +op 0x2000313: ScriptRunning +op 0x2000314: StartScript +op 0x2000315: StopScript +op 0x2000316: GetSecondsPassed +op 0x2000317: Enable +op 0x2000318: Disable +op 0x2000319: GetDisabled +op 0x200031a: Enable, explicit +op 0x200031b: Disable, explicit +op 0x200031c: GetDisabled, explicit +op 0x200031d: StartScript, explicit +op 0x200031e: GetDistance +op 0x200031f: GetDistance, explicit -opcodes 0x2000311-0x3ffffff unused +opcodes 0x2000320-0x3ffffff unused diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 363e8bb85..fdc3e0eab 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -201,19 +201,6 @@ namespace Compiler namespace Misc { - const int opcodeMenuMode = 38; - const int opcodeRandom = 45; - const int opcodeScriptRunning = 46; - const int opcodeStartScript = 47; - const int opcodeStopScript = 48; - const int opcodeGetSecondsPassed = 50; - const int opcodeEnable = 51; - const int opcodeDisable = 52; - const int opcodeGetDisabled = 53; - const int opcodeEnableExplicit = 54; - const int opcodeDisableExplicit = 55; - const int opcodeGetDisabledExplicit = 56; - const int opcodeStartScriptExplicit = 71; const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeOnActivateExplicit = 0x2000306; @@ -318,6 +305,19 @@ namespace Compiler const int opcodeRepairedOnMe = 0x200030c; const int opcodeRepairedOnMeExplicit = 0x200030d; const int opcodeToggleRecastMesh = 0x2000310; + const int opcodeMenuMode = 0x2000311; + const int opcodeRandom = 0x2000312; + const int opcodeScriptRunning = 0x2000313; + const int opcodeStartScript = 0x2000314; + const int opcodeStopScript = 0x2000315; + const int opcodeGetSecondsPassed = 0x2000316; + const int opcodeEnable = 0x2000317; + const int opcodeDisable = 0x2000318; + const int opcodeGetDisabled = 0x2000319; + const int opcodeEnableExplicit = 0x200031a; + const int opcodeDisableExplicit = 0x200031b; + const int opcodeGetDisabledExplicit = 0x200031c; + const int opcodeStartScriptExplicit = 0x200031d; } namespace Sky @@ -486,8 +486,6 @@ namespace Compiler namespace Transformation { - const int opcodeGetDistance = 49; - const int opcodeGetDistanceExplicit = 57; const int opcodeSetScale = 0x2000164; const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; @@ -528,6 +526,8 @@ namespace Compiler const int opcodeMoveWorldExplicit = 0x2000209; const int opcodeResetActors = 0x20002f4; const int opcodeFixme = 0x2000302; + const int opcodeGetDistance = 0x200031e; + const int opcodeGetDistanceExplicit = 0x200031f; } namespace User diff --git a/components/interpreter/docs/vmformat.txt b/components/interpreter/docs/vmformat.txt index 5d1eba088..b5c9cf0ae 100644 --- a/components/interpreter/docs/vmformat.txt +++ b/components/interpreter/docs/vmformat.txt @@ -96,27 +96,14 @@ op 34: compare (float) stack[1] with stack[0]; pop twice; push 1 if lesser than op 35: compare (float) stack[1] with stack[0]; pop twice; push 1 if lesser or equal, 0 else op 36: compare (float) stack[1] with stack[0]; pop twice; push 1 if greater than, 0 else op 37: compare (float) stack[1] with stack[0]; pop twice; push 1 if greater or equal, 0 else -op 38: push 1 if game is in menu mode, 0 else +opcode 38 unused op 39: store stack[0] in global short stack[1] and pop twice op 40: store stack[0] in global long stack[1] and pop twice op 41: store stack[0] in global float stack[1] and pop twice op 42: replace stack[0] with global short stack[0] op 43: replace stack[0] with global long stack[0] op 44: replace stack[0] with global float stack[0] -op 45: replace stack[0] with a random integer value in the range [0, stack[0]-1] -op 46: replace stack[0] with 1, if global script stack[0] is running, 0 else -op 47: start script stack[0] and pop -op 48: stop script stack[0] and pop -op 49: replace stack[0] with distance between implicit reference and a reference of ID stack[0] -op 50: push frame duration (float) -op 51: enable implicit reference -op 52: disable implicit reference -op 53: push 1, if implicit reference is disabled, 0 else -op 54: explicit reference = stack[0]; pop; enable explicit reference -op 55: explicit reference = stack[0]; pop; disable explicit reference -op 56: explicit reference = stack[0]; pop; push 1, if explicit reference is disabled, 0 else -op 57: explicit reference = stack[0]; pop; - replace stack[0] with distance between explicit reference and a reference of ID stack[0] +opcodes 45-57 unused op 58: report string literal index in stack[0]; additional arguments (if any) in stack[n]..stack[1]; n is determined according to the message string @@ -133,6 +120,5 @@ op 67: store stack[0] in member float stack[2] of global script with ID stack[1] op 68: replace stack[0] with member short stack[1] of global script with ID stack[0] op 69: replace stack[0] with member short stack[1] of global script with ID stack[0] op 70: replace stack[0] with member short stack[1] of global script with ID stack[0] -op 71: explicit reference (target) = stack[0]; pop; start script stack[0] and pop -opcodes 72-33554431 unused +opcodes 71-33554431 unused opcodes 33554432-67108863 reserved for extensions From e8ec62b298b1198f9cb14d63e83c8e4cee9d8794 Mon Sep 17 00:00:00 2001 From: Fanael Linithien Date: Sun, 24 May 2020 16:49:20 +0200 Subject: [PATCH 129/227] Use all-lowercase names for windows API headers This allows the code to successfully cross-compile from hosts with case sensitive file names, like linux. --- components/files/windowspath.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 7f15a7efe..92d1a9ff0 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include namespace bconv = boost::locale::conv; From 51c0806a31d631a18d59ec3ac65e8a3aff8ddd73 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 23 May 2020 23:12:56 +0300 Subject: [PATCH 130/227] Use AppliedOnce flag in more effect duration calculations (#5425) --- CHANGELOG.md | 1 + apps/openmw/mwgui/widgets.cpp | 3 +++ apps/openmw/mwmechanics/autocalcspell.cpp | 2 ++ apps/openmw/mwmechanics/spellcasting.cpp | 11 +++++++---- apps/openmw/mwmechanics/spellpriority.cpp | 3 ++- apps/openmw/mwmechanics/spellutil.cpp | 3 +++ 6 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 285301a1d..369fe0470 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work Bug #5416: Junk non-node records before the root node are not handled gracefully Bug #5424: Creatures do not headtrack player + Bug #5425: Poison effect only appears for one frame Bug #5427: GetDistance unknown ID error is misleading Feature #5362: Show the soul gems' trapped soul in count dialog diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 0568efeb4..74076641a 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -443,6 +443,9 @@ namespace MWGui // constant effects have no duration and no target if (!mEffectParams.mIsConstant) { + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce)) + mEffectParams.mDuration = std::max(1, mEffectParams.mDuration); + if (mEffectParams.mDuration > 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) { spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + MyGUI::utility::toString(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index 6d3090918..9cee1aa31 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -262,6 +262,8 @@ namespace MWMechanics int duration = 0; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) duration = effect.mDuration; + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce)) + duration = std::max(1, duration); static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() .get().find("fEffectCostMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 769934986..7aec5d471 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -208,10 +208,15 @@ namespace MWMechanics } bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); - if (hasDuration && effectIt->mDuration == 0) + effect.mDuration = hasDuration ? static_cast(effectIt->mDuration) : 1.f; + + bool appliedOnce = magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce; + if (!appliedOnce) + effect.mDuration = std::max(1.f, effect.mDuration); + + if (effect.mDuration == 0) { // We still should add effect to list to allow GetSpellEffects to detect this spell - effect.mDuration = 0.f; appliedLastingEffects.push_back(effect); // duration 0 means apply full magnitude instantly @@ -224,8 +229,6 @@ namespace MWMechanics } else { - effect.mDuration = hasDuration ? static_cast(effectIt->mDuration) : 1.f; - effect.mTimeLeft = effect.mDuration; targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude)); diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 9428beafc..81658193d 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -394,8 +394,9 @@ namespace MWMechanics priority = 10; const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); const DynamicStat& current = stats.getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth); + // NB: this currently assumes the hardcoded magic effect flags are used const float magnitude = (effect.mMagnMin + effect.mMagnMax)/2.f; - const float toHeal = magnitude * effect.mDuration; + const float toHeal = magnitude * std::max(1, effect.mDuration); // Effect doesn't heal more than we need, *or* we are below 1/2 health if (current.getModified() - current.getCurrent() > toHeal || current.getCurrent() < current.getModified()*0.5) diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index def3bbbc8..bb4953e48 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -31,9 +31,12 @@ namespace MWMechanics magicEffect = store.get().find(effect.mEffectID); bool hasMagnitude = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude); bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); + bool appliedOnce = magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce; int minMagn = hasMagnitude ? effect.mMagnMin : 1; int maxMagn = hasMagnitude ? effect.mMagnMax : 1; int duration = hasDuration ? effect.mDuration : 1; + if (!appliedOnce) + duration = std::max(1, duration); static const float fEffectCostMult = store.get().find("fEffectCostMult")->mValue.getFloat(); float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn)); From 89a2c69a61c028302c59c86f46c34d93cbcb82bc Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 25 May 2020 23:51:15 +0300 Subject: [PATCH 131/227] Support particle node transformations --- components/nifosg/nifloader.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index e3c6d823f..5b9d0698d 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -496,14 +496,6 @@ namespace NifOsg switch (nifNode->recType) { - case Nif::RC_NiAutoNormalParticles: - case Nif::RC_NiRotatingParticles: - // Leaf nodes in the NIF hierarchy, so won't be able to dynamically attach children. - // No support for keyframe controllers (just crashes in the original engine). - if (nifNode->trafo.isIdentity()) - node = new osg::Group; - dataVariance = osg::Object::STATIC; - break; case Nif::RC_NiBillboardNode: dataVariance = osg::Object::DYNAMIC; break; From e3df170a53def32cefcad3ad527f66d1913d2865 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 26 May 2020 10:58:24 +0400 Subject: [PATCH 132/227] Fix mControlsDisabled flag usage --- apps/openmw/mwbase/inputmanager.hpp | 2 ++ apps/openmw/mwinput/controllermanager.cpp | 10 ++++------ apps/openmw/mwinput/controllermanager.hpp | 3 +-- apps/openmw/mwinput/inputmanagerimp.cpp | 8 +++++--- apps/openmw/mwinput/inputmanagerimp.hpp | 3 +++ apps/openmw/mwinput/keyboardmanager.cpp | 6 +++--- apps/openmw/mwinput/keyboardmanager.hpp | 4 ---- apps/openmw/mwinput/mousemanager.cpp | 12 +++++------- apps/openmw/mwinput/mousemanager.hpp | 3 +-- 9 files changed, 24 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 932463e97..951b5053a 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -76,6 +76,8 @@ namespace MWBase virtual void resetIdleTime() = 0; virtual void executeAction(int action) = 0; + + virtual bool controlsDisabled() = 0; }; } diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index a71c5b31a..50a169c5c 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -36,7 +36,6 @@ namespace MWInput , mSneakToggleShortcutTimer(0.f) , mGamepadZoom(0) , mGamepadGuiCursorEnabled(true) - , mControlsDisabled(false) , mJoystickLastUsed(false) , mSneakGamepadShortcut(false) , mGamepadPreviewMode(false) @@ -83,9 +82,8 @@ namespace MWInput } } - bool ControllerManager::update(float dt, bool disableControls) + bool ControllerManager::update(float dt) { - mControlsDisabled = disableControls; mGamepadPreviewMode = mActionManager->getPreviewDelay() == 1.f; if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled)) @@ -232,7 +230,7 @@ namespace MWInput auto kc = sdlKeyToMyGUI(SDLK_ESCAPE); mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(kc, 0)); - if (!mControlsDisabled) + if (!MWBase::Environment::get().getInputManager()->controlsDisabled()) mBindingsManager->controllerButtonPressed(deviceID, arg); } @@ -244,7 +242,7 @@ namespace MWInput return; } - if (!mJoystickEnabled || mControlsDisabled) + if (!mJoystickEnabled || MWBase::Environment::get().getInputManager()->controlsDisabled()) return; mJoystickLastUsed = true; @@ -275,7 +273,7 @@ namespace MWInput void ControllerManager::axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg) { - if (!mJoystickEnabled || mControlsDisabled) + if (!mJoystickEnabled || MWBase::Environment::get().getInputManager()->controlsDisabled()) return; mJoystickLastUsed = true; diff --git a/apps/openmw/mwinput/controllermanager.hpp b/apps/openmw/mwinput/controllermanager.hpp index 6b9546b0c..94faff088 100644 --- a/apps/openmw/mwinput/controllermanager.hpp +++ b/apps/openmw/mwinput/controllermanager.hpp @@ -23,7 +23,7 @@ namespace MWInput virtual ~ControllerManager() = default; - bool update(float dt, bool disableControls); + bool update(float dt); virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg); virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg); @@ -56,7 +56,6 @@ namespace MWInput float mSneakToggleShortcutTimer; float mGamepadZoom; bool mGamepadGuiCursorEnabled; - bool mControlsDisabled; bool mJoystickLastUsed; bool mGuiCursorEnabled; bool mSneakGamepadShortcut; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 48e1581be..044f50668 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -31,6 +31,7 @@ namespace MWInput const std::string& userFile, bool userFileExists, const std::string& userControllerBindingsFile, const std::string& controllerBindingsFile, bool grab) : mGrabCursor(Settings::Manager::getBool("grab cursor", "Input")) + , mControlsDisabled(false) { mInputWrapper = new SDLUtil::InputWrapper(window, viewer, grab); mInputWrapper->setWindowEventCallback(MWBase::Environment::get().getWindowManager()); @@ -105,10 +106,11 @@ namespace MWInput void InputManager::update(float dt, bool disableControls, bool disableEvents) { + mControlsDisabled = disableControls; + mInputWrapper->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); mInputWrapper->capture(disableEvents); - mKeyboardManager->setControlsDisabled(disableControls); if (disableControls) { updateCursorMode(); @@ -119,8 +121,8 @@ namespace MWInput updateCursorMode(); - bool controllerMove = mControllerManager->update(dt, disableControls); - mMouseManager->update(dt, disableControls); + bool controllerMove = mControllerManager->update(dt); + mMouseManager->update(dt); mSensorManager->update(dt); mActionManager->update(dt, controllerMove); } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 4ecb6a82d..de969d040 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -94,6 +94,8 @@ namespace MWInput virtual void executeAction(int action); + virtual bool controlsDisabled() { return mControlsDisabled; } + private: void convertMousePosForMyGUI(int& x, int& y); @@ -110,6 +112,7 @@ namespace MWInput SDLUtil::InputWrapper* mInputWrapper; bool mGrabCursor; + bool mControlsDisabled; ControlSwitch* mControlSwitch; diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index 20115155e..db047a342 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -18,7 +18,6 @@ namespace MWInput { KeyboardManager::KeyboardManager(BindingsManager* bindingsManager) : mBindingsManager(bindingsManager) - , mControlsDisabled(false) { } @@ -53,10 +52,11 @@ namespace MWInput if (arg.repeat) return; - if (!mControlsDisabled && !consumed) + MWBase::InputManager* input = MWBase::Environment::get().getInputManager(); + if (!input->controlsDisabled() && !consumed) mBindingsManager->keyPressed(arg); - MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); + input->setJoystickLastUsed(false); } void KeyboardManager::keyReleased(const SDL_KeyboardEvent &arg) diff --git a/apps/openmw/mwinput/keyboardmanager.hpp b/apps/openmw/mwinput/keyboardmanager.hpp index ae473be51..b7027220f 100644 --- a/apps/openmw/mwinput/keyboardmanager.hpp +++ b/apps/openmw/mwinput/keyboardmanager.hpp @@ -19,12 +19,8 @@ namespace MWInput virtual void keyPressed(const SDL_KeyboardEvent &arg); virtual void keyReleased(const SDL_KeyboardEvent &arg); - void setControlsDisabled(bool disabled) { mControlsDisabled = disabled; } - private: BindingsManager* mBindingsManager; - - bool mControlsDisabled; }; } #endif diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index 2cce1cd80..de0ff80e0 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -33,7 +33,6 @@ namespace MWInput , mGuiCursorY(0) , mMouseWheel(0) , mMouseLookEnabled(false) - , mControlsDisabled(false) , mGuiCursorEnabled(true) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); @@ -88,7 +87,7 @@ namespace MWInput MWBase::Environment::get().getWindowManager()->setCursorActive(true); } - if (mMouseLookEnabled && !mControlsDisabled) + if (mMouseLookEnabled && !input->controlsDisabled()) { float x = arg.xrel * mCameraSensitivity * (mInvertX ? -1 : 1) / 256.f; float y = arg.yrel * mCameraSensitivity * (mInvertY ? -1 : 1) * mCameraYMultiplier / 256.f; @@ -136,10 +135,11 @@ namespace MWInput void MouseManager::mouseWheelMoved(const SDL_MouseWheelEvent &arg) { - if (mBindingsManager->isDetectingBindingState() || !mControlsDisabled) + MWBase::InputManager* input = MWBase::Environment::get().getInputManager(); + if (mBindingsManager->isDetectingBindingState() || !input->controlsDisabled()) mBindingsManager->mouseWheelMoved(arg); - MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); + input->setJoystickLastUsed(false); } void MouseManager::mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id) @@ -169,10 +169,8 @@ namespace MWInput mBindingsManager->mousePressed(arg, id); } - void MouseManager::update(float dt, bool disableControls) + void MouseManager::update(float dt) { - mControlsDisabled = disableControls; - if (!mMouseLookEnabled) return; diff --git a/apps/openmw/mwinput/mousemanager.hpp b/apps/openmw/mwinput/mousemanager.hpp index 58d97f6e5..aea07c8db 100644 --- a/apps/openmw/mwinput/mousemanager.hpp +++ b/apps/openmw/mwinput/mousemanager.hpp @@ -20,7 +20,7 @@ namespace MWInput virtual ~MouseManager() = default; - void update(float dt, bool disableControls); + void update(float dt); virtual void mouseMoved(const SDLUtil::MouseMotionEvent &arg); virtual void mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id); @@ -51,7 +51,6 @@ namespace MWInput float mGuiCursorY; int mMouseWheel; bool mMouseLookEnabled; - bool mControlsDisabled; bool mGuiCursorEnabled; }; } From fc9a10ba489278facc29dc993f3233f63bae76b8 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 26 May 2020 00:33:19 +0300 Subject: [PATCH 133/227] Streamline node controller handling Reduce code duplication Allow non-animated nodes controlled by NiVisController to be optimized out --- components/nifosg/nifloader.cpp | 136 ++++++++++---------------------- 1 file changed, 42 insertions(+), 94 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 5b9d0698d..c6a79333d 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -512,17 +512,9 @@ namespace NifOsg // This takes advantage of the fact root nodes can't have additional controllers // loaded from an external .kf file (original engine just throws "can't find node" errors if you try). if (!nifNode->parent && nifNode->controller.empty() && nifNode->trafo.isIdentity()) - { node = new osg::Group; - dataVariance = osg::Object::STATIC; - } - else - { - dataVariance = (nifNode->controller.empty() ? osg::Object::STATIC : osg::Object::DYNAMIC); - } - if (nifNode->isBone) - dataVariance = osg::Object::DYNAMIC; + dataVariance = nifNode->isBone ? osg::Object::DYNAMIC : osg::Object::STATIC; break; } @@ -535,7 +527,7 @@ namespace NifOsg } osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager, - std::vector boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool isAnimated, TextKeyMap* textKeys, osg::Node* rootNode=nullptr) + std::vector boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool hasAnimatedParents, TextKeyMap* textKeys, osg::Node* rootNode=nullptr) { if (rootNode != nullptr && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) return nullptr; @@ -547,9 +539,6 @@ namespace NifOsg node->addCullCallback(new BillboardCallback); } - if (!nifNode->controller.empty() && nifNode->controller->recType == Nif::RC_NiKeyframeController) - isAnimated = true; - node->setName(nifNode->name); if (parentNode) @@ -619,16 +608,6 @@ namespace NifOsg node->setNodeMask(Loader::getHiddenNodeMask()); } - if ((skipMeshes || hasMarkers) && isAnimated) // make sure the empty node is not optimized away so the physicssystem can find it. - { - node->setDataVariance(osg::Object::DYNAMIC); - } - - if ((nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips) && isAnimated) // Same thing for animated shapes - { - node->setDataVariance(osg::Object::DYNAMIC); - } - osg::ref_ptr composite = new SceneUtil::CompositeStateSetUpdater; applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags); @@ -660,9 +639,12 @@ namespace NifOsg if (composite->getNumControllers() > 0) node->addUpdateCallback(composite); - if (nifNode->recType != Nif::RC_NiTriShape && nifNode->recType != Nif::RC_NiTriStrips - && !nifNode->controller.empty() && node->getDataVariance() == osg::Object::DYNAMIC) - handleNodeControllers(nifNode, static_cast(node.get()), animflags); + bool isAnimated = false; + handleNodeControllers(nifNode, node, animflags, isAnimated); + hasAnimatedParents |= isAnimated; + // Make sure empty nodes are not optimized away so the physics system can find them. + if (isAnimated || (hasAnimatedParents && (skipMeshes || hasMarkers))) + node->setDataVariance(osg::Object::DYNAMIC); // LOD and Switch nodes must be wrapped by a transform (the current node) to support transformations properly // and we need to attach their children to the osg::LOD/osg::Switch nodes @@ -703,7 +685,7 @@ namespace NifOsg for(size_t i = 0;i < children.length();++i) { if(!children[i].empty()) - handleNode(children[i].getPtr(), currentNode, imageManager, boundTextures, animflags, skipMeshes, hasMarkers, isAnimated, textKeys, rootNode); + handleNode(children[i].getPtr(), currentNode, imageManager, boundTextures, animflags, skipMeshes, hasMarkers, hasAnimatedParents, textKeys, rootNode); } } @@ -734,40 +716,10 @@ namespace NifOsg setupController(niuvctrl, uvctrl, animflags); composite->addController(uvctrl); } - else if (ctrl->recType == Nif::RC_NiKeyframeController) - { - const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if(!key->data.empty()) - { - osg::ref_ptr callback(new KeyframeController(key->data.getPtr())); - - setupController(key, callback, animflags); - node->addUpdateCallback(callback); - } - } - else if (ctrl->recType == Nif::RC_NiPathController) - { - const Nif::NiPathController *path = static_cast(ctrl.getPtr()); - if (!path->posData.empty() && !path->floatData.empty()) - { - osg::ref_ptr callback(new PathController(path)); - - setupController(path, callback, animflags); - node->addUpdateCallback(callback); - } - } - else if (ctrl->recType == Nif::RC_NiVisController) - { - handleVisController(static_cast(ctrl.getPtr()), node, animflags); - } - else if(ctrl->recType == Nif::RC_NiGeomMorpherController) - {} // handled in handleTriShape - else - Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } } - void handleNodeControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, int animflags) + void handleNodeControllers(const Nif::Node* nifNode, osg::Node* node, int animflags, bool& isAnimated) { for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { @@ -776,56 +728,54 @@ namespace NifOsg if (ctrl->recType == Nif::RC_NiKeyframeController) { const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if(!key->data.empty()) - { - osg::ref_ptr callback(new KeyframeController(key->data.getPtr())); - - setupController(key, callback, animflags); - transformNode->addUpdateCallback(callback); - } + if (key->data.empty()) + continue; + osg::ref_ptr callback(new KeyframeController(key->data.getPtr())); + setupController(key, callback, animflags); + node->addUpdateCallback(callback); + isAnimated = true; } else if (ctrl->recType == Nif::RC_NiPathController) { const Nif::NiPathController *path = static_cast(ctrl.getPtr()); - if (!path->posData.empty() && !path->floatData.empty()) - { - osg::ref_ptr callback(new PathController(path)); - - setupController(path, callback, animflags); - transformNode->addUpdateCallback(callback); - } + if (path->posData.empty() || path->floatData.empty()) + continue; + osg::ref_ptr callback(new PathController(path)); + setupController(path, callback, animflags); + node->addUpdateCallback(callback); + isAnimated = true; } else if (ctrl->recType == Nif::RC_NiVisController) { - handleVisController(static_cast(ctrl.getPtr()), transformNode, animflags); + const Nif::NiVisController *visctrl = static_cast(ctrl.getPtr()); + if (visctrl->data.empty()) + continue; + osg::ref_ptr callback(new VisController(visctrl->data.getPtr(), Loader::getHiddenNodeMask())); + setupController(visctrl, callback, animflags); + node->addUpdateCallback(callback); } else if (ctrl->recType == Nif::RC_NiRollController) { - handleRollController(static_cast(ctrl.getPtr()), transformNode, animflags); + const Nif::NiRollController *rollctrl = static_cast(ctrl.getPtr()); + if (rollctrl->data.empty()) + continue; + osg::ref_ptr callback(new RollController(rollctrl->data.getPtr())); + setupController(rollctrl, callback, animflags); + node->addUpdateCallback(callback); + isAnimated = true; + } + else if (ctrl->recType == Nif::RC_NiGeomMorpherController + || ctrl->recType == Nif::RC_NiParticleSystemController + || ctrl->recType == Nif::RC_NiBSPArrayController + || ctrl->recType == Nif::RC_NiUVController) + { + // These controllers are handled elsewhere } else Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } } - void handleVisController(const Nif::NiVisController* visctrl, osg::Node* node, int animflags) - { - if (visctrl->data.empty()) - return; - osg::ref_ptr callback(new VisController(visctrl->data.getPtr(), Loader::getHiddenNodeMask())); - setupController(visctrl, callback, animflags); - node->addUpdateCallback(callback); - } - - void handleRollController(const Nif::NiRollController* rollctrl, osg::Node* node, int animflags) - { - if (rollctrl->data.empty()) - return; - osg::ref_ptr callback(new RollController(rollctrl->data.getPtr())); - setupController(rollctrl, callback, animflags); - node->addUpdateCallback(callback); - } - void handleMaterialControllers(const Nif::Property *materialProperty, SceneUtil::CompositeStateSetUpdater* composite, int animflags) { for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) @@ -1062,8 +1012,6 @@ namespace NifOsg continue; if(ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiBSPArrayController) partctrl = static_cast(ctrl.getPtr()); - else - Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } if (!partctrl) { From 7a9403aeed428ae222c5c09948f44a27f0d0c940 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 26 May 2020 15:01:26 +0300 Subject: [PATCH 134/227] Remove unnecessary casts --- components/nifosg/nifloader.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index c6a79333d..fc5f8e1f8 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1175,12 +1175,7 @@ namespace NifOsg osg::ref_ptr drawable; osg::ref_ptr geom (new osg::Geometry); triShapeToGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags); - Nif::ControllerPtr ctrl; - if (nifNode->recType == Nif::RC_NiTriShape) - ctrl = static_cast(nifNode)->controller; - else - ctrl = static_cast(nifNode)->controller; - for (; !ctrl.empty(); ctrl = ctrl->next) + for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) continue; From 74a74209acbf5eab9b7da24cf4be0f3c74b3e03d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 26 May 2020 15:08:10 +0300 Subject: [PATCH 135/227] Allow junk (data-less) NiParticleColorModifiers --- components/nifosg/nifloader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index fc5f8e1f8..131f0c470 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -870,6 +870,8 @@ namespace NifOsg else if (affectors->recType == Nif::RC_NiParticleColorModifier) { const Nif::NiParticleColorModifier *cl = static_cast(affectors.getPtr()); + if (cl->data.empty()) + continue; const Nif::NiColorData *clrdata = cl->data.getPtr(); program->addOperator(new ParticleColorAffector(clrdata)); } From 95cd47335299c59b2109b65a7ccded07491af0c1 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 26 May 2020 17:01:45 +0300 Subject: [PATCH 136/227] Reenable weapon animation lower body animation blending in FPV (#5441) Disabling it is a non-vanilla behavior that breaks things that aren't broken in vanilla --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 285301a1d..d153e9202 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Bug #5416: Junk non-node records before the root node are not handled gracefully Bug #5424: Creatures do not headtrack player Bug #5427: GetDistance unknown ID error is misleading + Bug #5441: Enemies can't push a player character when in critical strike stance Feature #5362: Show the soul gems' trapped soul in count dialog 0.46.0 diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ac8d417f3..0a332a10c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1260,10 +1260,9 @@ bool CharacterController::updateWeaponState(CharacterState& idle) } } - // Use blending only with 3d-person movement animations for bipedal actors - bool firstPersonPlayer = (mPtr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson()); + // For biped actors, blend weapon animations with lower body animations with higher priority MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon); - if (!firstPersonPlayer && mPtr.getClass().isBipedal(mPtr)) + if (mPtr.getClass().isBipedal(mPtr)) priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; bool forcestateupdate = false; From b00d72b9e4905672d9327354cc1839f8a555c2ed Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 26 May 2020 11:24:47 +0400 Subject: [PATCH 137/227] Move cursor update to the MouseManager --- apps/openmw/mwinput/inputmanagerimp.cpp | 36 +++---------------------- apps/openmw/mwinput/inputmanagerimp.hpp | 3 --- apps/openmw/mwinput/mousemanager.cpp | 31 +++++++++++++++++++-- apps/openmw/mwinput/mousemanager.hpp | 2 ++ 4 files changed, 34 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 044f50668..79541fbe4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -30,8 +30,7 @@ namespace MWInput osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation, const std::string& userFile, bool userFileExists, const std::string& userControllerBindingsFile, const std::string& controllerBindingsFile, bool grab) - : mGrabCursor(Settings::Manager::getBool("grab cursor", "Input")) - , mControlsDisabled(false) + : mControlsDisabled(false) { mInputWrapper = new SDLUtil::InputWrapper(window, viewer, grab); mInputWrapper->setWindowEventCallback(MWBase::Environment::get().getWindowManager()); @@ -81,29 +80,6 @@ namespace MWInput mActionManager->setAttemptJump(jumping); } - void InputManager::updateCursorMode() - { - bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) - && !MWBase::Environment::get().getWindowManager()->isConsoleMode(); - - bool wasRelative = mInputWrapper->getMouseRelative(); - bool isRelative = !MWBase::Environment::get().getWindowManager()->isGuiMode(); - - // don't keep the pointer away from the window edge in gui mode - // stop using raw mouse motions and switch to system cursor movements - mInputWrapper->setMouseRelative(isRelative); - - //we let the mouse escape in the main menu - mInputWrapper->setGrabPointer(grab && (mGrabCursor || isRelative)); - - //we switched to non-relative mode, move our cursor to where the in-game - //cursor is - if (!isRelative && wasRelative != isRelative) - { - mMouseManager->warpMouse(); - } - } - void InputManager::update(float dt, bool disableControls, bool disableEvents) { mControlsDisabled = disableControls; @@ -113,13 +89,13 @@ namespace MWInput if (disableControls) { - updateCursorMode(); + mMouseManager->updateCursorMode(); return; } mBindingsManager->update(dt); - updateCursorMode(); + mMouseManager->updateCursorMode(); bool controllerMove = mControllerManager->update(dt); mMouseManager->update(dt); @@ -153,12 +129,6 @@ namespace MWInput void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) { - for (const auto& setting : changed) - { - if (setting.first == "Input" && setting.second == "grab cursor") - mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); - } - mMouseManager->processChangedSettings(changed); mSensorManager->processChangedSettings(changed); } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index de969d040..debbf27e0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -101,8 +101,6 @@ namespace MWInput void handleGuiArrowKey(int action); - void updateCursorMode(); - void quickKey(int index); void showQuickKeysMenu(); @@ -111,7 +109,6 @@ namespace MWInput SDLUtil::InputWrapper* mInputWrapper; - bool mGrabCursor; bool mControlsDisabled; ControlSwitch* mControlSwitch; diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index de0ff80e0..df38868e1 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -24,8 +24,9 @@ namespace MWInput MouseManager::MouseManager(BindingsManager* bindingsManager, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window) : mInvertX(Settings::Manager::getBool("invert x axis", "Input")) , mInvertY(Settings::Manager::getBool("invert y axis", "Input")) - , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) - , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) + , mGrabCursor(Settings::Manager::getBool("grab cursor", "Input")) + , mCameraSensitivity(Settings::Manager::getFloat("camera sensitivity", "Input")) + , mCameraYMultiplier(Settings::Manager::getFloat("camera y multiplier", "Input")) , mBindingsManager(bindingsManager) , mInputWrapper(inputWrapper) , mInvUiScalingFactor(1.f) @@ -58,6 +59,9 @@ namespace MWInput if (setting.first == "Input" && setting.second == "camera sensitivity") mCameraSensitivity = Settings::Manager::getFloat("camera sensitivity", "Input"); + + if (setting.first == "Input" && setting.second == "grab cursor") + mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); } } @@ -169,6 +173,29 @@ namespace MWInput mBindingsManager->mousePressed(arg, id); } + void MouseManager::updateCursorMode() + { + bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) + && !MWBase::Environment::get().getWindowManager()->isConsoleMode(); + + bool wasRelative = mInputWrapper->getMouseRelative(); + bool isRelative = !MWBase::Environment::get().getWindowManager()->isGuiMode(); + + // don't keep the pointer away from the window edge in gui mode + // stop using raw mouse motions and switch to system cursor movements + mInputWrapper->setMouseRelative(isRelative); + + //we let the mouse escape in the main menu + mInputWrapper->setGrabPointer(grab && (mGrabCursor || isRelative)); + + //we switched to non-relative mode, move our cursor to where the in-game + //cursor is + if (!isRelative && wasRelative != isRelative) + { + warpMouse(); + } + } + void MouseManager::update(float dt) { if (!mMouseLookEnabled) diff --git a/apps/openmw/mwinput/mousemanager.hpp b/apps/openmw/mwinput/mousemanager.hpp index aea07c8db..0f523591a 100644 --- a/apps/openmw/mwinput/mousemanager.hpp +++ b/apps/openmw/mwinput/mousemanager.hpp @@ -20,6 +20,7 @@ namespace MWInput virtual ~MouseManager() = default; + void updateCursorMode(); void update(float dt); virtual void mouseMoved(const SDLUtil::MouseMotionEvent &arg); @@ -40,6 +41,7 @@ namespace MWInput private: bool mInvertX; bool mInvertY; + bool mGrabCursor; float mCameraSensitivity; float mCameraYMultiplier; From 000e44a18ecf190608efab7ba320782c5828da5f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 26 May 2020 16:45:08 +0400 Subject: [PATCH 138/227] Move data from WindowManager to CharacterCreation to simplify API --- apps/openmw/mwbase/windowmanager.hpp | 6 --- apps/openmw/mwgui/charactercreation.cpp | 63 +++++++++++----------- apps/openmw/mwgui/charactercreation.hpp | 5 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 69 ------------------------- apps/openmw/mwgui/windowmanagerimp.hpp | 14 ----- 5 files changed, 38 insertions(+), 119 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 2639fe590..4fa782b8f 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -253,12 +253,6 @@ namespace MWBase virtual void onFrame (float frameDuration) = 0; - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - virtual std::map getPlayerSkillValues() = 0; - virtual std::map getPlayerAttributeValues() = 0; - virtual SkillList getPlayerMinorSkills() = 0; - virtual SkillList getPlayerMajorSkills() = 0; - /** * Fetches a GMST string from the store, if there is no setting with the given * ID or it is not a string the default string is returned. diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 44376b87a..54e1a69a4 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -105,23 +105,32 @@ namespace MWGui mGenerateClassSpecializations[0] = 0; mGenerateClassSpecializations[1] = 0; mGenerateClassSpecializations[2] = 0; + + // Setup player stats + for (int i = 0; i < ESM::Attribute::Length; ++i) + mPlayerAttributes.emplace(ESM::Attribute::sAttributeIds[i], MWMechanics::AttributeValue()); + + for (int i = 0; i < ESM::Skill::Length; ++i) + mPlayerSkillValues.emplace(ESM::Skill::sSkillIds[i], MWMechanics::SkillValue()); } void CharacterCreation::setValue (const std::string& id, const MWMechanics::AttributeValue& value) { - if (mReviewDialog) + static const char *ids[] = { - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8", - 0 - }; + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", + "AttribVal5", "AttribVal6", "AttribVal7", "AttribVal8", 0 + }; - for (int i=0; ids[i]; ++i) + for (int i=0; ids[i]; ++i) + { + if (ids[i]==id) { - if (ids[i]==id) - mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value); + mPlayerAttributes[static_cast(i)] = value; + if (mReviewDialog) + mReviewDialog->setAttribute(static_cast(i), value); + + break; } } } @@ -147,6 +156,7 @@ namespace MWGui void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) { + mPlayerSkillValues[parSkill] = value; if (mReviewDialog) mReviewDialog->setSkillValue(parSkill, value); } @@ -155,6 +165,9 @@ namespace MWGui { if (mReviewDialog) mReviewDialog->configureSkills(major, minor); + + mPlayerMajorSkills = major; + mPlayerMinorSkills = minor; } void CharacterCreation::onFrame(float duration) @@ -269,31 +282,21 @@ namespace MWGui mReviewDialog->setClass(*playerClass); mReviewDialog->setBirthSign(player.getBirthSign()); - { - MWWorld::Ptr playerPtr = MWMechanics::getPlayer(); - const MWMechanics::CreatureStats& stats = playerPtr.getClass().getCreatureStats(playerPtr); - - mReviewDialog->setHealth ( stats.getHealth() ); - mReviewDialog->setMagicka( stats.getMagicka() ); - mReviewDialog->setFatigue( stats.getFatigue() ); - } + MWWorld::Ptr playerPtr = MWMechanics::getPlayer(); + const MWMechanics::CreatureStats& stats = playerPtr.getClass().getCreatureStats(playerPtr); + mReviewDialog->setHealth(stats.getHealth()); + mReviewDialog->setMagicka(stats.getMagicka()); + mReviewDialog->setFatigue(stats.getFatigue()); + for (auto& attributePair : mPlayerAttributes) { - std::map attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); - for (auto& attributePair : attributes) - { - mReviewDialog->setAttribute(static_cast (attributePair.first), attributePair.second); - } + mReviewDialog->setAttribute(static_cast (attributePair.first), attributePair.second); } - + for (auto& skillPair : mPlayerSkillValues) { - std::map skills = MWBase::Environment::get().getWindowManager()->getPlayerSkillValues(); - for (auto& skillPair : skills) - { - mReviewDialog->setSkillValue(static_cast (skillPair.first), skillPair.second); - } - mReviewDialog->configureSkills(MWBase::Environment::get().getWindowManager()->getPlayerMajorSkills(), MWBase::Environment::get().getWindowManager()->getPlayerMinorSkills()); + mReviewDialog->setSkillValue(static_cast (skillPair.first), skillPair.second); } + mReviewDialog->configureSkills(mPlayerMajorSkills, mPlayerMinorSkills); mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index d5739c3d0..4f6296785 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "../mwmechanics/stat.hpp" @@ -56,6 +57,10 @@ namespace MWGui osg::Group* mParent; Resource::ResourceSystem* mResourceSystem; + SkillList mPlayerMajorSkills, mPlayerMinorSkills; + std::map mPlayerAttributes; + std::map mPlayerSkillValues; + //Dialogs TextInputDialog* mNameDialog; RaceDialog* mRaceDialog; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 1d2dc185b..bcd878d34 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -182,12 +182,6 @@ namespace MWGui , mCursorVisible(true) , mCursorActive(false) , mPlayerBounty(-1) - , mPlayerName() - , mPlayerRaceId() - , mPlayerAttributes() - , mPlayerMajorSkills() - , mPlayerMinorSkills() - , mPlayerSkillValues() , mGui(nullptr) , mGuiModes() , mCursorManager(nullptr) @@ -594,17 +588,6 @@ namespace MWGui mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem); - // Setup player stats - for (int i = 0; i < ESM::Attribute::Length; ++i) - { - mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::AttributeValue())); - } - - for (int i = 0; i < ESM::Skill::Length; ++i) - { - mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::SkillValue())); - } - updatePinnedWindows(); // Set up visibility @@ -797,40 +780,14 @@ namespace MWGui { mStatsWindow->setValue (id, value); mCharGen->setValue(id, value); - - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8" - }; - static ESM::Attribute::AttributeID attributes[] = - { - ESM::Attribute::Strength, - ESM::Attribute::Intelligence, - ESM::Attribute::Willpower, - ESM::Attribute::Agility, - ESM::Attribute::Speed, - ESM::Attribute::Endurance, - ESM::Attribute::Personality, - ESM::Attribute::Luck - }; - for (size_t i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i) - { - if (id != ids[i]) - continue; - mPlayerAttributes[attributes[i]] = value; - break; - } } - void WindowManager::setValue (int parSkill, const MWMechanics::SkillValue& value) { /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we /// allow custom skills. mStatsWindow->setValue(static_cast (parSkill), value); mCharGen->setValue(static_cast (parSkill), value); - mPlayerSkillValues[parSkill] = value; } void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) @@ -843,10 +800,6 @@ namespace MWGui void WindowManager::setValue (const std::string& id, const std::string& value) { mStatsWindow->setValue (id, value); - if (id=="name") - mPlayerName = value; - else if (id=="race") - mPlayerRaceId = value; } void WindowManager::setValue (const std::string& id, int value) @@ -868,8 +821,6 @@ namespace MWGui { mStatsWindow->configureSkills (major, minor); mCharGen->configureSkills(major, minor); - mPlayerMajorSkills = major; - mPlayerMinorSkills = minor; } void WindowManager::updateSkillArea() @@ -1639,26 +1590,6 @@ namespace MWGui return mGuiModes.back(); } - std::map WindowManager::getPlayerSkillValues() - { - return mPlayerSkillValues; - } - - std::map WindowManager::getPlayerAttributeValues() - { - return mPlayerAttributes; - } - - WindowManager::SkillList WindowManager::getPlayerMinorSkills() - { - return mPlayerMinorSkills; - } - - WindowManager::SkillList WindowManager::getPlayerMajorSkills() - { - return mPlayerMajorSkills; - } - void WindowManager::disallowMouse() { mInputBlocker->setVisible (true); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 8b16cf25f..22f407c2b 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -282,12 +282,6 @@ namespace MWGui virtual void onFrame (float frameDuration); - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - virtual std::map getPlayerSkillValues(); - virtual std::map getPlayerAttributeValues(); - virtual SkillList getPlayerMinorSkills(); - virtual SkillList getPlayerMajorSkills(); - /** * Fetches a GMST string from the store, if there is no setting with the given * ID or it is not a string the default string is returned. @@ -474,14 +468,6 @@ namespace MWGui void setCursorVisible(bool visible); - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - // Various stats about player as needed by window manager - std::string mPlayerName; - std::string mPlayerRaceId; - std::map mPlayerAttributes; - SkillList mPlayerMajorSkills, mPlayerMinorSkills; - std::map mPlayerSkillValues; - MyGUI::Gui *mGui; // Gui struct GuiModeState From dcfc4cc5dd3249af593b49d1c5a1a48cbcc3760f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 26 May 2020 16:54:04 +0400 Subject: [PATCH 139/227] Rename onFrame() to update() to make WindowManager consistent with other managers --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/windowmanager.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 330d0e194..2ed6b424b 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -181,7 +181,7 @@ bool OMW::Engine::frame(float frametime) osg::Timer_t afterWorldTick = osg::Timer::instance()->tick(); // update GUI - mEnvironment.getWindowManager()->onFrame(frametime); + mEnvironment.getWindowManager()->update(frametime); unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); osg::Stats* stats = mViewer->getViewerStats(); diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 4fa782b8f..d6afe9243 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -251,7 +251,7 @@ namespace MWBase /// returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) virtual int readPressedButton() = 0; - virtual void onFrame (float frameDuration) = 0; + virtual void update (float duration) = 0; /** * Fetches a GMST string from the store, if there is no setting with the given diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index bcd878d34..7bae74dfc 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -956,7 +956,7 @@ namespace MWGui mHud->setPlayerPos(x, y, u, v); } - void WindowManager::onFrame (float frameDuration) + void WindowManager::update (float frameDuration) { bool gameRunning = MWBase::Environment::get().getStateManager()->getState()!= MWBase::StateManager::State_NoGame; diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 22f407c2b..2de0a4ca8 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -280,7 +280,7 @@ namespace MWGui virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) - virtual void onFrame (float frameDuration); + virtual void update (float duration); /** * Fetches a GMST string from the store, if there is no setting with the given From 2ff04b4e73d317e63622829e7ff4c9d1699500ef Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 26 May 2020 17:10:56 +0400 Subject: [PATCH 140/227] Move TextColours initialization to the TextColours itself --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/textcolours.cpp | 36 ++++++++++++++++++++++++++ apps/openmw/mwgui/textcolours.hpp | 5 ++-- apps/openmw/mwgui/windowmanagerimp.cpp | 32 +---------------------- 4 files changed, 41 insertions(+), 34 deletions(-) create mode 100644 apps/openmw/mwgui/textcolours.cpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2107b74fe..68e3949ba 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -45,7 +45,7 @@ add_openmw_dir (mwgui itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview - draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation + draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwgui/textcolours.cpp b/apps/openmw/mwgui/textcolours.cpp new file mode 100644 index 000000000..77afa5e26 --- /dev/null +++ b/apps/openmw/mwgui/textcolours.cpp @@ -0,0 +1,36 @@ +#include "textcolours.hpp" + +#include + +#include + +namespace MWGui +{ + MyGUI::Colour getTextColour(const std::string& type) + { + return MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=" + type + "}")); + } + + void TextColours::loadColours() + { + header = getTextColour("header"); + normal = getTextColour("normal"); + notify = getTextColour("notify"); + + link = getTextColour("link"); + linkOver = getTextColour("link_over"); + linkPressed = getTextColour("link_pressed"); + + answer = getTextColour("answer"); + answerOver = getTextColour("answer_over"); + answerPressed = getTextColour("answer_pressed"); + + journalLink = getTextColour("journal_link"); + journalLinkOver = getTextColour("journal_link_over"); + journalLinkPressed = getTextColour("journal_link_pressed"); + + journalTopic = getTextColour("journal_topic"); + journalTopicOver = getTextColour("journal_topic_over"); + journalTopicPressed = getTextColour("journal_topic_pressed"); + } +} diff --git a/apps/openmw/mwgui/textcolours.hpp b/apps/openmw/mwgui/textcolours.hpp index 3fa55654b..83bc1d3f5 100644 --- a/apps/openmw/mwgui/textcolours.hpp +++ b/apps/openmw/mwgui/textcolours.hpp @@ -5,14 +5,12 @@ namespace MWGui { - struct TextColours { MyGUI::Colour header; MyGUI::Colour normal; MyGUI::Colour notify; - MyGUI::Colour link; MyGUI::Colour linkOver; MyGUI::Colour linkPressed; @@ -28,6 +26,9 @@ namespace MWGui MyGUI::Colour journalTopic; MyGUI::Colour journalTopicOver; MyGUI::Colour journalTopicPressed; + + public: + void loadColours(); }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7bae74dfc..98bebe889 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -117,19 +117,8 @@ #include "keyboardnavigation.hpp" #include "resourceskin.hpp" -namespace -{ - - MyGUI::Colour getTextColour(const std::string& type) - { - return MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=" + type + "}")); - } - -} - namespace MWGui { - WindowManager::WindowManager( SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, @@ -392,26 +381,7 @@ namespace MWGui int w = MyGUI::RenderManager::getInstance().getViewSize().width; int h = MyGUI::RenderManager::getInstance().getViewSize().height; - mTextColours.header = getTextColour("header"); - mTextColours.normal = getTextColour("normal"); - mTextColours.notify = getTextColour("notify"); - - mTextColours.link = getTextColour("link"); - mTextColours.linkOver = getTextColour("link_over"); - mTextColours.linkPressed = getTextColour("link_pressed"); - - mTextColours.answer = getTextColour("answer"); - mTextColours.answerOver = getTextColour("answer_over"); - mTextColours.answerPressed = getTextColour("answer_pressed"); - - mTextColours.journalLink = getTextColour("journal_link"); - mTextColours.journalLinkOver = getTextColour("journal_link_over"); - mTextColours.journalLinkPressed = getTextColour("journal_link_pressed"); - - mTextColours.journalTopic = getTextColour("journal_topic"); - mTextColours.journalTopicOver = getTextColour("journal_topic_over"); - mTextColours.journalTopicPressed = getTextColour("journal_topic_pressed"); - + mTextColours.loadColours(); mDragAndDrop = new DragAndDrop(); From a8231ae2977f31e79d7266e0cab73e8ecd5dd8b0 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 26 May 2020 19:01:33 +0200 Subject: [PATCH 141/227] fix explicit startscript calls --- apps/openmw/mwscript/miscextensions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 89bf44701..0dd0287a7 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -114,9 +114,9 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { + MWWorld::Ptr target = R()(runtime, false); std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWWorld::Ptr target = R()(runtime, false); MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, target); } }; From 396afe79f128dc8c1ad239eef51b4a019282246e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 28 May 2020 23:09:10 +0400 Subject: [PATCH 142/227] Move font loading to the FontLoader --- apps/openmw/mwgui/windowmanagerimp.cpp | 99 +---------------------- apps/openmw/mwgui/windowmanagerimp.hpp | 4 - components/fontloader/fontloader.cpp | 107 ++++++++++++++++++++++++- components/fontloader/fontloader.hpp | 12 ++- components/widgets/fontwrapper.hpp | 2 +- 5 files changed, 116 insertions(+), 108 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 98bebe889..ab7c3334c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -181,7 +181,6 @@ namespace MWGui , mRestAllowed(true) , mShowOwned(0) , mEncoding(encoding) - , mFontHeight(16) , mVersionDescription(versionDescription) , mWindowVisible(true) { @@ -219,13 +218,6 @@ namespace MWGui SpellView::registerComponents(); Gui::registerAllWidgets(); - int fontSize = Settings::Manager::getInt("font size", "GUI"); - fontSize = std::min(std::max(12, fontSize), 20); - mFontHeight = fontSize; - - MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); - MyGUI::ResourceManager::getInstance().registerLoadXmlDelegate("Resource") = newDelegate(this, &WindowManager::loadFontDelegate); - MyGUI::FactoryManager::getInstance().registerFactory("Controller"); MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); @@ -282,94 +274,6 @@ namespace MWGui Settings::Manager::getFloat("contrast", "Video")); } - void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) - { - MyGUI::xml::ElementEnumerator resourceNode = _node->getElementEnumerator(); - bool createCopy = false; - while (resourceNode.next("Resource")) - { - std::string type, name; - resourceNode->findAttribute("type", type); - resourceNode->findAttribute("name", name); - - if (name.empty()) - continue; - - if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) - { - createCopy = true; - - // For TrueType fonts we should override Size and Resolution properties - // to allow to configure font size via config file, without need to edit XML files. - // Also we should take UI scaling factor in account. - int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); - resolution = std::min(960, std::max(48, resolution)); - - float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); - resolution *= uiScale; - - MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property"); - resolutionNode->addAttribute("key", "Resolution"); - resolutionNode->addAttribute("value", std::to_string(resolution)); - - MyGUI::xml::ElementPtr sizeNode = resourceNode->createChild("Property"); - sizeNode->addAttribute("key", "Size"); - sizeNode->addAttribute("value", std::to_string(mFontHeight)); - } - else if (Misc::StringUtils::ciEqual(type, "ResourceSkin") || - Misc::StringUtils::ciEqual(type, "AutoSizedResourceSkin")) - { - // We should adjust line height for MyGUI widgets depending on font size - MyGUI::xml::ElementPtr heightNode = resourceNode->createChild("Property"); - heightNode->addAttribute("key", "HeightLine"); - heightNode->addAttribute("value", std::to_string(mFontHeight+2)); - } - } - - MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version); - - if (createCopy) - { - MyGUI::xml::ElementPtr copy = _node->createCopy(); - - MyGUI::xml::ElementEnumerator copyFont = copy->getElementEnumerator(); - while (copyFont.next("Resource")) - { - std::string type, name; - copyFont->findAttribute("type", type); - copyFont->findAttribute("name", name); - - if (name.empty()) - continue; - - if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) - { - // Since the journal and books use the custom scaling factor depending on resolution, - // setup separate fonts with different Resolution to fit these windows. - // These fonts have an internal prefix. - int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); - resolution = std::min(960, std::max(48, resolution)); - - float currentX = Settings::Manager::getInt("resolution x", "Video"); - float currentY = Settings::Manager::getInt("resolution y", "Video"); - // TODO: read size from openmw_layout.xml - float heightScale = (currentY / 520); - float widthScale = (currentX / 600); - float uiScale = std::min(widthScale, heightScale); - resolution *= uiScale; - - MyGUI::xml::ElementPtr resolutionNode = copyFont->createChild("Property"); - resolutionNode->addAttribute("key", "Resolution"); - resolutionNode->addAttribute("value", std::to_string(resolution)); - - copyFont->setAttribute("name", "Journalbook " + name); - } - } - - MyGUI::ResourceManager::getInstance().loadFromXmlNode(copy, _file, _version); - } - } - void WindowManager::loadUserFonts() { mFontLoader->loadTrueTypeFonts(); @@ -566,7 +470,7 @@ namespace MWGui int WindowManager::getFontHeight() const { - return mFontHeight; + return mFontLoader->getFontHeight(); } void WindowManager::setNewGame(bool newgame) @@ -587,7 +491,6 @@ namespace MWGui { mKeyboardNavigation.reset(); - MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 2de0a4ca8..42750705d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -411,8 +411,6 @@ namespace MWGui MWWorld::Ptr mSelectedEnchantItem; MWWorld::Ptr mSelectedWeapon; - void loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version); - std::vector mCurrentModals; // Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window). @@ -517,8 +515,6 @@ namespace MWGui ToUTF8::FromType mEncoding; - int mFontHeight; - std::string mVersionDescription; bool mWindowVisible; diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index dae83a1f9..6dcebe0bd 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -21,6 +21,8 @@ #include +#include + namespace { unsigned long utf8ToUnicode(const std::string& utf8) @@ -147,15 +149,24 @@ namespace Gui FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath) : mVFS(vfs) , mUserDataPath(userDataPath) + , mFontHeight(16) { if (encoding == ToUTF8::WINDOWS_1252) mEncoding = ToUTF8::CP437; else mEncoding = encoding; + + int fontSize = Settings::Manager::getInt("font size", "GUI"); + mFontHeight = std::min(std::max(12, fontSize), 20); + + MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); + MyGUI::ResourceManager::getInstance().registerLoadXmlDelegate("Resource") = MyGUI::newDelegate(this, &FontLoader::loadFontFromXml); } FontLoader::~FontLoader() { + MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); + for (std::vector::iterator it = mTextures.begin(); it != mTextures.end(); ++it) delete *it; mTextures.clear(); @@ -190,7 +201,7 @@ namespace Gui { size_t pos = name.find_last_of('.'); if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".fnt") == 0) - loadFont(name, exportToFile); + loadBitmapFont(name, exportToFile); } else break; @@ -238,7 +249,7 @@ namespace Gui float ascent; } GlyphInfo; - void FontLoader::loadFont(const std::string &fileName, bool exportToFile) + void FontLoader::loadBitmapFont(const std::string &fileName, bool exportToFile) { Files::IStreamPtr file = mVFS->get(fileName); @@ -527,4 +538,96 @@ namespace Gui MyGUI::ResourceManager::getInstance().addResource(bookFont); } + void FontLoader::loadFontFromXml(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) + { + MyGUI::xml::ElementEnumerator resourceNode = _node->getElementEnumerator(); + bool createCopy = false; + while (resourceNode.next("Resource")) + { + std::string type, name; + resourceNode->findAttribute("type", type); + resourceNode->findAttribute("name", name); + + if (name.empty()) + continue; + + if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) + { + createCopy = true; + + // For TrueType fonts we should override Size and Resolution properties + // to allow to configure font size via config file, without need to edit XML files. + // Also we should take UI scaling factor in account. + int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::min(960, std::max(48, resolution)); + + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + resolution *= uiScale; + + MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property"); + resolutionNode->addAttribute("key", "Resolution"); + resolutionNode->addAttribute("value", std::to_string(resolution)); + + MyGUI::xml::ElementPtr sizeNode = resourceNode->createChild("Property"); + sizeNode->addAttribute("key", "Size"); + sizeNode->addAttribute("value", std::to_string(mFontHeight)); + } + else if (Misc::StringUtils::ciEqual(type, "ResourceSkin") || + Misc::StringUtils::ciEqual(type, "AutoSizedResourceSkin")) + { + // We should adjust line height for MyGUI widgets depending on font size + MyGUI::xml::ElementPtr heightNode = resourceNode->createChild("Property"); + heightNode->addAttribute("key", "HeightLine"); + heightNode->addAttribute("value", std::to_string(mFontHeight+2)); + } + } + + MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version); + + if (createCopy) + { + MyGUI::xml::ElementPtr copy = _node->createCopy(); + + MyGUI::xml::ElementEnumerator copyFont = copy->getElementEnumerator(); + while (copyFont.next("Resource")) + { + std::string type, name; + copyFont->findAttribute("type", type); + copyFont->findAttribute("name", name); + + if (name.empty()) + continue; + + if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) + { + // Since the journal and books use the custom scaling factor depending on resolution, + // setup separate fonts with different Resolution to fit these windows. + // These fonts have an internal prefix. + int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::min(960, std::max(48, resolution)); + + float currentX = Settings::Manager::getInt("resolution x", "Video"); + float currentY = Settings::Manager::getInt("resolution y", "Video"); + // TODO: read size from openmw_layout.xml somehow + float heightScale = (currentY / 520); + float widthScale = (currentX / 600); + float uiScale = std::min(widthScale, heightScale); + resolution *= uiScale; + + MyGUI::xml::ElementPtr resolutionNode = copyFont->createChild("Property"); + resolutionNode->addAttribute("key", "Resolution"); + resolutionNode->addAttribute("value", std::to_string(resolution)); + + copyFont->setAttribute("name", "Journalbook " + name); + } + } + + MyGUI::ResourceManager::getInstance().loadFromXmlNode(copy, _file, _version); + } + } + + int FontLoader::getFontHeight() + { + return mFontHeight; + } } diff --git a/components/fontloader/fontloader.hpp b/components/fontloader/fontloader.hpp index 39301f9f5..94b022501 100644 --- a/components/fontloader/fontloader.hpp +++ b/components/fontloader/fontloader.hpp @@ -3,6 +3,9 @@ #include "boost/filesystem/operations.hpp" +#include +#include + #include #include @@ -19,8 +22,6 @@ namespace MyGUI namespace Gui { - - /// @brief loads Morrowind's .fnt/.tex fonts for use with MyGUI and OSG /// @note The FontLoader needs to remain in scope as long as you want to use the loaded fonts. class FontLoader @@ -33,16 +34,21 @@ namespace Gui void loadBitmapFonts (bool exportToFile); void loadTrueTypeFonts (); + void loadFontFromXml(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version); + + int getFontHeight(); + private: ToUTF8::FromType mEncoding; const VFS::Manager* mVFS; std::string mUserDataPath; + int mFontHeight; std::vector mTextures; std::vector mFonts; /// @param exportToFile export the converted font (Image and XML with glyph metrics) to files? - void loadFont (const std::string& fileName, bool exportToFile); + void loadBitmapFont (const std::string& fileName, bool exportToFile); FontLoader(const FontLoader&); void operator=(const FontLoader&); diff --git a/components/widgets/fontwrapper.hpp b/components/widgets/fontwrapper.hpp index 84406c70c..8b0011dda 100644 --- a/components/widgets/fontwrapper.hpp +++ b/components/widgets/fontwrapper.hpp @@ -38,7 +38,7 @@ namespace Gui std::string getFontSize() { - // Note: we can not use the WindowManager here, so there is a code duplication a bit. + // Note: we can not use the FontLoader here, so there is a code duplication a bit. static const std::string fontSize = std::to_string(clamp(Settings::Manager::getInt("font size", "GUI"), 12, 20)); return fontSize; } From 48b3fe5733e98ccb88025bb2b0976b7b6e35dd13 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 29 May 2020 12:26:02 +0400 Subject: [PATCH 143/227] Use C++11-style loops in the StateManager --- apps/openmw/mwstate/statemanagerimp.cpp | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 86a26212f..02f4bc7e7 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -240,12 +240,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot ESM::ESMWriter writer; - const std::vector& current = - MWBase::Environment::get().getWorld()->getContentFiles(); - - for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); - ++iter) - writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 + for (const std::string& contentFile : MWBase::Environment::get().getWorld()->getContentFiles()) + writer.addMaster(contentFile, 0); // not using the size information anyway -> use value of 0 writer.setFormat (ESM::SavedGame::sCurrentFormat); @@ -346,10 +342,10 @@ void MWState::StateManager::quickSave (std::string name) if (currentCharacter) { - for (Character::SlotIterator it = currentCharacter->begin(); it != currentCharacter->end(); ++it) + for (auto& save : *currentCharacter) { //Visiting slots allows the quicksave finder to find the oldest quicksave - saveFinder.visitSave(&*it); + saveFinder.visitSave(&save); } } @@ -360,12 +356,10 @@ void MWState::StateManager::quickSave (std::string name) void MWState::StateManager::loadGame(const std::string& filepath) { - for (CharacterIterator it = mCharacterManager.begin(); it != mCharacterManager.end(); ++it) + for (const auto& character : mCharacterManager) { - const MWState::Character& character = *it; - for (MWState::Character::SlotIterator slotIt = character.begin(); slotIt != character.end(); ++slotIt) + for (const auto& slot : character) { - const MWState::Slot& slot = *slotIt; if (slot.mPath == boost::filesystem::path(filepath)) { loadGame(&character, slot.mPath.string()); @@ -630,13 +624,12 @@ bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const { const std::vector& selectedContentFiles = MWBase::Environment::get().getWorld()->getContentFiles(); bool notFound = false; - for (std::vector::const_iterator it = profile.mContentFiles.begin(); - it != profile.mContentFiles.end(); ++it) + for (const std::string& contentFile : profile.mContentFiles) { - if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), *it) + if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), contentFile) == selectedContentFiles.end()) { - Log(Debug::Warning) << "Warning: Saved game dependency " << *it << " is missing."; + Log(Debug::Warning) << "Warning: Saved game dependency " << contentFile << " is missing."; notFound = true; } } From efd5f13b2b076e7ad5739f72489c7b789a496871 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 17 May 2020 04:06:39 +0300 Subject: [PATCH 144/227] Make greeting-related actor data temporary (bug #5397) --- CHANGELOG.md | 1 + apps/openmw/mwbase/mechanicsmanager.hpp | 8 +++ apps/openmw/mwmechanics/actor.cpp | 40 +++++++++++ apps/openmw/mwmechanics/actor.hpp | 18 +++++ apps/openmw/mwmechanics/actors.cpp | 71 ++++++++++++++----- apps/openmw/mwmechanics/actors.hpp | 11 ++- apps/openmw/mwmechanics/actorutil.hpp | 7 ++ apps/openmw/mwmechanics/aitravel.cpp | 6 +- apps/openmw/mwmechanics/aiwander.cpp | 4 +- apps/openmw/mwmechanics/creaturestats.cpp | 43 +---------- apps/openmw/mwmechanics/creaturestats.hpp | 24 ------- .../mwmechanics/mechanicsmanagerimp.cpp | 20 ++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 5 ++ apps/openmw/mwscript/aiextensions.cpp | 10 +-- 14 files changed, 173 insertions(+), 95 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f293cb2..dea84b56b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Bug #5367: Selecting a spell on an enchanted item per hotkey always plays the equip sound Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures Bug #5370: Opening an unlocked but trapped door uses the key + Bug #5397: NPC greeting does not reset if you leave + reenter area Bug #5400: Editor: Verifier checks race of non-skin bodyparts Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work Bug #5416: Junk non-node records before the root node are not handled gracefully diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index cca789a40..3bde83e94 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -7,6 +7,9 @@ #include #include +#include "../mwmechanics/actorutil.hpp" +// For MWMechanics::GreetingState + #include "../mwworld/ptr.hpp" namespace osg @@ -272,6 +275,11 @@ namespace MWBase virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0; virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0; + + virtual int getGreetingTimer(const MWWorld::Ptr& ptr) const = 0; + virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const = 0; + virtual MWMechanics::GreetingState getGreetingState(const MWWorld::Ptr& ptr) const = 0; + virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const = 0; }; } diff --git a/apps/openmw/mwmechanics/actor.cpp b/apps/openmw/mwmechanics/actor.cpp index f7c6b7f1c..a5c55633a 100644 --- a/apps/openmw/mwmechanics/actor.cpp +++ b/apps/openmw/mwmechanics/actor.cpp @@ -18,4 +18,44 @@ namespace MWMechanics { return mCharacterController.get(); } + + int Actor::getGreetingTimer() const + { + return mGreetingTimer; + } + + void Actor::setGreetingTimer(int timer) + { + mGreetingTimer = timer; + } + + float Actor::getAngleToPlayer() const + { + return mTargetAngleRadians; + } + + void Actor::setAngleToPlayer(float angle) + { + mTargetAngleRadians = angle; + } + + GreetingState Actor::getGreetingState() const + { + return mGreetingState; + } + + void Actor::setGreetingState(GreetingState state) + { + mGreetingState = state; + } + + bool Actor::isTurningToPlayer() const + { + return mIsTurningToPlayer; + } + + void Actor::setTurningToPlayer(bool turning) + { + mIsTurningToPlayer = turning; + } } diff --git a/apps/openmw/mwmechanics/actor.hpp b/apps/openmw/mwmechanics/actor.hpp index 119527b64..287ca420f 100644 --- a/apps/openmw/mwmechanics/actor.hpp +++ b/apps/openmw/mwmechanics/actor.hpp @@ -3,6 +3,8 @@ #include +#include "../mwmechanics/actorutil.hpp" + namespace MWRender { class Animation; @@ -27,8 +29,24 @@ namespace MWMechanics CharacterController* getCharacterController(); + int getGreetingTimer() const; + void setGreetingTimer(int timer); + + float getAngleToPlayer() const; + void setAngleToPlayer(float angle); + + GreetingState getGreetingState() const; + void setGreetingState(GreetingState state); + + bool isTurningToPlayer() const; + void setTurningToPlayer(bool turning); + private: std::unique_ptr mCharacterController; + int mGreetingTimer{0}; + float mTargetAngleRadians{0.f}; + GreetingState mGreetingState{Greet_None}; + bool mIsTurningToPlayer{false}; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 223d9fc34..adc13efa7 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -447,7 +447,7 @@ namespace MWMechanics actor.getClass().getMovementSettings(actor).mSpeedFactor = newSpeedFactor; } - void Actors::updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly) + void Actors::updateGreetingState(const MWWorld::Ptr& actor, Actor* actorState, bool turnOnly) { if (!actor.getClass().isActor() || actor == getPlayer()) return; @@ -460,9 +460,9 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->isSwimming(actor) || (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1)) { - stats.setTurningToPlayer(false); - stats.setGreetingTimer(0); - stats.setGreetingState(Greet_None); + actorState->setTurningToPlayer(false); + actorState->setGreetingTimer(0); + actorState->setGreetingState(Greet_None); return; } @@ -471,14 +471,14 @@ namespace MWMechanics osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); osg::Vec3f dir = playerPos - actorPos; - if (stats.isTurningToPlayer()) + if (actorState->isTurningToPlayer()) { // Reduce the turning animation glitch by using a *HUGE* value of // epsilon... TODO: a proper fix might be in either the physics or the // animation subsystem - if (zTurn(actor, stats.getAngleToPlayer(), osg::DegreesToRadians(5.f))) + if (zTurn(actor, actorState->getAngleToPlayer(), osg::DegreesToRadians(5.f))) { - stats.setTurningToPlayer(false); + actorState->setTurningToPlayer(false); // An original engine launches an endless idle2 when an actor greets player. playAnimationGroup (actor, "idle2", 0, std::numeric_limits::max(), false); } @@ -493,8 +493,8 @@ namespace MWMechanics float helloDistance = static_cast(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier); - int greetingTimer = stats.getGreetingTimer(); - GreetingState greetingState = stats.getGreetingState(); + int greetingTimer = actorState->getGreetingTimer(); + GreetingState greetingState = actorState->getGreetingState(); if (greetingState == Greet_None) { if ((playerPos - actorPos).length2() <= helloDistance*helloDistance && @@ -516,7 +516,7 @@ namespace MWMechanics greetingTimer++; if (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor)) - turnActorToFacePlayer(actor, dir); + turnActorToFacePlayer(actor, actorState, dir); if (greetingTimer >= GREETING_COOLDOWN) { @@ -532,20 +532,19 @@ namespace MWMechanics greetingState = Greet_None; } - stats.setGreetingTimer(greetingTimer); - stats.setGreetingState(greetingState); + actorState->setGreetingTimer(greetingTimer); + actorState->setGreetingState(greetingState); } - void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir) + void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor* actorState, const osg::Vec3f& dir) { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[0] = 0; - CreatureStats &stats = actor.getClass().getCreatureStats(actor); - if (!stats.isTurningToPlayer()) + if (!actorState->isTurningToPlayer()) { - stats.setAngleToPlayer(std::atan2(dir.x(), dir.y())); - stats.setTurningToPlayer(true); + actorState->setAngleToPlayer(std::atan2(dir.x(), dir.y())); + actorState->setTurningToPlayer(true); } } @@ -1695,7 +1694,7 @@ namespace MWMechanics if (isConscious(iter->first)) { stats.getAiSequence().execute(iter->first, *ctrl, duration); - updateGreetingState(iter->first, timerUpdateHello > 0); + updateGreetingState(iter->first, iter->second, timerUpdateHello > 0); playIdleDialogue(iter->first); updateMovementSpeed(iter->first); } @@ -2390,6 +2389,42 @@ namespace MWMechanics return ctrl->isAttackingOrSpell(); } + int Actors::getGreetingTimer(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return 0; + + return it->second->getGreetingTimer(); + } + + float Actors::getAngleToPlayer(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return 0.f; + + return it->second->getAngleToPlayer(); + } + + GreetingState Actors::getGreetingState(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return Greet_None; + + return it->second->getGreetingState(); + } + + bool Actors::isTurningToPlayer(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return false; + + return it->second->isTurningToPlayer(); + } + void Actors::fastForwardAi() { if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 4e952d1c8..25716d392 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -7,6 +7,8 @@ #include #include +#include "../mwmechanics/actorutil.hpp" + namespace ESM { class ESMReader; @@ -123,8 +125,8 @@ namespace MWMechanics void playIdleDialogue(const MWWorld::Ptr& actor); void updateMovementSpeed(const MWWorld::Ptr& actor); - void updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly); - void turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir); + void updateGreetingState(const MWWorld::Ptr& actor, Actor* actorState, bool turnOnly); + void turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor* actorState, const osg::Vec3f& dir); void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance); @@ -195,6 +197,11 @@ namespace MWMechanics bool isReadyToBlock(const MWWorld::Ptr& ptr) const; bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const; + int getGreetingTimer(const MWWorld::Ptr& ptr) const; + float getAngleToPlayer(const MWWorld::Ptr& ptr) const; + GreetingState getGreetingState(const MWWorld::Ptr& ptr) const; + bool isTurningToPlayer(const MWWorld::Ptr& ptr) const; + private: void updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl); diff --git a/apps/openmw/mwmechanics/actorutil.hpp b/apps/openmw/mwmechanics/actorutil.hpp index 82a904799..cf3d92558 100644 --- a/apps/openmw/mwmechanics/actorutil.hpp +++ b/apps/openmw/mwmechanics/actorutil.hpp @@ -8,6 +8,13 @@ namespace MWWorld namespace MWMechanics { + enum GreetingState + { + Greet_None, + Greet_InProgress, + Greet_Done + }; + MWWorld::Ptr getPlayer(); bool isPlayerInCombat(); bool canActorMoveByZAxis(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index dba70316b..822523c76 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -3,6 +3,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" @@ -43,14 +44,15 @@ namespace MWMechanics bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { - auto& stats = actor.getClass().getCreatureStats(actor); + MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager(); - if (stats.isTurningToPlayer() || stats.getGreetingState() == Greet_InProgress) + if (mechMgr->isTurningToPlayer(actor) || mechMgr->getGreetingState(actor) == Greet_InProgress) return false; const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); const osg::Vec3f targetPos(mX, mY, mZ); + auto& stats = actor.getClass().getCreatureStats(actor); stats.setMovementFlag(CreatureStats::Flag_Run, false); stats.setDrawState(DrawState_Nothing); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 597453409..2c40c1ba5 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -206,7 +206,7 @@ namespace MWMechanics storage.setState(AiWanderStorage::Wander_Walking); } - GreetingState greetingState = cStats.getGreetingState(); + GreetingState greetingState = MWBase::Environment::get().getMechanicsManager()->getGreetingState(actor); if (greetingState == Greet_InProgress) { if (storage.mState == AiWanderStorage::Wander_Walking) @@ -442,7 +442,7 @@ namespace MWMechanics } // Check if idle animation finished - GreetingState greetingState = actor.getClass().getCreatureStats(actor).getGreetingState(); + GreetingState greetingState = MWBase::Environment::get().getMechanicsManager()->getGreetingState(actor); if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None)) { if (mPathFinder.isPathConstructed()) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 1c377540a..800b5c22f 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -23,53 +23,12 @@ namespace MWMechanics mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1), - mDeathAnimation(-1), mTimeOfDeath(), mGreetingState(Greet_None), - mGreetingTimer(0), mTargetAngleRadians(0), mIsTurningToPlayer(false), mLevel (0) + mDeathAnimation(-1), mTimeOfDeath(), mLevel (0) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; } - int MWMechanics::CreatureStats::getGreetingTimer() const - { - return mGreetingTimer; - } - - void MWMechanics::CreatureStats::setGreetingTimer(int timer) - { - mGreetingTimer = timer; - } - - float MWMechanics::CreatureStats::getAngleToPlayer() const - { - return mTargetAngleRadians; - } - - void MWMechanics::CreatureStats::setAngleToPlayer(float angle) - { - mTargetAngleRadians = angle; - } - - GreetingState MWMechanics::CreatureStats::getGreetingState() const - { - return mGreetingState; - } - - void MWMechanics::CreatureStats::setGreetingState(GreetingState state) - { - mGreetingState = state; - } - - bool MWMechanics::CreatureStats::isTurningToPlayer() const - { - return mIsTurningToPlayer; - } - - void MWMechanics::CreatureStats::setTurningToPlayer(bool turning) - { - mIsTurningToPlayer = turning; - } - const AiSequence& CreatureStats::getAiSequence() const { return mAiSequence; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 7c4a83db1..a4ecb23b3 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -19,13 +19,6 @@ namespace ESM namespace MWMechanics { - enum GreetingState - { - Greet_None, - Greet_InProgress, - Greet_Done - }; - /// \brief Common creature stats /// /// @@ -77,11 +70,6 @@ namespace MWMechanics MWWorld::TimeStamp mTimeOfDeath; - GreetingState mGreetingState; - int mGreetingTimer; - float mTargetAngleRadians; - bool mIsTurningToPlayer; - public: typedef std::pair SummonKey; // private: @@ -97,18 +85,6 @@ namespace MWMechanics public: CreatureStats(); - int getGreetingTimer() const; - void setGreetingTimer(int timer); - - float getAngleToPlayer() const; - void setAngleToPlayer(float angle); - - GreetingState getGreetingState() const; - void setGreetingState(GreetingState state); - - bool isTurningToPlayer() const; - void setTurningToPlayer(bool turning); - DrawState_ getDrawState() const; void setDrawState(DrawState_ state); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5ca7b3cdd..88045ec44 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1951,4 +1951,24 @@ namespace MWMechanics stats.setAttribute(frameNumber, "Mechanics Actors", mActors.size()); stats.setAttribute(frameNumber, "Mechanics Objects", mObjects.size()); } + + int MechanicsManager::getGreetingTimer(const MWWorld::Ptr &ptr) const + { + return mActors.getGreetingTimer(ptr); + } + + float MechanicsManager::getAngleToPlayer(const MWWorld::Ptr &ptr) const + { + return mActors.getAngleToPlayer(ptr); + } + + GreetingState MechanicsManager::getGreetingState(const MWWorld::Ptr &ptr) const + { + return mActors.getGreetingState(ptr); + } + + bool MechanicsManager::isTurningToPlayer(const MWWorld::Ptr &ptr) const + { + return mActors.isTurningToPlayer(ptr); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 83d1b236f..6785f979f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -242,6 +242,11 @@ namespace MWMechanics virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; + virtual int getGreetingTimer(const MWWorld::Ptr& ptr) const override; + virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const override; + virtual GreetingState getGreetingState(const MWWorld::Ptr& ptr) const override; + virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const override; + private: bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set &playerFollowers); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 79639197d..603ed8836 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -430,13 +430,13 @@ namespace MWScript if (!targetPtr.isEmpty() && targetPtr.getCellRef().getRefId() == testedTargetId) targetsAreEqual = true; } - else + else if (testedTargetId == "player") // Currently the player ID is hardcoded { - bool turningToPlayer = creatureStats.isTurningToPlayer(); - bool greeting = creatureStats.getGreetingState() == MWMechanics::Greet_InProgress; + MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager(); + bool greeting = mechMgr->getGreetingState(actor) == MWMechanics::Greet_InProgress; bool sayActive = MWBase::Environment::get().getSoundManager()->sayActive(actor); - if (turningToPlayer || (greeting && sayActive)) - targetsAreEqual = (testedTargetId == "player"); // Currently the player ID is hardcoded + if ((greeting && sayActive) || mechMgr->isTurningToPlayer(actor)) + targetsAreEqual = true; } runtime.push(int(targetsAreEqual)); } From b0b4550f0593e4ab8b8fa13b687fbbc644e6f0af Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 18 May 2020 23:04:48 +0300 Subject: [PATCH 145/227] Pass Actor by reference, simplify GetTarget for greetings --- apps/openmw/mwmechanics/actors.cpp | 32 +++++++++++++-------------- apps/openmw/mwmechanics/actors.hpp | 4 ++-- apps/openmw/mwscript/aiextensions.cpp | 3 +-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index adc13efa7..873d5fa7c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -447,7 +447,7 @@ namespace MWMechanics actor.getClass().getMovementSettings(actor).mSpeedFactor = newSpeedFactor; } - void Actors::updateGreetingState(const MWWorld::Ptr& actor, Actor* actorState, bool turnOnly) + void Actors::updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly) { if (!actor.getClass().isActor() || actor == getPlayer()) return; @@ -460,9 +460,9 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->isSwimming(actor) || (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1)) { - actorState->setTurningToPlayer(false); - actorState->setGreetingTimer(0); - actorState->setGreetingState(Greet_None); + actorState.setTurningToPlayer(false); + actorState.setGreetingTimer(0); + actorState.setGreetingState(Greet_None); return; } @@ -471,14 +471,14 @@ namespace MWMechanics osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); osg::Vec3f dir = playerPos - actorPos; - if (actorState->isTurningToPlayer()) + if (actorState.isTurningToPlayer()) { // Reduce the turning animation glitch by using a *HUGE* value of // epsilon... TODO: a proper fix might be in either the physics or the // animation subsystem - if (zTurn(actor, actorState->getAngleToPlayer(), osg::DegreesToRadians(5.f))) + if (zTurn(actor, actorState.getAngleToPlayer(), osg::DegreesToRadians(5.f))) { - actorState->setTurningToPlayer(false); + actorState.setTurningToPlayer(false); // An original engine launches an endless idle2 when an actor greets player. playAnimationGroup (actor, "idle2", 0, std::numeric_limits::max(), false); } @@ -493,8 +493,8 @@ namespace MWMechanics float helloDistance = static_cast(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier); - int greetingTimer = actorState->getGreetingTimer(); - GreetingState greetingState = actorState->getGreetingState(); + int greetingTimer = actorState.getGreetingTimer(); + GreetingState greetingState = actorState.getGreetingState(); if (greetingState == Greet_None) { if ((playerPos - actorPos).length2() <= helloDistance*helloDistance && @@ -532,19 +532,19 @@ namespace MWMechanics greetingState = Greet_None; } - actorState->setGreetingTimer(greetingTimer); - actorState->setGreetingState(greetingState); + actorState.setGreetingTimer(greetingTimer); + actorState.setGreetingState(greetingState); } - void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor* actorState, const osg::Vec3f& dir) + void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir) { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[0] = 0; - if (!actorState->isTurningToPlayer()) + if (!actorState.isTurningToPlayer()) { - actorState->setAngleToPlayer(std::atan2(dir.x(), dir.y())); - actorState->setTurningToPlayer(true); + actorState.setAngleToPlayer(std::atan2(dir.x(), dir.y())); + actorState.setTurningToPlayer(true); } } @@ -1694,7 +1694,7 @@ namespace MWMechanics if (isConscious(iter->first)) { stats.getAiSequence().execute(iter->first, *ctrl, duration); - updateGreetingState(iter->first, iter->second, timerUpdateHello > 0); + updateGreetingState(iter->first, *iter->second, timerUpdateHello > 0); playIdleDialogue(iter->first); updateMovementSpeed(iter->first); } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 25716d392..bd5a14c0d 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -125,8 +125,8 @@ namespace MWMechanics void playIdleDialogue(const MWWorld::Ptr& actor); void updateMovementSpeed(const MWWorld::Ptr& actor); - void updateGreetingState(const MWWorld::Ptr& actor, Actor* actorState, bool turnOnly); - void turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor* actorState, const osg::Vec3f& dir); + void updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly); + void turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir); void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 603ed8836..05c07a972 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -435,8 +435,7 @@ namespace MWScript MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager(); bool greeting = mechMgr->getGreetingState(actor) == MWMechanics::Greet_InProgress; bool sayActive = MWBase::Environment::get().getSoundManager()->sayActive(actor); - if ((greeting && sayActive) || mechMgr->isTurningToPlayer(actor)) - targetsAreEqual = true; + targetsAreEqual = (greeting && sayActive) || mechMgr->isTurningToPlayer(actor); } runtime.push(int(targetsAreEqual)); } From 577786f1109eb40f4a283e64a5d350e6d830c462 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 24 May 2020 19:03:54 +0300 Subject: [PATCH 146/227] Don't disable player's collision shape in TCL (#5435) --- CHANGELOG.md | 1 + apps/openmw/mwphysics/physicssystem.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f293cb2..64b1526d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Bug #5424: Creatures do not headtrack player Bug #5425: Poison effect only appears for one frame Bug #5427: GetDistance unknown ID error is misleading + Bug #5435: Enemies can't hurt the player when collision is off Bug #5441: Enemies can't push a player character when in critical strike stance Feature #5362: Show the soul gems' trapped soul in count dialog diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 808b1e75a..7cc0f7770 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -648,7 +648,7 @@ namespace MWPhysics bool cmode = found->second->getCollisionMode(); cmode = !cmode; found->second->enableCollisionMode(cmode); - found->second->enableCollisionBody(cmode); + // NB: Collision body isn't disabled for vanilla TCL compatibility return cmode; } From 7aca18f92b2bd91f8e22a41c89dd4376f3e262dd Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 30 May 2020 17:59:36 +0300 Subject: [PATCH 147/227] Handle NiLines (feature #5445) --- CHANGELOG.md | 1 + components/nif/data.cpp | 26 ++++++++++++++ components/nif/data.hpp | 8 +++++ components/nif/niffile.cpp | 2 ++ components/nif/node.hpp | 20 +++++++++++ components/nif/record.hpp | 2 ++ components/nif/recordptr.hpp | 2 ++ components/nifosg/nifloader.cpp | 64 ++++++++++++++++++++------------- 8 files changed, 101 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f293cb2..48cbe2ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Bug #5427: GetDistance unknown ID error is misleading Bug #5441: Enemies can't push a player character when in critical strike stance Feature #5362: Show the soul gems' trapped soul in count dialog + Feature #5445: Handle NiLines 0.46.0 ------ diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 8ae49476b..82865faa7 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -110,6 +110,32 @@ void NiTriStripsData::read(NIFStream *nif) nif->getUShorts(strips[i], lengths[i]); } +void NiLinesData::read(NIFStream *nif) +{ + NiGeometryData::read(nif); + size_t num = vertices.size(); + std::vector flags; + nif->getChars(flags, num); + // Can't construct a line from a single vertex. + if (num < 2) + return; + // Convert connectivity flags into usable geometry. The last element needs special handling. + for (size_t i = 0; i < num-1; ++i) + { + if (flags[i] & 1) + { + lines.emplace_back(i); + lines.emplace_back(i+1); + } + } + // If there are just two vertices, they can be connected twice. Probably isn't critical. + if (flags[num-1] & 1) + { + lines.emplace_back(num-1); + lines.emplace_back(0); + } +} + void NiAutoNormalParticlesData::read(NIFStream *nif) { NiGeometryData::read(nif); diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 33818810a..66b3f693a 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -62,6 +62,14 @@ public: void read(NIFStream *nif); }; +struct NiLinesData : public NiGeometryData +{ + // Lines, series of indices that correspond to connected vertices. + std::vector lines; + + void read(NIFStream *nif); +}; + class NiAutoNormalParticlesData : public NiGeometryData { public: diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 13c9ced60..b33f0c051 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -54,6 +54,7 @@ static std::map makeFactory() newFactory.insert(makeEntry("NiBillboardNode", &construct , RC_NiBillboardNode )); newFactory.insert(makeEntry("NiTriShape", &construct , RC_NiTriShape )); newFactory.insert(makeEntry("NiTriStrips", &construct , RC_NiTriStrips )); + newFactory.insert(makeEntry("NiLines", &construct , RC_NiLines )); newFactory.insert(makeEntry("NiRotatingParticles", &construct , RC_NiRotatingParticles )); newFactory.insert(makeEntry("NiAutoNormalParticles", &construct , RC_NiAutoNormalParticles )); newFactory.insert(makeEntry("NiCamera", &construct , RC_NiCamera )); @@ -97,6 +98,7 @@ static std::map makeFactory() newFactory.insert(makeEntry("NiFloatData", &construct , RC_NiFloatData )); newFactory.insert(makeEntry("NiTriShapeData", &construct , RC_NiTriShapeData )); newFactory.insert(makeEntry("NiTriStripsData", &construct , RC_NiTriStripsData )); + newFactory.insert(makeEntry("NiLinesData", &construct , RC_NiLinesData )); newFactory.insert(makeEntry("NiVisData", &construct , RC_NiVisData )); newFactory.insert(makeEntry("NiColorData", &construct , RC_NiColorData )); newFactory.insert(makeEntry("NiPixelData", &construct , RC_NiPixelData )); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 06a1a3b76..e605df32a 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -182,6 +182,26 @@ struct NiTriStrips : NiGeometry } }; +struct NiLines : NiGeometry +{ + NiLinesDataPtr data; + + void read(NIFStream *nif) + { + Node::read(nif); + data.read(nif); + skin.read(nif); + } + + void post(NIFFile *nif) + { + Node::post(nif); + data.post(nif); + skin.post(nif); + if (!skin.empty()) + nif->setUseSkinning(true); + } +}; struct NiCamera : Node { diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 074dea9cf..67202d2fe 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -43,6 +43,7 @@ enum RecordType RC_NiCollisionSwitch, RC_NiTriShape, RC_NiTriStrips, + RC_NiLines, RC_NiRotatingParticles, RC_NiAutoNormalParticles, RC_NiBSParticleNode, @@ -83,6 +84,7 @@ enum RecordType RC_NiFloatData, RC_NiTriShapeData, RC_NiTriStripsData, + RC_NiLinesData, RC_NiVisData, RC_NiColorData, RC_NiPixelData, diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 478ecfdbb..57607cb6a 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -142,6 +142,7 @@ class NiRotatingParticlesData; class NiAutoNormalParticlesData; class NiPalette; struct NiParticleModifier; +struct NiLinesData; using NodePtr = RecordPtrT; using ExtraPtr = RecordPtrT; @@ -158,6 +159,7 @@ using NiColorDataPtr = RecordPtrT; using NiKeyframeDataPtr = RecordPtrT; using NiTriShapeDataPtr = RecordPtrT; using NiTriStripsDataPtr = RecordPtrT; +using NiLinesDataPtr = RecordPtrT; using NiSkinInstancePtr = RecordPtrT; using NiSourceTexturePtr = RecordPtrT; using NiRotatingParticlesDataPtr = RecordPtrT; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 131f0c470..bff414707 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -612,7 +612,9 @@ namespace NifOsg applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags); - if ((nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips) && !skipMeshes) + const bool isGeometry = nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines; + + if (isGeometry && !skipMeshes) { const std::string nodeName = Misc::StringUtils::lowerCase(nifNode->name); static const std::string markerName = "tri editormarker"; @@ -624,9 +626,9 @@ namespace NifOsg Nif::NiSkinInstancePtr skin = static_cast(nifNode)->skin; if (skin.empty()) - handleTriShape(nifNode, node, composite, boundTextures, animflags); + handleGeometry(nifNode, node, composite, boundTextures, animflags); else - handleSkinnedTriShape(nifNode, node, composite, boundTextures, animflags); + handleSkinnedGeometry(nifNode, node, composite, boundTextures, animflags); if (!nifNode->controller.empty()) handleMeshControllers(nifNode, node, composite, boundTextures, animflags); @@ -1099,8 +1101,11 @@ namespace NifOsg partsys->getOrCreateStateSet(); } - void triCommonToGeometry(osg::Geometry *geometry, const std::vector& vertices, const std::vector& normals, const std::vector>& uvlist, const std::vector& colors, const std::vector& boundTextures, const std::string& name) + void handleNiGeometryData(osg::Geometry *geometry, const Nif::NiGeometryData* data, const std::vector& boundTextures, const std::string& name) { + const auto& vertices = data->vertices; + const auto& normals = data->normals; + const auto& colors = data->colors; if (!vertices.empty()) geometry->setVertexArray(new osg::Vec3Array(vertices.size(), vertices.data())); if (!normals.empty()) @@ -1108,6 +1113,7 @@ namespace NifOsg if (!colors.empty()) geometry->setColorArray(new osg::Vec4Array(colors.size(), colors.data()), osg::Array::BIND_PER_VERTEX); + const auto& uvlist = data->uvlist; int textureStage = 0; for (const unsigned int uvSet : boundTextures) { @@ -1124,43 +1130,53 @@ namespace NifOsg } } - void triShapeToGeometry(const Nif::Node *nifNode, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + void handleNiGeometry(const Nif::Node *nifNode, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - bool vertexColorsPresent = false; + const Nif::NiGeometryData* niGeometryData = nullptr; if (nifNode->recType == Nif::RC_NiTriShape) { const Nif::NiTriShape* triShape = static_cast(nifNode); if (!triShape->data.empty()) { const Nif::NiTriShapeData* data = triShape->data.getPtr(); - vertexColorsPresent = !data->colors.empty(); - triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triShape->name); + niGeometryData = static_cast(data); if (!data->triangles.empty()) geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(), (unsigned short*)data->triangles.data())); } } - else + else if (nifNode->recType == Nif::RC_NiTriStrips) { const Nif::NiTriStrips* triStrips = static_cast(nifNode); if (!triStrips->data.empty()) { const Nif::NiTriStripsData* data = triStrips->data.getPtr(); - vertexColorsPresent = !data->colors.empty(); - triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triStrips->name); + niGeometryData = static_cast(data); if (!data->strips.empty()) { - for (const std::vector& strip : data->strips) + for (const auto& strip : data->strips) { - // Can't make a triangle from less than three vertices. - if (strip.size() < 3) - continue; - geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), - (unsigned short*)strip.data())); + if (strip.size() >= 3) + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), + (unsigned short*)strip.data())); } } } } + else if (nifNode->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) + 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. @@ -1168,15 +1184,15 @@ namespace NifOsg // above the actual renderable would be tedious. std::vector drawableProps; collectDrawableProperties(nifNode, drawableProps); - applyDrawableProperties(parentNode, drawableProps, composite, vertexColorsPresent, animflags); + applyDrawableProperties(parentNode, drawableProps, composite, niGeometryData && !niGeometryData->colors.empty(), animflags); } - void handleTriShape(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips); + assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines); osg::ref_ptr drawable; osg::ref_ptr geom (new osg::Geometry); - triShapeToGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags); + handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags); for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) @@ -1215,12 +1231,12 @@ namespace NifOsg return morphGeom; } - void handleSkinnedTriShape(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, + void handleSkinnedGeometry(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips); + assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines); osg::ref_ptr geometry (new osg::Geometry); - triShapeToGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags); + handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags); osg::ref_ptr rig(new SceneUtil::RigGeometry); rig->setSourceGeometry(geometry); rig->setName(nifNode->name); From d72152183f9cd8d4aeba13ab5ae8a720bb645993 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 30 May 2020 21:41:25 +0300 Subject: [PATCH 148/227] Update spell effects during death animation (#5403) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/actors.cpp | 7 ++++++- apps/openmw/mwmechanics/spellcasting.cpp | 13 +++++++++---- apps/openmw/mwworld/inventorystore.cpp | 3 ++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85d65526d..62dd1c2db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Bug #5370: Opening an unlocked but trapped door uses the key Bug #5397: NPC greeting does not reset if you leave + reenter area Bug #5400: Editor: Verifier checks race of non-skin bodyparts + Bug #5403: Enchantment effect doesn't show on an enemy during death animation Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work Bug #5416: Junk non-node records before the root node are not handled gracefully Bug #5424: Creatures do not headtrack player diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 873d5fa7c..41836c74f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1623,9 +1623,14 @@ namespace MWMechanics iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); - // For dead actors we need to remove looping spell particles + // For dead actors we need to update looping spell particles if (iter->first.getClass().getCreatureStats(iter->first).isDead()) + { + // They can be added during the death animation + if (!iter->first.getClass().getCreatureStats(iter->first).isDeathAnimationFinished()) + adjustMagicEffects(iter->first); ctrl->updateContinuousVfx(); + } else { bool cellChanged = world->hasCellChanged(); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 7aec5d471..9f7108239 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -60,8 +60,13 @@ namespace MWMechanics void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded) { - if (!target.isEmpty() && target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead()) - return; + if (!target.isEmpty() && target.getClass().isActor()) + { + // Early-out for characters that have departed. + const auto& stats = target.getClass().getCreatureStats(target); + if (stats.isDead() && stats.isDeathAnimationFinished()) + return; + } // If none of the effects need to apply, we can early-out bool found = false; @@ -201,9 +206,9 @@ namespace MWMechanics } bool effectAffectsHealth = isHarmful || effectIt->mEffectID == ESM::MagicEffect::RestoreHealth; - if (castByPlayer && target != caster && effectAffectsHealth) + if (castByPlayer && target != caster && !target.getClass().getCreatureStats(target).isDead() && effectAffectsHealth) { - // If player is attempting to cast a harmful spell or is healing someone, show the target's HP bar. + // If player is attempting to cast a harmful spell on or is healing a living target, show the target's HP bar. MWBase::Environment::get().getWindowManager()->setEnemy(target); } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index d4358532c..7ef0dd09a 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -572,7 +572,8 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) mMagicEffects = MWMechanics::MagicEffects(); - if (actor.getClass().getCreatureStats(actor).isDead()) + const auto& stats = actor.getClass().getCreatureStats(actor); + if (stats.isDead() && stats.isDeathAnimationFinished()) return; for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter) From ce7c47ee121043b3edb78ae7fceb842604e17c62 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 1 Jun 2020 15:36:14 +0200 Subject: [PATCH 149/227] Return cloned AiPackage as unique_ptr --- apps/openmw/mwmechanics/aipackage.hpp | 4 +++- apps/openmw/mwmechanics/aisequence.cpp | 8 ++++---- apps/openmw/mwmechanics/typedaipackage.hpp | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index a09362baa..873ad1c29 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWMECHANICS_AIPACKAGE_H #define GAME_MWMECHANICS_AIPACKAGE_H +#include + #include #include "pathfinding.hpp" @@ -59,7 +61,7 @@ namespace MWMechanics virtual ~AiPackage() = default; ///Clones the package - virtual AiPackage *clone() const = 0; + virtual std::unique_ptr clone() const = 0; /// Updates and runs the package (Should run every frame) /// \return Package completed? diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 00d44202a..8ed1f0bee 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -27,7 +27,7 @@ void AiSequence::copy (const AiSequence& sequence) { for (std::list::const_iterator iter (sequence.mPackages.begin()); iter!=sequence.mPackages.end(); ++iter) - mPackages.push_back ((*iter)->clone()); + mPackages.push_back ((*iter)->clone().release()); // We need to keep an AiWander storage, if present - it has a state machine. // Not sure about another temporary storages @@ -288,7 +288,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat())) { package->reset(); - mPackages.push_back(package->clone()); + mPackages.push_back(package->clone().release()); } // To account for the rare case where AiPackage::execute() queued another AI package // (e.g. AiPursue executing a dialogue script that uses startCombat) @@ -380,12 +380,12 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo if((*it)->getPriority() <= package.getPriority()) { - mPackages.insert(it,package.clone()); + mPackages.insert(it,package.clone().release()); return; } } - mPackages.push_back (package.clone()); + mPackages.push_back (package.clone().release()); // Make sure that temporary storage is empty if (cancelOther) diff --git a/apps/openmw/mwmechanics/typedaipackage.hpp b/apps/openmw/mwmechanics/typedaipackage.hpp index 2801accc4..e2b5f8688 100644 --- a/apps/openmw/mwmechanics/typedaipackage.hpp +++ b/apps/openmw/mwmechanics/typedaipackage.hpp @@ -8,9 +8,9 @@ namespace MWMechanics template struct TypedAiPackage : public AiPackage { - virtual TypedAiPackage *clone() const override + virtual std::unique_ptr clone() const override { - return new T(*static_cast(this)); + return std::make_unique(*static_cast(this)); } }; } From b67e18329e4abb47c1299f5d0a8e5258e91e44fc Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 May 2020 00:29:21 +0200 Subject: [PATCH 150/227] Store AI packages as unique_ptr --- apps/openmw/mwmechanics/actors.cpp | 28 ++++---- apps/openmw/mwmechanics/aisequence.cpp | 89 +++++++++++--------------- apps/openmw/mwmechanics/aisequence.hpp | 11 ++-- apps/openmw/mwworld/worldimp.cpp | 2 +- 4 files changed, 58 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 873d5fa7c..4fdacfd05 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -112,11 +112,11 @@ void adjustCommandedActor (const MWWorld::Ptr& actor) bool hasCommandPackage = false; - std::list::const_iterator it; - for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + auto it = stats.getAiSequence().begin(); + for (; it != stats.getAiSequence().end(); ++it) { if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && - static_cast(*it)->isCommanded()) + static_cast(it->get())->isCommanded()) { hasCommandPackage = true; break; @@ -419,7 +419,7 @@ namespace MWMechanics if (world->isSwimming(actor) || (playerPos - actorPos).length2() >= 3000 * 3000) return; - // Our implementation is not FPS-dependent unlike Morrowind's so it needs to be recalibrated. + // Our implementation is not FPS-dependent unlike Morrowind's so it needs to be recalibrated. // We chose to use the chance MW would have when run at 60 FPS with the default value of the GMST. const float delta = MWBase::Environment::get().getFrameDuration() * 6.f; static const float fVoiceIdleOdds = world->getStore().get().find("fVoiceIdleOdds")->mValue.getFloat(); @@ -435,9 +435,9 @@ namespace MWMechanics CreatureStats &stats = actor.getClass().getCreatureStats(actor); MWMechanics::AiSequence& seq = stats.getAiSequence(); - if (!seq.isEmpty() && seq.getActivePackage()->useVariableSpeed()) + if (!seq.isEmpty() && seq.getActivePackage().useVariableSpeed()) { - osg::Vec3f targetPos = seq.getActivePackage()->getDestination(); + osg::Vec3f targetPos = seq.getActivePackage().getDestination(); osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3(); float distance = (targetPos - actorPos).length(); if (distance < DECELERATE_DISTANCE) @@ -604,7 +604,7 @@ namespace MWMechanics bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end(); // If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them - // Doesn't apply for player followers/escorters + // Doesn't apply for player followers/escorters if (!aggressive && !isPlayerFollowerOrEscorter) { // Check that actor2 is in combat with actor1 @@ -673,7 +673,7 @@ namespace MWMechanics return; bool followerOrEscorter = false; - for (const AiPackage* package : creatureStats2.getAiSequence()) + for (const auto& package : creatureStats2.getAiSequence()) { // The follow package must be first or have nothing but combat before it if (package->sideWithTarget()) @@ -1738,7 +1738,7 @@ namespace MWMechanics if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed()) { MWMechanics::AiSequence& seq = stats.getAiSequence(); - alwaysActive = !seq.isEmpty() && seq.getActivePackage()->alwaysActive(); + alwaysActive = !seq.isEmpty() && seq.getActivePackage().alwaysActive(); } bool inRange = isPlayer || dist <= mActorsProcessingRange || alwaysActive; int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower) @@ -2158,7 +2158,7 @@ namespace MWMechanics // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package // Actors that are targeted by this actor's Follow or Escort packages also side with them - for (const AiPackage* package : stats.getAiSequence()) + for (const auto& package : stats.getAiSequence()) { if (package->sideWithTarget() && !package->getTarget().isEmpty()) { @@ -2192,9 +2192,9 @@ namespace MWMechanics if (stats.isDead()) continue; - // An actor counts as following if AiFollow is the current AiPackage, + // An actor counts as following if AiFollow is the current AiPackage, // or there are only Combat and Wander packages before the AiFollow package - for (const AiPackage* package : stats.getAiSequence()) + for (const auto& package : stats.getAiSequence()) { if (package->followTargetThroughDoors() && package->getTarget() == actor) list.push_back(iteratedActor); @@ -2257,11 +2257,11 @@ namespace MWMechanics // An actor counts as following if AiFollow is the current AiPackage, // or there are only Combat and Wander packages before the AiFollow package - for (AiPackage* package : stats.getAiSequence()) + for (const auto& package : stats.getAiSequence()) { if (package->followTargetThroughDoors() && package->getTarget() == actor) { - list.push_back(static_cast(package)->getFollowIndex()); + list.push_back(static_cast(package.get())->getFollowIndex()); break; } else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 8ed1f0bee..9f5d4ccaf 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -25,9 +25,8 @@ namespace MWMechanics void AiSequence::copy (const AiSequence& sequence) { - for (std::list::const_iterator iter (sequence.mPackages.begin()); - iter!=sequence.mPackages.end(); ++iter) - mPackages.push_back ((*iter)->clone().release()); + for (const auto& package : sequence.mPackages) + mPackages.push_back(package->clone()); // We need to keep an AiWander storage, if present - it has a state machine. // Not sure about another temporary storages @@ -74,7 +73,7 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const { if (getTypeId() != AiPackage::TypeIdCombat) return false; - + targetActor = mPackages.front()->getTarget(); return !targetActor.isEmpty(); @@ -82,7 +81,7 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const bool AiSequence::getCombatTargets(std::vector &targetActors) const { - for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCombat) targetActors.push_back((*it)->getTarget()); @@ -91,24 +90,23 @@ bool AiSequence::getCombatTargets(std::vector &targetActors) const return !targetActors.empty(); } -std::list::const_iterator AiSequence::begin() const +std::list>::const_iterator AiSequence::begin() const { return mPackages.begin(); } -std::list::const_iterator AiSequence::end() const +std::list>::const_iterator AiSequence::end() const { return mPackages.end(); } -void AiSequence::erase(std::list::const_iterator package) +void AiSequence::erase(std::list>::const_iterator package) { // Not sure if manually terminated packages should trigger mDone, probably not? - for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for(auto it = mPackages.begin(); it != mPackages.end(); ++it) { if (package == it) { - delete *it; mPackages.erase(it); return; } @@ -118,7 +116,7 @@ void AiSequence::erase(std::list::const_iterator package) bool AiSequence::isInCombat() const { - for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) return true; @@ -128,7 +126,7 @@ bool AiSequence::isInCombat() const bool AiSequence::isEngagedWithActor() const { - for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) { @@ -142,7 +140,7 @@ bool AiSequence::isEngagedWithActor() const bool AiSequence::hasPackage(int typeId) const { - for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == typeId) return true; @@ -152,7 +150,7 @@ bool AiSequence::hasPackage(int typeId) const bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const { - for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) { @@ -165,11 +163,10 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const void AiSequence::stopCombat() { - for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ) + for(auto it = mPackages.begin(); it != mPackages.end(); ) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) { - delete *it; it = mPackages.erase(it); } else @@ -179,11 +176,10 @@ void AiSequence::stopCombat() void AiSequence::stopPursuit() { - for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ) + for(auto it = mPackages.begin(); it != mPackages.end(); ) { if ((*it)->getTypeId() == AiPackage::TypeIdPursue) { - delete *it; it = mPackages.erase(it); } else @@ -213,7 +209,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } auto packageIt = mPackages.begin(); - MWMechanics::AiPackage* package = *packageIt; + MWMechanics::AiPackage* package = packageIt->get(); if (!package->alwaysActive() && outOfRange) return; @@ -231,7 +227,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac float bestRating = 0.f; - for(std::list::iterator it = mPackages.begin(); it != mPackages.end();) + for (auto it = mPackages.begin(); it != mPackages.end();) { if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; @@ -240,7 +236,6 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac // target disappeared (e.g. summoned creatures) if (target.isEmpty()) { - delete *it; it = mPackages.erase(it); } else @@ -276,24 +271,23 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } packageIt = mPackages.begin(); - package = *packageIt; + package = packageIt->get(); packageTypeId = package->getTypeId(); } try { - if (package->execute (actor, characterController, mAiState, duration)) + if (package->execute(actor, characterController, mAiState, duration)) { // Put repeating noncombat AI packages on the end of the stack so they can be used again if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat())) { package->reset(); - mPackages.push_back(package->clone().release()); + mPackages.push_back(package->clone()); } // To account for the rare case where AiPackage::execute() queued another AI package // (e.g. AiPursue executing a dialogue script that uses startCombat) mPackages.erase(packageIt); - delete package; if (isActualAiPackage(packageTypeId)) mDone = true; } @@ -311,9 +305,6 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac void AiSequence::clear() { - for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) - delete *iter; - mPackages.clear(); } @@ -340,8 +331,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo osg::Vec3f dest; if (currentTypeId == MWMechanics::AiPackage::TypeIdWander) { - AiPackage* activePackage = getActivePackage(); - dest = activePackage->getDestination(actor); + dest = getActivePackage().getDestination(actor); } else { @@ -355,11 +345,10 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo // remove previous packages if required if (cancelOther && package.shouldCancelPreviousAi()) { - for(std::list::iterator it = mPackages.begin(); it != mPackages.end();) + for (auto it = mPackages.begin(); it != mPackages.end();) { if((*it)->canCancel()) { - delete *it; it = mPackages.erase(it); } else @@ -369,7 +358,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo } // insert new package in correct place depending on priority - for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { // We should keep current AiCast package, if we try to add a new one. if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast && @@ -380,12 +369,12 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo if((*it)->getPriority() <= package.getPriority()) { - mPackages.insert(it,package.clone().release()); + mPackages.insert(it, package.clone()); return; } } - mPackages.push_back (package.clone().release()); + mPackages.push_back(package.clone()); // Make sure that temporary storage is empty if (cancelOther) @@ -401,12 +390,11 @@ bool MWMechanics::AiSequence::isEmpty() const return mPackages.empty(); } -AiPackage* MWMechanics::AiSequence::getActivePackage() +const AiPackage& MWMechanics::AiSequence::getActivePackage() { if(mPackages.empty()) throw std::runtime_error(std::string("No AI Package!")); - else - return mPackages.front(); + return *mPackages.front(); } void AiSequence::fill(const ESM::AIPackageList &list) @@ -417,7 +405,7 @@ void AiSequence::fill(const ESM::AIPackageList &list) for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) { - MWMechanics::AiPackage* package; + std::unique_ptr package; if (it->mType == ESM::AI_Wander) { ESM::AIWander data = it->mWander; @@ -425,38 +413,36 @@ void AiSequence::fill(const ESM::AIPackageList &list) idles.reserve(8); for (int i=0; i<8; ++i) idles.push_back(data.mIdle[i]); - package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat != 0); + package = std::make_unique(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat != 0); } else if (it->mType == ESM::AI_Escort) { ESM::AITarget data = it->mTarget; - package = new MWMechanics::AiEscort(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); + package = std::make_unique(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } else if (it->mType == ESM::AI_Travel) { ESM::AITravel data = it->mTravel; - package = new MWMechanics::AiTravel(data.mX, data.mY, data.mZ); + package = std::make_unique(data.mX, data.mY, data.mZ); } else if (it->mType == ESM::AI_Activate) { ESM::AIActivate data = it->mActivate; - package = new MWMechanics::AiActivate(data.mName.toString()); + package = std::make_unique(data.mName.toString()); } else //if (it->mType == ESM::AI_Follow) { ESM::AITarget data = it->mTarget; - package = new MWMechanics::AiFollow(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); + package = std::make_unique(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } - mPackages.push_back(package); + mPackages.push_back(std::move(package)); } } void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const { - for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) - { - (*iter)->writeState(sequence); - } + for (const auto& package : mPackages) + package->writeState(sequence); sequence.mLastAiPackage = mLastAiPackage; } @@ -527,7 +513,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) if (!package.get()) continue; - mPackages.push_back(package.release()); + mPackages.push_back(std::move(package)); } mLastAiPackage = sequence.mLastAiPackage; @@ -537,8 +523,7 @@ void AiSequence::fastForward(const MWWorld::Ptr& actor) { if (!mPackages.empty()) { - MWMechanics::AiPackage* package = mPackages.front(); - package->fastForward(actor, mAiState); + mPackages.front()->fastForward(actor, mAiState); } } diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 7f07d5aae..12b837d87 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -2,6 +2,7 @@ #define GAME_MWMECHANICS_AISEQUENCE_H #include +#include #include "aistate.hpp" @@ -36,7 +37,7 @@ namespace MWMechanics class AiSequence { ///AiPackages to run though - std::list mPackages; + std::list> mPackages; ///Finished with top AIPackage, set for one frame bool mDone; @@ -64,10 +65,10 @@ namespace MWMechanics virtual ~AiSequence(); /// Iterator may be invalidated by any function calls other than begin() or end(). - std::list::const_iterator begin() const; - std::list::const_iterator end() const; + std::list>::const_iterator begin() const; + std::list>::const_iterator end() const; - void erase (std::list::const_iterator package); + void erase(std::list>::const_iterator package); /// Returns currently executing AiPackage type /** \see enum AiPackage::TypeId **/ @@ -125,7 +126,7 @@ namespace MWMechanics /// Return the current active package. /** If there is no active package, it will throw an exception **/ - AiPackage* getActivePackage(); + const AiPackage& getActivePackage(); /// Fills the AiSequence with packages /** Typically used for loading from the ESM diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b7a1d22bf..4a513e98f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3063,7 +3063,7 @@ namespace MWWorld { if (actor != MWMechanics::getPlayer()) { - for (const MWMechanics::AiPackage* package : stats.getAiSequence()) + for (const auto& package : stats.getAiSequence()) { if (package->getTypeId() == MWMechanics::AiPackage::TypeIdCast) { From 4d7947d27c68201f5d8d544556c869829eebfbfe Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 2 Jun 2020 21:59:37 +0200 Subject: [PATCH 151/227] Mutate base records when editing AI settings (#2798) --- apps/openmw/mwbase/world.hpp | 9 ++++++ apps/openmw/mwclass/creature.cpp | 5 ++++ apps/openmw/mwclass/creature.hpp | 2 ++ apps/openmw/mwclass/npc.cpp | 5 ++++ apps/openmw/mwclass/npc.hpp | 2 ++ apps/openmw/mwmechanics/actorutil.hpp | 37 +++++++++++++++++++++++++ apps/openmw/mwscript/aiextensions.cpp | 7 +++-- apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/class.cpp | 5 ++++ apps/openmw/mwworld/class.hpp | 4 ++- apps/openmw/mwworld/esmstore.cpp | 35 +++++++++++------------ apps/openmw/mwworld/esmstore.hpp | 3 ++ apps/openmw/mwworld/worldimp.cpp | 11 ++++++++ apps/openmw/mwworld/worldimp.hpp | 8 ++++++ 14 files changed, 112 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 84f9b4984..05802c658 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -35,6 +35,7 @@ namespace ESM struct Position; struct Cell; struct Class; + struct Creature; struct Potion; struct Spell; struct NPC; @@ -377,6 +378,14 @@ namespace MWBase ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. /// \return pointer to created record + virtual const ESM::Creature *createOverrideRecord (const ESM::Creature& record) = 0; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + + virtual const ESM::NPC *createOverrideRecord (const ESM::NPC& record) = 0; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + virtual void update (float duration, bool paused) = 0; virtual void updatePhysics (float duration, bool paused) = 0; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index d1a439528..375b70dde 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -878,4 +878,9 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); scale *= ref->mBase->mScale; } + + void Creature::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const + { + MWMechanics::setBaseAISetting(id, setting, value); + } } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 35688ed1a..9be5c4272 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -129,6 +129,8 @@ namespace MWClass virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const; /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh + + virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 297471e07..df483962a 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1437,4 +1437,9 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->getFactionRank(); } + + void Npc::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const + { + MWMechanics::setBaseAISetting(id, setting, value); + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 9b92f6338..3d63697fb 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -164,6 +164,8 @@ namespace MWClass virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const; virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const; + + virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; }; } diff --git a/apps/openmw/mwmechanics/actorutil.hpp b/apps/openmw/mwmechanics/actorutil.hpp index 82a904799..cdb787311 100644 --- a/apps/openmw/mwmechanics/actorutil.hpp +++ b/apps/openmw/mwmechanics/actorutil.hpp @@ -1,6 +1,16 @@ #ifndef OPENMW_MWMECHANICS_ACTORUTIL_H #define OPENMW_MWMECHANICS_ACTORUTIL_H +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/esmstore.hpp" + +#include "./creaturestats.hpp" + namespace MWWorld { class Ptr; @@ -11,6 +21,33 @@ namespace MWMechanics MWWorld::Ptr getPlayer(); bool isPlayerInCombat(); bool canActorMoveByZAxis(const MWWorld::Ptr& actor); + + template + void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) + { + T copy = *MWBase::Environment::get().getWorld()->getStore().get().find(id); + switch(setting) + { + case MWMechanics::CreatureStats::AiSetting::AI_Hello: + copy.mAiData.mHello = value; + break; + case MWMechanics::CreatureStats::AiSetting::AI_Fight: + copy.mAiData.mFight = value; + break; + case MWMechanics::CreatureStats::AiSetting::AI_Flee: + copy.mAiData.mFlee = value; + break; + case MWMechanics::CreatureStats::AiSetting::AI_Alarm: + copy.mAiData.mAlarm = value; + break; + default: + assert(0); + } + MWBase::Environment::get().getWorld()->createOverrideRecord(copy); + } + + template void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value); + template void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value); } #endif diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 79639197d..fdd13cfb6 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -257,8 +257,10 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - ptr.getClass().getCreatureStats (ptr).setAiSetting (mIndex, - ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getBase() + value); + int modified = ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getBase() + value; + + ptr.getClass().getCreatureStats (ptr).setAiSetting (mIndex, modified); + ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, modified); } }; template @@ -277,6 +279,7 @@ namespace MWScript MWMechanics::Stat stat = ptr.getClass().getCreatureStats(ptr).getAiSetting(mIndex); stat.setModified(value, 0); ptr.getClass().getCreatureStats(ptr).setAiSetting(mIndex, stat); + ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, value); } }; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 86a26212f..560c24578 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -460,6 +460,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str case ESM::REC_ENAB: case ESM::REC_LEVC: case ESM::REC_LEVI: + case ESM::REC_CREA: MWBase::Environment::get().getWorld()->readRecord(reader, n.intval, contentFileMap); break; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 0287db56f..07981bf2a 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -516,4 +516,9 @@ namespace MWWorld result.z() = magicEffect->mData.mBlue / 255.f; return result; } + + void Class::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const + { + throw std::runtime_error ("class does not have creature stats"); + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 8c3740edd..fb816d810 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -10,6 +10,7 @@ #include "ptr.hpp" #include "doorstate.hpp" +#include "../mwmechanics/creaturestats.hpp" namespace ESM { @@ -28,7 +29,6 @@ namespace MWPhysics namespace MWMechanics { - class CreatureStats; class NpcStats; struct Movement; } @@ -360,6 +360,8 @@ namespace MWWorld virtual float getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const; virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; + + virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; }; } diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 1f6ed5102..f86226640 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -273,7 +273,8 @@ void ESMStore::validate() +mSpells.getDynamicSize() +mWeapons.getDynamicSize() +mCreatureLists.getDynamicSize() - +mItemLists.getDynamicSize(); + +mItemLists.getDynamicSize() + +mCreatures.getDynamicSize(); } void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const @@ -295,6 +296,7 @@ void ESMStore::validate() mNpcs.write (writer, progress); mItemLists.write (writer, progress); mCreatureLists.write (writer, progress); + mCreatures.write (writer, progress); } bool ESMStore::readRecord (ESM::ESMReader& reader, uint32_t type) @@ -312,24 +314,8 @@ void ESMStore::validate() case ESM::REC_NPC_: case ESM::REC_LEVI: case ESM::REC_LEVC: - - { - mStores[type]->read (reader); - } - - if (type==ESM::REC_NPC_) - { - // NPC record will always be last and we know that there can be only one - // dynamic NPC record (player) -> We are done here with dynamic record loading - setUp(); - - const ESM::NPC *player = mNpcs.find ("player"); - - if (!mRaces.find (player->mRace) || - !mClasses.find (player->mClass)) - throw std::runtime_error ("Invalid player record (race or class unavailable"); - } - + case ESM::REC_CREA: + mStores[type]->read (reader); return true; case ESM::REC_DYNA: @@ -343,4 +329,15 @@ void ESMStore::validate() } } + void ESMStore::checkPlayer() + { + setUp(); + + const ESM::NPC *player = mNpcs.find ("player"); + + if (!mRaces.find (player->mRace) || + !mClasses.find (player->mClass)) + throw std::runtime_error ("Invalid player record (race or class unavailable"); + } + } // end namespace diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index d170a32c5..fe1cfc708 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -239,6 +239,9 @@ namespace MWWorld bool readRecord (ESM::ESMReader& reader, uint32_t type); ///< \return Known type? + + // To be called when we are done with dynamic record loading + void checkPlayer(); }; template <> diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ba4a6467a..6d20f5d45 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -403,6 +403,7 @@ namespace MWWorld reader.getHNT(mLevitationEnabled, "LEVT"); return; case ESM::REC_PLAY: + mStore.checkPlayer(); mPlayer->readRecord(reader, type); if (getPlayerPtr().isInCell()) { @@ -1815,6 +1816,16 @@ namespace MWWorld return mStore.overrideRecord(record); } + const ESM::Creature *World::createOverrideRecord(const ESM::Creature &record) + { + return mStore.overrideRecord(record); + } + + const ESM::NPC *World::createOverrideRecord(const ESM::NPC &record) + { + return mStore.overrideRecord(record); + } + const ESM::NPC *World::createRecord(const ESM::NPC &record) { bool update = false; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7b6d2afdc..201803a48 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -490,6 +490,14 @@ namespace MWWorld ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. /// \return pointer to created record + const ESM::Creature *createOverrideRecord (const ESM::Creature& record) override; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + + const ESM::NPC *createOverrideRecord (const ESM::NPC& record) override; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + void update (float duration, bool paused) override; void updatePhysics (float duration, bool paused) override; From c0e62e9529f0e9fcf6c0b7ab0a5aab04fcdc81b2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 29 May 2020 13:37:13 +0400 Subject: [PATCH 152/227] Refactor list of variables in the WorldManager --- apps/openmw/mwworld/worldimp.cpp | 7 +++-- apps/openmw/mwworld/worldimp.hpp | 48 ++++++++++++++++---------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4a513e98f..d660d90af 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -164,10 +164,11 @@ namespace MWWorld const std::string& startCell, const std::string& startupScript, const std::string& resourcePath, const std::string& userDataPath) : mResourceSystem(resourceSystem), mLocalScripts (mStore), - mSky (true), mCells (mStore, mEsm), - mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mUserDataPath(userDataPath), + mCells (mStore, mEsm), mSky (true), + mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), + mUserDataPath(userDataPath), mShouldUpdateNavigator(false), mActivationDistanceOverride (activationDistanceOverride), - mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true), + mStartCell(startCell), mDistanceToFacedObject(-1.f), mTeleportEnabled(true), mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f) { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 942788499..f6341b509 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -77,13 +77,13 @@ namespace MWWorld class World final: public MWBase::World { + private: Resource::ResourceSystem* mResourceSystem; std::vector mEsm; MWWorld::ESMStore mStore; LocalScripts mLocalScripts; MWWorld::Globals mGlobalVariables; - bool mSky; ESM::Variant* mGameHour; ESM::Variant* mDaysPassed; @@ -104,6 +104,7 @@ namespace MWWorld std::unique_ptr mWeatherManager; std::shared_ptr mProjectileManager; + bool mSky; bool mGodMode; bool mScriptsEnabled; std::vector mContentFiles; @@ -111,18 +112,31 @@ namespace MWWorld std::string mUserDataPath; osg::Vec3f mDefaultHalfExtents; - bool mShouldUpdateNavigator = false; - - // not implemented - World (const World&); - World& operator= (const World&); + bool mShouldUpdateNavigator; int mActivationDistanceOverride; + std::string mStartCell; + + float mSwimHeightScale; + + float mDistanceToFacedObject; + + bool mTeleportEnabled; + bool mLevitationEnabled; + bool mGoToJail; + int mDaysInPrison; + bool mPlayerTraveling; + bool mPlayerInJail; + + float mSpellPreloadTimer; + std::map mDoorStates; ///< only holds doors that are currently moving. 1 = opening, 2 = closing - std::string mStartCell; + // not implemented + World (const World&); + World& operator= (const World&); void updateWeather(float duration, bool paused = false); int getDaysPerMonth (int month) const; @@ -141,10 +155,6 @@ namespace MWWorld MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true); - public: // FIXME - void addContainerScripts(const Ptr& reference, CellStore* cell) override; - void removeContainerScripts(const Ptr& reference) override; - private: void PCDropped (const Ptr& item); bool rotateDoor(const Ptr door, DoorState state, float duration); @@ -172,19 +182,6 @@ namespace MWWorld void loadContentFiles(const Files::Collections& fileCollections, const std::vector& content, ContentLoader& contentLoader); - float mSwimHeightScale; - - float mDistanceToFacedObject; - - bool mTeleportEnabled; - bool mLevitationEnabled; - bool mGoToJail; - int mDaysInPrison; - bool mPlayerTraveling; - bool mPlayerInJail; - - float mSpellPreloadTimer; - float feetToGameUnits(float feet); float getActivationDistancePlusTelekinesis(); @@ -192,6 +189,9 @@ namespace MWWorld MWWorld::ConstPtr getClosestMarkerFromExteriorPosition( const osg::Vec3f& worldPos, const std::string &id ); public: + // FIXME + void addContainerScripts(const Ptr& reference, CellStore* cell) override; + void removeContainerScripts(const Ptr& reference) override; World ( osgViewer::Viewer* viewer, From 3dce225f28cae4a7e6ac1d5c992eb64c873677e3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 30 Sep 2019 20:27:42 +0400 Subject: [PATCH 153/227] Implement vanilla-style corprus handling (bug #3714, bug #4623) --- CHANGELOG.md | 2 + apps/openmw/mwbase/mechanicsmanager.hpp | 2 + apps/openmw/mwgui/jailscreen.cpp | 6 + apps/openmw/mwmechanics/activespells.cpp | 41 ++++- apps/openmw/mwmechanics/activespells.hpp | 2 + apps/openmw/mwmechanics/actors.cpp | 113 ++++++++++++- apps/openmw/mwmechanics/creaturestats.cpp | 38 ++++- apps/openmw/mwmechanics/creaturestats.hpp | 20 ++- .../mwmechanics/mechanicsmanagerimp.cpp | 18 +++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 + apps/openmw/mwmechanics/spells.cpp | 152 ++++++------------ apps/openmw/mwmechanics/spells.hpp | 22 +-- apps/openmw/mwmechanics/stat.cpp | 9 +- apps/openmw/mwscript/statsextensions.cpp | 1 + apps/openmw/mwworld/inventorystore.cpp | 12 +- apps/openmw/mwworld/inventorystore.hpp | 4 +- components/esm/attr.hpp | 2 +- components/esm/creaturestats.cpp | 21 +++ components/esm/creaturestats.hpp | 11 +- components/esm/savedgame.cpp | 2 +- components/esm/spellstate.cpp | 34 ++-- components/esm/spellstate.hpp | 5 +- 22 files changed, 360 insertions(+), 159 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87796d015..58fe39c1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Bug #1952: Incorrect particle lighting Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #3676: NiParticleColorModifier isn't applied properly + Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects + Bug #4623: Corprus implementation is incorrect Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #5108: Savegame bloating due to inefficient fog textures format Bug #5165: Active spells should use real time intead of timestamps diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 3bde83e94..c9ce43e01 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -280,6 +280,8 @@ namespace MWBase virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const = 0; virtual MWMechanics::GreetingState getGreetingState(const MWWorld::Ptr& ptr) const = 0; virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const = 0; + + virtual void restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) = 0; }; } diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 9c8fbfb17..4a89304b3 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -81,6 +81,12 @@ namespace MWGui MWBase::Environment::get().getMechanicsManager()->rest(mDays * 24, true); MWBase::Environment::get().getWorld()->advanceTime(mDays * 24); + // We should not worsen corprus when in prison + for (auto& spell : player.getClass().getCreatureStats(player).getCorprusSpells()) + { + spell.second.mNextWorsening += mDays * 24; + } + std::set skills; for (int day=0; day& effects = iter->second.mEffects; for (std::vector::iterator effectIt = effects.begin(); effectIt != effects.end();) { if (effectIt->mTimeLeft <= 0) { - effectIt = effects.erase(effectIt); rebuild = true; + + // Note: it we expire a Corprus effect, we should remove the whole spell. + if (effectIt->mEffectId == ESM::MagicEffect::Corprus) + { + iter = mSpells.erase (iter); + interrupt = true; + break; + } + + effectIt = effects.erase(effectIt); } else { @@ -43,7 +53,9 @@ namespace MWMechanics ++effectIt; } } - ++iter; + + if (!interrupt) + ++iter; } } } @@ -278,6 +290,31 @@ namespace MWMechanics mSpellsChanged = true; } + void ActiveSpells::purgeCorprusDisease() + { + for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();) + { + bool hasCorprusEffect = false; + for (std::vector::iterator effectIt = iter->second.mEffects.begin(); + effectIt != iter->second.mEffects.end();++effectIt) + { + if (effectIt->mEffectId == ESM::MagicEffect::Corprus) + { + hasCorprusEffect = true; + break; + } + } + + if (hasCorprusEffect) + { + mSpells.erase(iter++); + mSpellsChanged = true; + } + else + ++iter; + } + } + void ActiveSpells::clear() { mSpells.clear(); diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index ddfa56ecf..9a1783bc9 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -99,6 +99,8 @@ namespace MWMechanics bool isSpellActive (const std::string& id) const; ///< case insensitive + void purgeCorprusDisease(); + const MagicEffects& getMagicEffects() const; void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4fdacfd05..584a09492 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -176,6 +176,49 @@ namespace MWMechanics } }; + class GetCurrentMagnitudes : public MWMechanics::EffectSourceVisitor + { + std::string mSpellId; + + public: + GetCurrentMagnitudes(const std::string& spellId) + : mSpellId(spellId) + { + } + + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, const std::string& sourceId, int casterActorId, + float magnitude, float remainingTime = -1, float totalTime = -1) + { + if (magnitude <= 0) + return; + + if (sourceId != mSpellId) + return; + + mMagnitudes.push_back(std::make_pair(key, magnitude)); + } + + std::vector> mMagnitudes; + }; + + class GetCorprusSpells : public MWMechanics::EffectSourceVisitor + { + + public: + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, const std::string& sourceId, int casterActorId, + float magnitude, float remainingTime = -1, float totalTime = -1) + { + if (key.mId != ESM::MagicEffect::Corprus) + return; + + mSpells.push_back(sourceId); + } + + std::vector mSpells; + }; + class SoulTrap : public MWMechanics::EffectSourceVisitor { MWWorld::Ptr mCreature; @@ -942,21 +985,75 @@ namespace MWMechanics if (creatureStats.needToRecalcDynamicStats()) calculateDynamicStats(ptr); + if (ptr == getPlayer()) { - Spells & spells = creatureStats.getSpells(); - for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) + GetCorprusSpells getCorprusSpellsVisitor; + creatureStats.getSpells().visitEffectSources(getCorprusSpellsVisitor); + creatureStats.getActiveSpells().visitEffectSources(getCorprusSpellsVisitor); + ptr.getClass().getInventoryStore(ptr).visitEffectSources(getCorprusSpellsVisitor); + std::vector corprusSpells = getCorprusSpellsVisitor.mSpells; + std::vector corprusSpellsToRemove; + + for (auto it = creatureStats.getCorprusSpells().begin(); it != creatureStats.getCorprusSpells().end(); ++it) { - if (spells.getCorprusSpells().find(it->first) != spells.getCorprusSpells().end()) + if(std::find(corprusSpells.begin(), corprusSpells.end(), it->first) == corprusSpells.end()) { - if (MWBase::Environment::get().getWorld()->getTimeStamp() >= spells.getCorprusSpells().at(it->first).mNextWorsening) - { - spells.worsenCorprus(it->first); + // Corprus effect expired, remove entry and restore stats. + MWBase::Environment::get().getMechanicsManager()->restoreStatsAfterCorprus(ptr, it->first); + corprusSpellsToRemove.push_back(it->first); + corprusSpells.erase(std::remove(corprusSpells.begin(), corprusSpells.end(), it->first), corprusSpells.end()); + continue; + } + + corprusSpells.erase(std::remove(corprusSpells.begin(), corprusSpells.end(), it->first), corprusSpells.end()); - if (ptr == getPlayer()) - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}"); + if (MWBase::Environment::get().getWorld()->getTimeStamp() >= it->second.mNextWorsening) + { + it->second.mNextWorsening += CorprusStats::sWorseningPeriod; + GetCurrentMagnitudes getMagnitudesVisitor (it->first); + creatureStats.getSpells().visitEffectSources(getMagnitudesVisitor); + creatureStats.getActiveSpells().visitEffectSources(getMagnitudesVisitor); + ptr.getClass().getInventoryStore(ptr).visitEffectSources(getMagnitudesVisitor); + for (auto& effectMagnitude : getMagnitudesVisitor.mMagnitudes) + { + if (effectMagnitude.first.mId == ESM::MagicEffect::FortifyAttribute) + { + AttributeValue attr = creatureStats.getAttribute(effectMagnitude.first.mArg); + attr.damage(-effectMagnitude.second); + creatureStats.setAttribute(effectMagnitude.first.mArg, attr); + it->second.mWorsenings[effectMagnitude.first.mArg] = 0; + } + else if (effectMagnitude.first.mId == ESM::MagicEffect::DrainAttribute) + { + AttributeValue attr = creatureStats.getAttribute(effectMagnitude.first.mArg); + int currentDamage = attr.getDamage(); + if (currentDamage >= 0) + it->second.mWorsenings[effectMagnitude.first.mArg] = std::min(it->second.mWorsenings[effectMagnitude.first.mArg], currentDamage); + + it->second.mWorsenings[effectMagnitude.first.mArg] += effectMagnitude.second; + attr.damage(effectMagnitude.second); + creatureStats.setAttribute(effectMagnitude.first.mArg, attr); + } } + + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}"); } } + + for (std::string& oldCorprusSpell : corprusSpellsToRemove) + { + creatureStats.removeCorprusSpell(oldCorprusSpell); + } + + for (std::string& newCorprusSpell : corprusSpells) + { + CorprusStats corprus; + for (int i=0; igetTimeStamp() + CorprusStats::sWorseningPeriod; + + creatureStats.addCorprusSpell(newCorprusSpell, corprus); + } } // AI setting modifiers diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 800b5c22f..a64fb087c 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -551,6 +551,14 @@ namespace MWMechanics state.mHasAiSettings = true; for (int i=0; i<4; ++i) mAiSettings[i].writeState (state.mAiSettings[i]); + + for (auto it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) + { + for (int i=0; ifirst].mWorsenings[i] = mCorprusSpells.at(it->first).mWorsenings[i]; + + state.mCorprusSpells[it->first].mNextWorsening = mCorprusSpells.at(it->first).mNextWorsening.toEsm(); + } } void CreatureStats::readState (const ESM::CreatureStats& state) @@ -589,7 +597,7 @@ namespace MWMechanics mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath); //mHitAttemptActorId = state.mHitAttemptActorId; - mSpells.readState(state.mSpells); + mSpells.readState(state.mSpells, this); mActiveSpells.readState(state.mActiveSpells); mAiSequence.readState(state.mAiSequence); mMagicEffects.readState(state.mMagicEffects); @@ -600,6 +608,15 @@ namespace MWMechanics if (state.mHasAiSettings) for (int i=0; i<4; ++i) mAiSettings[i].readState(state.mAiSettings[i]); + + mCorprusSpells.clear(); + for (auto it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it) + { + for (int i=0; ifirst].mWorsenings[i] = state.mCorprusSpells.at(it->first).mWorsenings[i]; + + mCorprusSpells[it->first].mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening); + } } void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime) @@ -675,4 +692,23 @@ namespace MWMechanics { return mSummonGraveyard; } + + std::map &CreatureStats::getCorprusSpells() + { + return mCorprusSpells; + } + + void CreatureStats::addCorprusSpell(const std::string& sourceId, CorprusStats& stats) + { + mCorprusSpells[sourceId] = stats; + } + + void CreatureStats::removeCorprusSpell(const std::string& sourceId) + { + auto corprusIt = mCorprusSpells.find(sourceId); + if (corprusIt != mCorprusSpells.end()) + { + mCorprusSpells.erase(corprusIt); + } + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index a4ecb23b3..5a5dd3f12 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -12,6 +12,8 @@ #include "aisequence.hpp" #include "drawstate.hpp" +#include + namespace ESM { struct CreatureStats; @@ -19,6 +21,14 @@ namespace ESM namespace MWMechanics { + struct CorprusStats + { + static const int sWorseningPeriod = 24; + + int mWorsenings[ESM::Attribute::Length]; + MWWorld::TimeStamp mNextWorsening; + }; + /// \brief Common creature stats /// /// @@ -26,7 +36,7 @@ namespace MWMechanics { static int sActorId; DrawState_ mDrawState; - AttributeValue mAttributes[8]; + AttributeValue mAttributes[ESM::Attribute::Length]; DynamicStat mDynamic[3]; // health, magicka, fatigue Spells mSpells; ActiveSpells mActiveSpells; @@ -79,6 +89,8 @@ namespace MWMechanics // This may be necessary when the creature is in an inactive cell. std::vector mSummonGraveyard; + std::map mCorprusSpells; + protected: int mLevel; @@ -280,6 +292,12 @@ namespace MWMechanics /// assigned this function will return false). static void cleanup(); + + std::map & getCorprusSpells(); + + void addCorprusSpell(const std::string& sourceId, CorprusStats& stats); + + void removeCorprusSpell(const std::string& sourceId); }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 88045ec44..5376ec86e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -289,6 +289,24 @@ namespace MWMechanics mWatched = ptr; } + void MechanicsManager::restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) + { + auto& stats = actor.getClass().getCreatureStats (actor); + auto& corprusSpells = stats.getCorprusSpells(); + + auto corprusIt = corprusSpells.find(sourceId); + + if (corprusIt != corprusSpells.end()) + { + for (int i = 0; i < ESM::Attribute::Length; ++i) + { + MWMechanics::AttributeValue attr = stats.getAttribute(i); + attr.restore(corprusIt->second.mWorsenings[i]); + actor.getClass().getCreatureStats(actor).setAttribute(i, attr); + } + } + } + void MechanicsManager::update(float duration, bool paused) { if(!mWatched.isEmpty()) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 6785f979f..86bc4c720 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -247,6 +247,8 @@ namespace MWMechanics virtual GreetingState getGreetingState(const MWWorld::Ptr& ptr) const override; virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const override; + virtual void restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) override; + private: bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set &playerFollowers); diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 0eaa531e2..ae7454f19 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -7,9 +7,13 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" +#include "actorutil.hpp" +#include "creaturestats.hpp" #include "magiceffects.hpp" +#include "stat.hpp" namespace MWMechanics { @@ -63,12 +67,6 @@ namespace MWMechanics } } } - - for (std::map::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) - { - mEffects += it->second; - mSourcedEffects[it->first] += it->second; - } } bool Spells::hasSpell(const std::string &spell) const @@ -101,15 +99,6 @@ namespace MWMechanics } } - if (hasCorprusEffect(spell)) - { - CorprusStats corprus; - corprus.mWorsenings = 0; - corprus.mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod; - - mCorprusSpells[spell] = corprus; - } - SpellParams params; params.mEffectRands = random; mSpells.insert (std::make_pair (spell, params)); @@ -127,27 +116,6 @@ namespace MWMechanics const ESM::Spell* spell = getSpell(spellId); TContainer::iterator iter = mSpells.find (spell); - std::map::iterator corprusIt = mCorprusSpells.find(spell); - - // if it's corprus, remove negative and keep positive effects - if (corprusIt != mCorprusSpells.end()) - { - worsenCorprus(spell); - if (mPermanentSpellEffects.find(spell) != mPermanentSpellEffects.end()) - { - MagicEffects & effects = mPermanentSpellEffects[spell]; - for (MagicEffects::Collection::const_iterator effectIt = effects.begin(); effectIt != effects.end();) - { - const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->first.mId); - if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) - effects.remove((effectIt++)->first); - else - ++effectIt; - } - } - mCorprusSpells.erase(corprusIt); - } - if (iter!=mSpells.end()) { mSpells.erase (iter); @@ -320,31 +288,6 @@ namespace MWMechanics } } - void Spells::worsenCorprus(const ESM::Spell* spell) - { - mCorprusSpells[spell].mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod; - mCorprusSpells[spell].mWorsenings++; - - // update worsened effects - mPermanentSpellEffects[spell] = MagicEffects(); - int i=0; - for (std::vector::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt, ++i) - { - const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectID); - if ((effectIt->mEffectID != ESM::MagicEffect::Corprus) && (magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce)) - { - float random = 1.f; - if (mSpells[spell].mEffectRands.find(i) != mSpells[spell].mEffectRands.end()) - random = mSpells[spell].mEffectRands.at(i); - - float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; - magnitude *= std::max(1, mCorprusSpells[spell].mWorsenings); - mPermanentSpellEffects[spell].add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(magnitude)); - mSpellsChanged = true; - } - } - } - bool Spells::hasCorprusEffect(const ESM::Spell *spell) { for (std::vector::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt) @@ -357,11 +300,6 @@ namespace MWMechanics return false; } - const std::map &Spells::getCorprusSpells() const - { - return mCorprusSpells; - } - void Spells::purgeEffect(int effectId) { for (TContainer::iterator spellIt = mSpells.begin(); spellIt != mSpells.end(); ++spellIt) @@ -412,7 +350,7 @@ namespace MWMechanics mUsedPowers[spell] = MWBase::Environment::get().getWorld()->getTimeStamp(); } - void Spells::readState(const ESM::SpellState &state) + void Spells::readState(const ESM::SpellState &state, CreatureStats* creatureStats) { for (ESM::SpellState::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it) { @@ -436,31 +374,64 @@ namespace MWMechanics mUsedPowers[spell] = MWWorld::TimeStamp(it->second); } - for (std::map >::const_iterator it = - state.mPermanentSpellEffects.begin(); it != state.mPermanentSpellEffects.end(); ++it) + for (std::map::const_iterator it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it) { const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); if (!spell) continue; - mPermanentSpellEffects[spell] = MagicEffects(); - for (std::vector::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) + CorprusStats stats; + + int worsening = state.mCorprusSpells.at(it->first).mWorsenings; + + for (int i=0; imEffects.mList) { - mPermanentSpellEffects[spell].add(EffectKey(effectIt->mId, effectIt->mArg), effectIt->mMagnitude); + if (effect.mEffectID == ESM::MagicEffect::DrainAttribute) + stats.mWorsenings[effect.mAttribute] = worsening; } + stats.mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening); + + creatureStats->addCorprusSpell(it->first, stats); } - mCorprusSpells.clear(); - for (std::map::const_iterator it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it) + mSpellsChanged = true; + + // Permanent effects are used only to keep the custom magnitude of corprus spells effects (after cure too), and only in old saves. Convert data to the new approach. + for (std::map >::const_iterator it = + state.mPermanentSpellEffects.begin(); it != state.mPermanentSpellEffects.end(); ++it) { - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); - if (!spell) // Discard unavailable corprus spells + const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); + if (!spell) continue; - mCorprusSpells[spell].mWorsenings = state.mCorprusSpells.at(it->first).mWorsenings; - mCorprusSpells[spell].mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening); - } - mSpellsChanged = true; + // Import data only for player, other actors should not suffer from corprus worsening. + MWWorld::Ptr player = getPlayer(); + if (creatureStats->getActorId() != player.getClass().getCreatureStats(player).getActorId()) + return; + + // Note: if target actor has the Restore attirbute effects, stats will be restored. + for (std::vector::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) + { + // Applied corprus effects are already in loaded stats modifiers + if (effectIt->mId == ESM::MagicEffect::FortifyAttribute) + { + AttributeValue attr = creatureStats->getAttribute(effectIt->mArg); + attr.setModifier(attr.getModifier() - effectIt->mMagnitude); + attr.damage(-effectIt->mMagnitude); + creatureStats->setAttribute(effectIt->mArg, attr); + } + else if (effectIt->mId == ESM::MagicEffect::DrainAttribute) + { + AttributeValue attr = creatureStats->getAttribute(effectIt->mArg); + attr.setModifier(attr.getModifier() + effectIt->mMagnitude); + attr.damage(effectIt->mMagnitude); + creatureStats->setAttribute(effectIt->mArg, attr); + } + } + } } void Spells::writeState(ESM::SpellState &state) const @@ -477,26 +448,5 @@ namespace MWMechanics for (std::map::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it) state.mUsedPowers[it->first->mId] = it->second.toEsm(); - - for (std::map::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) - { - std::vector effectList; - for (MagicEffects::Collection::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) - { - ESM::SpellState::PermanentSpellEffectInfo info; - info.mId = effectIt->first.mId; - info.mArg = effectIt->first.mArg; - info.mMagnitude = effectIt->second.getModifier(); - - effectList.push_back(info); - } - state.mPermanentSpellEffects[it->first->mId] = effectList; - } - - for (std::map::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) - { - state.mCorprusSpells[it->first->mId].mWorsenings = mCorprusSpells.at(it->first).mWorsenings; - state.mCorprusSpells[it->first->mId].mNextWorsening = mCorprusSpells.at(it->first).mNextWorsening.toEsm(); - } } } diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index e635f4f56..a4a599f8b 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -22,6 +22,8 @@ namespace ESM namespace MWMechanics { + class CreatureStats; + class MagicEffects; /// \brief Spell list @@ -33,7 +35,8 @@ namespace MWMechanics public: typedef const ESM::Spell* SpellKey; - struct SpellParams { + struct SpellParams + { std::map mEffectRands; // std::set mPurgedEffects; // indices of purged effects }; @@ -41,27 +44,14 @@ namespace MWMechanics typedef std::map TContainer; typedef TContainer::const_iterator TIterator; - struct CorprusStats - { - static const int sWorseningPeriod = 24; - - int mWorsenings; - MWWorld::TimeStamp mNextWorsening; - }; - private: TContainer mSpells; - // spell-tied effects that will be applied even after removing the spell (currently used to keep positive effects when corprus is removed) - std::map mPermanentSpellEffects; - // Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different) std::string mSelectedSpell; std::map mUsedPowers; - std::map mCorprusSpells; - mutable bool mSpellsChanged; mutable MagicEffects mEffects; mutable std::map mSourcedEffects; @@ -73,9 +63,7 @@ namespace MWMechanics public: Spells(); - void worsenCorprus(const ESM::Spell* spell); static bool hasCorprusEffect(const ESM::Spell *spell); - const std::map & getCorprusSpells() const; void purgeEffect(int effectId); void purgeEffect(int effectId, const std::string & sourceId); @@ -128,7 +116,7 @@ namespace MWMechanics void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const; - void readState (const ESM::SpellState& state); + void readState (const ESM::SpellState& state, CreatureStats* creatureStats); void writeState (ESM::SpellState& state) const; }; } diff --git a/apps/openmw/mwmechanics/stat.cpp b/apps/openmw/mwmechanics/stat.cpp index f53052a28..6a559a361 100644 --- a/apps/openmw/mwmechanics/stat.cpp +++ b/apps/openmw/mwmechanics/stat.cpp @@ -256,10 +256,17 @@ namespace MWMechanics void AttributeValue::damage(float damage) { - mDamage += std::min(damage, (float)getModified()); + float threshold = mBase + mModifier; + + if (mDamage + damage > threshold) + mDamage = threshold; + else + mDamage += damage; } void AttributeValue::restore(float amount) { + if (mDamage <= 0) return; + mDamage -= std::min(mDamage, amount); } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 45eafa960..8ba0cdcf2 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -499,6 +499,7 @@ namespace MWScript creatureStats.getSpells().purgeEffect(effect.first.mId); } + MWBase::Environment::get().getMechanicsManager()->restoreStatsAfterCorprus(ptr, id); creatureStats.getSpells().remove (id); MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index d4358532c..8d2dfe6a4 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -921,16 +921,16 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito } } -void MWWorld::InventoryStore::purgeEffect(short effectId) +void MWWorld::InventoryStore::purgeEffect(short effectId, bool wholeSpell) { for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it) { if (*it != end()) - purgeEffect(effectId, (*it)->getCellRef().getRefId()); + purgeEffect(effectId, (*it)->getCellRef().getRefId(), wholeSpell); } } -void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId) +void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId, bool wholeSpell) { TEffectMagnitudes::iterator effectMagnitudeIt = mPermanentMagicEffectMagnitudes.find(sourceId); if (effectMagnitudeIt == mPermanentMagicEffectMagnitudes.end()) @@ -963,6 +963,12 @@ void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sou if (effectIt->mEffectID != effectId) continue; + if (wholeSpell) + { + mPermanentMagicEffectMagnitudes.erase(sourceId); + return; + } + float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom; magnitude *= params[i].mMultiplier; diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index e18132f4b..d597e5f30 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -203,10 +203,10 @@ namespace MWWorld void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor); - void purgeEffect (short effectId); + void purgeEffect (short effectId, bool wholeSpell = false); ///< Remove a magic effect - void purgeEffect (short effectId, const std::string& sourceId); + void purgeEffect (short effectId, const std::string& sourceId, bool wholeSpell = false); ///< Remove a magic effect virtual void clear(); diff --git a/components/esm/attr.hpp b/components/esm/attr.hpp index c0a54c659..dd891a96d 100644 --- a/components/esm/attr.hpp +++ b/components/esm/attr.hpp @@ -21,7 +21,7 @@ struct Attribute Endurance = 5, Personality = 6, Luck = 7, - Length + Length = 8 }; AttributeID mId; diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 2270bb6dd..9c6ee838e 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -134,6 +134,17 @@ void ESM::CreatureStats::load (ESMReader &esm) for (int i=0; i<4; ++i) mAiSettings[i].load(esm); } + + while (esm.isNextSub("CORP")) + { + std::string id = esm.getHString(); + + CorprusStats stats; + esm.getHNT(stats.mWorsenings, "WORS"); + esm.getHNT(stats.mNextWorsening, "TIME"); + + mCorprusSpells[id] = stats; + } } void ESM::CreatureStats::save (ESMWriter &esm) const @@ -218,6 +229,15 @@ void ESM::CreatureStats::save (ESMWriter &esm) const for (int i=0; i<4; ++i) mAiSettings[i].save(esm); } + + for (std::map::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) + { + esm.writeHNString("CORP", it->first); + + const CorprusStats & stats = it->second; + esm.writeHNT("WORS", stats.mWorsenings); + esm.writeHNT("TIME", stats.mNextWorsening); + } } void ESM::CreatureStats::blank() @@ -245,4 +265,5 @@ void ESM::CreatureStats::blank() mDrawState = 0; mDeathAnimation = -1; mLevel = 1; + mCorprusSpells.clear(); } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 8c69553a3..17ab4bbe7 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -9,6 +9,7 @@ #include "defs.hpp" +#include "attr.hpp" #include "spellstate.hpp" #include "activespells.hpp" #include "magiceffects.hpp" @@ -22,7 +23,13 @@ namespace ESM // format 0, saved games only struct CreatureStats { - StatState mAttributes[8]; + struct CorprusStats + { + int mWorsenings[Attribute::Length]; + TimeStamp mNextWorsening; + }; + + StatState mAttributes[Attribute::Length]; StatState mDynamic[3]; MagicEffects mMagicEffects; @@ -76,9 +83,9 @@ namespace ESM int mDrawState; signed char mDeathAnimation; ESM::TimeStamp mTimeOfDeath; - int mLevel; + std::map mCorprusSpells; SpellState mSpells; ActiveSpells mActiveSpells; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 6696ed478..cda411314 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 9; +int ESM::SavedGame::sCurrentFormat = 10; void ESM::SavedGame::load (ESMReader &esm) { diff --git a/components/esm/spellstate.cpp b/components/esm/spellstate.cpp index a21078e10..2eb1e7867 100644 --- a/components/esm/spellstate.cpp +++ b/components/esm/spellstate.cpp @@ -33,23 +33,36 @@ namespace ESM mSpells[id] = state; } + // Obsolete while (esm.isNextSub("PERM")) { std::string spellId = esm.getHString(); - std::vector permEffectList; - while (esm.isNextSub("EFID")) + + while (true) { + ESM_Context restorePoint = esm.getContext(); + + if (!esm.isNextSub("EFID")) + break; + PermanentSpellEffectInfo info; esm.getHT(info.mId); - esm.getHNT(info.mArg, "ARG_"); - esm.getHNT(info.mMagnitude, "MAGN"); + if (esm.isNextSub("BASE")) + { + esm.restoreContext(restorePoint); + return; + } + else + esm.getHNT(info.mArg, "ARG_"); + esm.getHNT(info.mMagnitude, "MAGN"); permEffectList.push_back(info); } mPermanentSpellEffects[spellId] = permEffectList; } + // Obsolete while (esm.isNextSub("CORP")) { std::string id = esm.getHString(); @@ -91,19 +104,6 @@ namespace ESM esm.writeHNT("PURG", *pIt); } - for (std::map >::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) - { - esm.writeHNString("PERM", it->first); - - const std::vector & effects = it->second; - for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) - { - esm.writeHNT("EFID", effectIt->mId); - esm.writeHNT("ARG_", effectIt->mArg); - esm.writeHNT("MAGN", effectIt->mMagnitude); - } - } - for (std::map::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) { esm.writeHNString("CORP", it->first); diff --git a/components/esm/spellstate.hpp b/components/esm/spellstate.hpp index ec613afab..55c57611a 100644 --- a/components/esm/spellstate.hpp +++ b/components/esm/spellstate.hpp @@ -29,15 +29,16 @@ namespace ESM float mMagnitude; }; - struct SpellParams { + struct SpellParams + { std::map mEffectRands; std::set mPurgedEffects; }; typedef std::map TContainer; TContainer mSpells; + // FIXME: obsolete, used only for old saves std::map > mPermanentSpellEffects; - std::map mCorprusSpells; std::map mUsedPowers; From 5468fcb29f0636bd82b56b4bb1f9a8a72f059098 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 23 Dec 2018 15:18:33 +0400 Subject: [PATCH 154/227] Store attributes and skills values as floats (bug #4021) --- CHANGELOG.md | 1 + apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwclass/creature.hpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 10 ++--- apps/openmw/mwclass/npc.hpp | 2 +- apps/openmw/mwgui/jailscreen.cpp | 4 +- apps/openmw/mwgui/levelupdialog.cpp | 2 +- apps/openmw/mwgui/pickpocketitemmodel.cpp | 2 +- apps/openmw/mwgui/statswindow.cpp | 2 +- apps/openmw/mwgui/trainingwindow.cpp | 4 +- apps/openmw/mwmechanics/actors.cpp | 8 ++-- apps/openmw/mwmechanics/alchemy.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/combat.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 10 ++--- apps/openmw/mwmechanics/creaturestats.hpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 14 +++--- apps/openmw/mwmechanics/npcstats.cpp | 10 ++--- apps/openmw/mwmechanics/pickpocket.cpp | 4 +- apps/openmw/mwmechanics/repair.cpp | 6 +-- apps/openmw/mwmechanics/security.cpp | 4 +- apps/openmw/mwmechanics/spellresistance.cpp | 4 +- apps/openmw/mwmechanics/spellutil.cpp | 4 +- apps/openmw/mwmechanics/stat.cpp | 22 ++++----- apps/openmw/mwmechanics/stat.hpp | 22 ++++----- apps/openmw/mwscript/statsextensions.cpp | 24 +++++----- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 6 +-- components/compiler/extensions0.cpp | 12 ++--- components/esm/creaturestats.cpp | 3 +- components/esm/creaturestats.hpp | 2 +- components/esm/npcstats.cpp | 13 +++--- components/esm/npcstats.hpp | 2 +- components/esm/player.cpp | 5 ++- components/esm/player.hpp | 4 +- components/esm/savedgame.cpp | 2 +- components/esm/statstate.cpp | 45 ++++++++++++++----- components/esm/statstate.hpp | 2 +- 41 files changed, 153 insertions(+), 124 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c48660848..298220699 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #3676: NiParticleColorModifier isn't applied properly Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects + Bug #4021: Attributes and skills are not stored as floats Bug #4623: Corprus implementation is incorrect Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #5108: Savegame bloating due to inefficient fog textures format diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index e649bba12..3f9bfb859 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -276,7 +276,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); int armorSkillType = getEquipmentSkill(ptr); - int armorSkill = actor.getClass().getSkill(actor, armorSkillType); + float armorSkill = actor.getClass().getSkill(actor, armorSkillType); const MWBase::World *world = MWBase::Environment::get().getWorld(); int iBaseArmorSkill = world->getStore().get().find("iBaseArmorSkill")->mValue.getInteger(); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 375b70dde..3feb25ca7 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -605,7 +605,7 @@ namespace MWClass float Creature::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - return static_cast(stats.getAttribute(ESM::Attribute::Strength).getModified() * 5); + return stats.getAttribute(ESM::Attribute::Strength).getModified() * 5; } int Creature::getServices(const MWWorld::ConstPtr &actor) const @@ -745,7 +745,7 @@ namespace MWClass throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } - int Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const + float Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 9be5c4272..3288c0d11 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -108,7 +108,7 @@ namespace MWClass virtual bool canSwim (const MWWorld::ConstPtr &ptr) const; virtual bool canWalk (const MWWorld::ConstPtr &ptr) const; - virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr &ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index bd61131bf..a007ad115 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -125,7 +125,7 @@ namespace MWClass } MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - int alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); + float alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index df483962a..5efc96ca1 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -127,8 +127,8 @@ namespace } // initial health - int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); - int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); + float strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); + float endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); int multiplier = 3; @@ -1011,7 +1011,7 @@ namespace MWClass gmst.fJumpEncumbranceMultiplier->mValue.getFloat() * (1.0f - Npc::getNormalizedEncumbrance(ptr)); - float a = static_cast(getSkill(ptr, ESM::Skill::Acrobatics)); + float a = getSkill(ptr, ESM::Skill::Acrobatics); float b = 0.0f; if(a > 50.0f) { @@ -1136,7 +1136,7 @@ namespace MWClass float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); + float unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); float ratings[MWWorld::InventoryStore::Slots]; for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) @@ -1283,7 +1283,7 @@ namespace MWClass return MWWorld::Ptr(cell.insert(ref), &cell); } - int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const + float Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const { return getNpcStats(ptr).getSkill(skill).getModified(); } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 3d63697fb..ae4f32d13 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -129,7 +129,7 @@ namespace MWClass virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 4a89304b3..67124884f 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -95,9 +95,9 @@ namespace MWGui MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill); if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak) - value.setBase(std::min(100, value.getBase()+1)); + value.setBase(std::min(100.f, value.getBase()+1)); else - value.setBase(std::max(0, value.getBase()-1)); + value.setBase(std::max(0.f, value.getBase()-1)); } const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index eb165254a..86f92db6f 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -157,7 +157,7 @@ namespace MWGui mAttributeValues[i]->setEnabled(true); availableAttributes++; - int mult = pcStats.getLevelupAttributeMultiplier (i); + float mult = pcStats.getLevelupAttributeMultiplier (i); mult = std::min(mult, 100-pcStats.getAttribute(i).getBase()); text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult)); } diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index 2a167be2d..b4de5cb50 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -22,7 +22,7 @@ namespace MWGui { MWWorld::Ptr player = MWMechanics::getPlayer(); mSourceModel = sourceModel; - int chance = player.getClass().getSkill(player, ESM::Skill::Sneak); + float chance = player.getClass().getSkill(player, ESM::Skill::Sneak); mSourceModel->update(); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 8998e3a80..4959396cc 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -159,7 +159,7 @@ namespace MWGui for (int i=0; ids[i]; ++i) if (ids[i]==id) { - setText (id, std::to_string(value.getModified())); + setText (id, std::to_string(static_cast(value.getModified()))); MyGUI::TextBox* box; getWidget(box, id); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index acc2ef72a..e4e4bae5a 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -74,11 +74,11 @@ namespace MWGui mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); // NPC can train you in his best 3 skills - std::vector< std::pair > skills; + std::vector< std::pair > skills; for (int i=0; i& settings = MWBase::Environment::get().getWorld()->getStore().get(); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); health = 0.1f * endurance; float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat (); @@ -765,7 +765,7 @@ namespace MWMechanics { CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr); - int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified(); + float intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified(); float base = 1.f; if (ptr == getPlayer()) @@ -844,7 +844,7 @@ namespace MWMechanics float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat (); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); if (normalizedEncumbrance > 1) @@ -871,7 +871,7 @@ namespace MWMechanics return; // Restore fatigue - int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); + float endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat (); static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index b490db436..116937fcd 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -468,7 +468,7 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc) { - int alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); + float alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0a332a10c..e4f870ed0 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2128,7 +2128,7 @@ void CharacterController::update(float duration, bool animationOnly) cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true); } - const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); + const float acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); if (healthLost > (acrobaticsSkill * fatigueTerm)) { if (!godmode) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 9698892e4..183845b8c 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -101,7 +101,7 @@ namespace MWMechanics blockerTerm *= gmst.find("fBlockStillBonus")->mValue.getFloat(); blockerTerm *= blockerStats.getFatigueTerm(); - int attackerSkill = 0; + float attackerSkill = 0; if (weapon.isEmpty()) attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand); else diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index a64fb087c..0f11b8b2e 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -126,7 +126,7 @@ namespace MWMechanics return mMagicEffects; } - void CreatureStats::setAttribute(int index, int base) + void CreatureStats::setAttribute(int index, float base) { AttributeValue current = getAttribute(index); current.setBase(base); @@ -152,10 +152,10 @@ namespace MWMechanics index == ESM::Attribute::Agility || index == ESM::Attribute::Endurance) { - int strength = getAttribute(ESM::Attribute::Strength).getModified(); - int willpower = getAttribute(ESM::Attribute::Willpower).getModified(); - int agility = getAttribute(ESM::Attribute::Agility).getModified(); - int endurance = getAttribute(ESM::Attribute::Endurance).getModified(); + float strength = getAttribute(ESM::Attribute::Strength).getModified(); + float willpower = getAttribute(ESM::Attribute::Willpower).getModified(); + float agility = getAttribute(ESM::Attribute::Agility).getModified(); + float endurance = getAttribute(ESM::Attribute::Endurance).getModified(); DynamicStat fatigue = getFatigue(); float diff = (strength+willpower+agility+endurance) - fatigue.getBase(); float currentToBaseRatio = fatigue.getBase() > 0 ? (fatigue.getCurrent() / fatigue.getBase()) : 0; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 5a5dd3f12..b35c1e3b6 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -138,7 +138,7 @@ namespace MWMechanics void setAttribute(int index, const AttributeValue &value); // Shortcut to set only the base - void setAttribute(int index, int base); + void setAttribute(int index, float base); void setHealth(const DynamicStat &value); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5376ec86e..6933907db 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -688,10 +688,10 @@ namespace MWMechanics // I suppose the temporary disposition change (second param to getDerivedDisposition()) _has_ to be considered here, // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = getDerivedDisposition(ptr); - float a = static_cast(std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100)); + float a = std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100.f); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d = static_cast(std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100)); + float d = std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100.f); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); @@ -1621,8 +1621,8 @@ namespace MWMechanics static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat(); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); - int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); - int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float bootWeight = 0; if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr)) { @@ -1645,10 +1645,10 @@ namespace MWMechanics float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility; CreatureStats& observerStats = observer.getClass().getCreatureStats(observer); - int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); - int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); + float obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); + float obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude(); - int obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak); + float obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak); float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index ee48ea7d5..1e9003a2f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -226,9 +226,9 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook) { - int base = getSkill (skillIndex).getBase(); + float base = getSkill (skillIndex).getBase(); - if (base >= 100) + if (base >= 100.f) return; base += 1; @@ -299,7 +299,7 @@ void MWMechanics::NpcStats::levelUp() for (int i=0; i(stats.getAttribute(ESM::Attribute::Agility).getModified()); - float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); + float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); return (add + 0.2f * agility + 0.1f * luck + sneak) * stats.getFatigueTerm(); } diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index faa0e3b09..389d00d85 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -32,9 +32,9 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); float fatigueTerm = stats.getFatigueTerm(); - int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); - int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - int armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); + float pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); + float pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fRepairAmountMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index ab286cbee..001375feb 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -19,8 +19,8 @@ namespace MWMechanics : mActor(actor) { CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - mAgility = static_cast(creatureStats.getAttribute(ESM::Attribute::Agility).getModified()); - mLuck = static_cast(creatureStats.getAttribute(ESM::Attribute::Luck).getModified()); + mAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified(); + mLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified(); mSecuritySkill = static_cast(actor.getClass().getSkill(actor, ESM::Skill::Security)); mFatigueTerm = creatureStats.getFatigueTerm(); } diff --git a/apps/openmw/mwmechanics/spellresistance.cpp b/apps/openmw/mwmechanics/spellresistance.cpp index a187600fb..1edf14091 100644 --- a/apps/openmw/mwmechanics/spellresistance.cpp +++ b/apps/openmw/mwmechanics/spellresistance.cpp @@ -40,8 +40,8 @@ namespace MWMechanics float resistance = getEffectResistanceAttribute(effectId, magicEffects); - int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); + float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float x = (willpower + 0.1f * luck) * stats.getFatigueTerm(); // This makes spells that are easy to cast harder to resist and vice versa diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index bb4953e48..8b2f5c46c 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -94,8 +94,8 @@ namespace MWMechanics CreatureStats& stats = actor.getClass().getCreatureStats(actor); - int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck); diff --git a/apps/openmw/mwmechanics/stat.cpp b/apps/openmw/mwmechanics/stat.cpp index 6a559a361..7f71cf9b1 100644 --- a/apps/openmw/mwmechanics/stat.cpp +++ b/apps/openmw/mwmechanics/stat.cpp @@ -227,29 +227,29 @@ namespace MWMechanics } AttributeValue::AttributeValue() : - mBase(0), mModifier(0), mDamage(0) + mBase(0.f), mModifier(0.f), mDamage(0.f) { } - int AttributeValue::getModified() const + float AttributeValue::getModified() const { - return std::max(0, mBase - (int) mDamage + mModifier); + return std::max(0.f, mBase - mDamage + mModifier); } - int AttributeValue::getBase() const + float AttributeValue::getBase() const { return mBase; } - int AttributeValue::getModifier() const + float AttributeValue::getModifier() const { return mModifier; } - void AttributeValue::setBase(int base) + void AttributeValue::setBase(float base) { mBase = base; } - void AttributeValue::setModifier(int mod) + void AttributeValue::setModifier(float mod) { mModifier = mod; } @@ -275,14 +275,14 @@ namespace MWMechanics return mDamage; } - void AttributeValue::writeState (ESM::StatState& state) const + void AttributeValue::writeState (ESM::StatState& state) const { state.mBase = mBase; state.mMod = mModifier; state.mDamage = mDamage; } - void AttributeValue::readState (const ESM::StatState& state) + void AttributeValue::readState (const ESM::StatState& state) { mBase = state.mBase; mModifier = state.mMod; @@ -303,13 +303,13 @@ namespace MWMechanics mProgress = progress; } - void SkillValue::writeState (ESM::StatState& state) const + void SkillValue::writeState (ESM::StatState& state) const { AttributeValue::writeState (state); state.mProgress = mProgress; } - void SkillValue::readState (const ESM::StatState& state) + void SkillValue::readState (const ESM::StatState& state) { AttributeValue::readState (state); mProgress = state.mProgress; diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 751b80b9c..5f49da48e 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -122,20 +122,20 @@ namespace MWMechanics class AttributeValue { - int mBase; - int mModifier; + float mBase; + float mModifier; float mDamage; // needs to be float to allow continuous damage public: AttributeValue(); - int getModified() const; - int getBase() const; - int getModifier() const; + float getModified() const; + float getBase() const; + float getModifier() const; - void setBase(int base); + void setBase(float base); - void setModifier(int mod); + void setModifier(float mod); // Maximum attribute damage is limited to the modified value. // Note: I think MW applies damage directly to mModified, since you can also @@ -145,8 +145,8 @@ namespace MWMechanics float getDamage() const; - void writeState (ESM::StatState& state) const; - void readState (const ESM::StatState& state); + void writeState (ESM::StatState& state) const; + void readState (const ESM::StatState& state); }; class SkillValue : public AttributeValue @@ -157,8 +157,8 @@ namespace MWMechanics float getProgress() const; void setProgress(float progress); - void writeState (ESM::StatState& state) const; - void readState (const ESM::StatState& state); + void writeState (ESM::StatState& state) const; + void readState (const ESM::StatState& state); }; inline bool operator== (const AttributeValue& left, const AttributeValue& right) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 8ba0cdcf2..37751c6d4 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -95,7 +95,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = + Interpreter::Type_Float value = ptr.getClass() .getCreatureStats (ptr) .getAttribute(mIndex) @@ -118,7 +118,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); @@ -140,7 +140,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass() @@ -155,9 +155,9 @@ namespace MWScript return; if (value < 0) - attribute.setBase(std::max(0, attribute.getBase() + value)); + attribute.setBase(std::max(0.f, attribute.getBase() + value)); else - attribute.setBase(std::min(100, attribute.getBase() + value)); + attribute.setBase(std::min(100.f, attribute.getBase() + value)); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } @@ -345,7 +345,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = ptr.getClass().getSkill(ptr, mIndex); + Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mIndex); runtime.push (value); } @@ -364,7 +364,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr); @@ -386,7 +386,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::SkillValue &skill = ptr.getClass() @@ -396,14 +396,14 @@ namespace MWScript if (value == 0) return; - if (((skill.getBase() <= 0) && (value < 0)) - || ((skill.getBase() >= 100) && (value > 0))) + if (((skill.getBase() <= 0.f) && (value < 0.f)) + || ((skill.getBase() >= 100.f) && (value > 0.f))) return; if (value < 0) - skill.setBase(std::max(0, skill.getBase() + value)); + skill.setBase(std::max(0.f, skill.getBase() + value)); else - skill.setBase(std::min(100, skill.getBase() + value)); + skill.setBase(std::min(100.f, skill.getBase() + value)); } }; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 07981bf2a..d7ee59ee2 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -421,7 +421,7 @@ namespace MWWorld return canSwim(ptr) || canWalk(ptr) || canFly(ptr); } - int Class::getSkill(const MWWorld::Ptr& ptr, int skill) const + float Class::getSkill(const MWWorld::Ptr& ptr, int skill) const { throw std::runtime_error("class does not support skills"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index fb816d810..fd679c43f 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -321,7 +321,7 @@ namespace MWWorld bool isPureLandCreature(const MWWorld::Ptr& ptr) const; bool isMobile(const MWWorld::Ptr& ptr) const; - virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const; virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index a2bd84953..ada211470 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -284,12 +284,12 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots // rate weapon for (int i = 0; i < static_cast(weaponSkillsLength); ++i) { - int max = 0; + float max = 0; int maxWeaponSkill = -1; for (int j = 0; j < static_cast(weaponSkillsLength); ++j) { - int skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); + float skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); if (skillValue > max && !weaponSkillVisited[j]) { max = skillValue; @@ -399,7 +399,7 @@ void MWWorld::InventoryStore::autoEquipArmor (const MWWorld::Ptr& actor, TSlots& static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); + float unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index c67af29c2..60b4a7451 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -423,13 +423,13 @@ namespace Compiler for (int i=0; i mAttributes[Attribute::Length]; + StatState mAttributes[Attribute::Length]; StatState mDynamic[3]; MagicEffects mMagicEffects; diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index c5fa2a09e..277335e8c 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -31,8 +31,9 @@ void ESM::NpcStats::load (ESMReader &esm) mDisposition = 0; esm.getHNOT (mDisposition, "DISP"); + bool intFallback = esm.getFormat() < 11; for (int i=0; i<27; ++i) - mSkills[i].load (esm); + mSkills[i].load (esm, intFallback); mWerewolfDeprecatedData = false; if (esm.getFormat() < 8 && esm.peekNextSub("STBA")) @@ -40,17 +41,17 @@ void ESM::NpcStats::load (ESMReader &esm) // we have deprecated werewolf skills, stored interleaved // Load into one big vector, then remove every 2nd value mWerewolfDeprecatedData = true; - std::vector > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0])); + std::vector > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0])); for (int i=0; i<27; ++i) { - ESM::StatState skill; - skill.load(esm); + ESM::StatState skill; + skill.load(esm, intFallback); skills.push_back(skill); } int i=0; - for (std::vector >::iterator it = skills.begin(); it != skills.end(); ++i) + for (std::vector >::iterator it = skills.begin(); it != skills.end(); ++i) { if (i%2 == 1) it = skills.erase(it); @@ -68,7 +69,7 @@ void ESM::NpcStats::load (ESMReader &esm) { ESM::StatState dummy; for (int i=0; i<8; ++i) - dummy.load(esm); + dummy.load(esm, intFallback); mWerewolfDeprecatedData = true; } diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index 467a099ce..3ad94b543 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -31,7 +31,7 @@ namespace ESM std::map mFactions; // lower case IDs int mDisposition; - StatState mSkills[27]; + StatState mSkills[27]; int mBounty; int mReputation; int mWerewolfKills; diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 571a10a8c..3850390e9 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -43,12 +43,13 @@ void ESM::Player::load (ESMReader &esm) checkPrevItems = false; } + bool intFallback = esm.getFormat() < 11; if (esm.hasMoreSubs()) { for (int i=0; i mSaveAttributes[ESM::Attribute::Length]; - StatState mSaveSkills[ESM::Skill::Length]; + StatState mSaveAttributes[ESM::Attribute::Length]; + StatState mSaveSkills[ESM::Skill::Length]; typedef std::map PreviousItems; // previous equipped items, needed for bound spells PreviousItems mPreviousItems; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index cda411314..063ae86ed 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 10; +int ESM::SavedGame::sCurrentFormat = 11; void ESM::SavedGame::load (ESMReader &esm) { diff --git a/components/esm/statstate.cpp b/components/esm/statstate.cpp index c17bedd81..b9ddc3efd 100644 --- a/components/esm/statstate.cpp +++ b/components/esm/statstate.cpp @@ -9,19 +9,44 @@ namespace ESM StatState::StatState() : mBase(0), mMod(0), mCurrent(0), mDamage(0), mProgress(0) {} template - void StatState::load(ESMReader &esm) + void StatState::load(ESMReader &esm, bool intFallback) { - esm.getHNT(mBase, "STBA"); + // We changed stats values from integers to floats; ensure backwards compatibility + if (intFallback) + { + int base = 0; + esm.getHNT(base, "STBA"); + mBase = static_cast(base); - mMod = 0; - esm.getHNOT(mMod, "STMO"); - mCurrent = 0; - esm.getHNOT(mCurrent, "STCU"); + int mod = 0; + esm.getHNOT(mod, "STMO"); + mMod = static_cast(mod); - // mDamage was changed to a float; ensure backwards compatibility - T oldDamage = 0; - esm.getHNOT(oldDamage, "STDA"); - mDamage = static_cast(oldDamage); + int current = 0; + esm.getHNOT(current, "STCU"); + mCurrent = static_cast(current); + + // mDamage was changed to a float; ensure backwards compatibility + int oldDamage = 0; + esm.getHNOT(oldDamage, "STDA"); + mDamage = static_cast(oldDamage); + } + else + { + mBase = 0; + esm.getHNT(mBase, "STBA"); + + mMod = 0; + esm.getHNOT(mMod, "STMO"); + + mCurrent = 0; + esm.getHNOT(mCurrent, "STCU"); + + mDamage = 0; + esm.getHNOT(mDamage, "STDF"); + + mProgress = 0; + } esm.getHNOT(mDamage, "STDF"); diff --git a/components/esm/statstate.hpp b/components/esm/statstate.hpp index 47aeb0331..d81d24a61 100644 --- a/components/esm/statstate.hpp +++ b/components/esm/statstate.hpp @@ -20,7 +20,7 @@ namespace ESM StatState(); - void load (ESMReader &esm); + void load (ESMReader &esm, bool intFallback = false); void save (ESMWriter &esm) const; }; } From d35ccc39c6f95b0957e3e8b943f7694559b8f3c5 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 1 Jun 2020 13:51:56 +0200 Subject: [PATCH 155/227] Fix build tests with double precision bullet --- apps/openmw/CMakeLists.txt | 4 ---- components/CMakeLists.txt | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 68e3949ba..a6bd85b1d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -15,10 +15,6 @@ set(GAME_HEADER engine.hpp ) -if (BULLET_USE_DOUBLES) - add_definitions(-DBT_USE_DOUBLE_PRECISION) -endif() - source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f626fe714..507b25cd1 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -287,5 +287,5 @@ endif() set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) if (BULLET_USE_DOUBLES) - add_definitions(-DBT_USE_DOUBLE_PRECISION) + target_compile_definitions(components PUBLIC BT_USE_DOUBLE_PRECISION) endif() From 86c1d0f4beffe86f367a74fd32b53d9fca5a4343 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 3 Jun 2020 22:36:55 +0000 Subject: [PATCH 156/227] Warn about fake stub Python --- CI/before_script.msvc.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 2f40aef9c..8f70e5369 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -13,7 +13,16 @@ MISSINGTOOLS=0 command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; } command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; } -command -v python >/dev/null 2>&1 || { echo "Warning: Python is not on the path, automatic Qt installation impossible."; } + +MISSINGPYTHON=0 +if ! command -v python >/dev/null 2>&1; then + echo "Warning: Python is not on the path, automatic Qt installation impossible." + MISSINGPYTHON=1 +elif ! python --version >/dev/null 2>&1; then + echo "Warning: Python is (probably) fake stub Python that comes bundled with newer versions of Windows, automatic Qt installation impossible." + echo "If you think you have Python installed, try changing the order of your PATH environment variable in Advanced System Settings." + MISSINGPYTHON=1 +fi if [ $MISSINGTOOLS -ne 0 ]; then wrappedExit 1 @@ -745,6 +754,11 @@ fi if [ -d 'Qt/5.15.0' ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then + if [ $MISSINGTOOLS -ne 0 ]; then + echo "Can't be automatically installed without Python." + wrappedExit 1 + fi + pushd "$DEPS" > /dev/null if ! [ -d 'aqt-venv' ]; then echo " Creating Virtualenv for aqt..." From 6e267e398e642e8cd200dd6cbcd2eb7c85ecaeb2 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 3 Jun 2020 22:38:08 +0000 Subject: [PATCH 157/227] Fix copy-paste snafu --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 8f70e5369..75bde1f81 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -754,7 +754,7 @@ fi if [ -d 'Qt/5.15.0' ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then - if [ $MISSINGTOOLS -ne 0 ]; then + if [ $MISSINGPYTHON -ne 0 ]; then echo "Can't be automatically installed without Python." wrappedExit 1 fi From da8ea9d8c7a94326307ec1122572d54caee33c09 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 2 Jun 2020 21:30:46 +0200 Subject: [PATCH 158/227] Mark not changing AiPackages fields as const --- apps/openmw/mwmechanics/aiactivate.hpp | 2 +- apps/openmw/mwmechanics/aiavoiddoor.hpp | 2 +- apps/openmw/mwmechanics/aicast.cpp | 18 ++++++++--- apps/openmw/mwmechanics/aicast.hpp | 8 ++--- apps/openmw/mwmechanics/aiescort.cpp | 13 +++----- apps/openmw/mwmechanics/aiescort.hpp | 16 ++++----- apps/openmw/mwmechanics/aiface.hpp | 3 +- apps/openmw/mwmechanics/aifollow.cpp | 13 ++++---- apps/openmw/mwmechanics/aifollow.hpp | 16 ++++----- apps/openmw/mwmechanics/aitravel.hpp | 8 ++--- apps/openmw/mwmechanics/aiwander.cpp | 43 +++++++++++++------------ apps/openmw/mwmechanics/aiwander.hpp | 12 +++---- 12 files changed, 80 insertions(+), 74 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 5a9e3d6d8..5a96f4cdb 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -34,7 +34,7 @@ namespace MWMechanics void writeState(ESM::AiSequence::AiSequence& sequence) const final; private: - std::string mObjectId; + const std::string mObjectId; }; } #endif // GAME_MWMECHANICS_AIACTIVATE_H diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index cc02c4de1..fdbf7ebc7 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -33,7 +33,7 @@ namespace MWMechanics private: float mDuration; - MWWorld::ConstPtr mDoorPtr; + const MWWorld::ConstPtr mDoorPtr; osg::Vec3f mLastPos; int mDirection; diff --git a/apps/openmw/mwmechanics/aicast.cpp b/apps/openmw/mwmechanics/aicast.cpp index de61851cd..cc4c03bf1 100644 --- a/apps/openmw/mwmechanics/aicast.cpp +++ b/apps/openmw/mwmechanics/aicast.cpp @@ -10,12 +10,22 @@ #include "creaturestats.hpp" #include "steering.hpp" +namespace MWMechanics +{ + namespace + { + float getInitialDistance(const std::string& spellId) + { + ActionSpell action = ActionSpell(spellId); + bool isRanged; + return action.getCombatRange(isRanged); + } + } +} + MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell) - : mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0) + : mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(getInitialDistance(spellId)) { - ActionSpell action = ActionSpell(spellId); - bool isRanged; - mDistance = action.getCombatRange(isRanged); } bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration) diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index 21b629f24..cdf7db2bf 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -27,11 +27,11 @@ namespace MWMechanics bool shouldCancelPreviousAi() const final { return false; } private: - std::string mTargetId; - std::string mSpellId; + const std::string mTargetId; + const std::string mSpellId; bool mCasting; - bool mManual; - float mDistance; + const bool mManual; + const float mDistance; }; } diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 5e1d38331..216547f58 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -26,7 +26,6 @@ namespace MWMechanics , mCellY(std::numeric_limits::max()) { mTargetActorRefId = actorId; - mMaxDist = 450; } AiEscort::AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z) @@ -35,24 +34,20 @@ namespace MWMechanics , mCellY(std::numeric_limits::max()) { mTargetActorRefId = actorId; - mMaxDist = 450; } AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort) : mCellId(escort->mCellId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ) - , mMaxDist(450) + // mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration. + // The exact value of mDuration only matters for repeating packages. + // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. + , mDuration(escort->mRemainingDuration > 0) , mRemainingDuration(escort->mRemainingDuration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { mTargetActorRefId = escort->mTargetId; mTargetActorId = escort->mTargetActorId; - // mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration. - // The exact value of mDuration only matters for repeating packages. - if (mRemainingDuration > 0) // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. - mDuration = 1; - else - mDuration = 0; } bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 9f5ac2f42..c12de1fac 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -45,16 +45,16 @@ namespace MWMechanics osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } private: - std::string mCellId; - float mX; - float mY; - float mZ; - float mMaxDist; - float mDuration; // In hours + const std::string mCellId; + const float mX; + const float mY; + const float mZ; + float mMaxDist = 450; + const float mDuration; // In hours float mRemainingDuration; // In hours - int mCellX; - int mCellY; + const int mCellX; + const int mCellY; }; } #endif diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index ce1c9847b..516dd18dc 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -20,7 +20,8 @@ namespace MWMechanics bool shouldCancelPreviousAi() const final { return false; } private: - float mTargetX, mTargetY; + const float mTargetX; + const float mTargetY; }; } diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index bffa238d5..eb5b595ab 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -58,18 +58,17 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded) } AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) - : mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded), mRemainingDuration(follow->mRemainingDuration) + : mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded) + // mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration. + // The exact value of mDuration only matters for repeating packages. + // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. + , mDuration(follow->mRemainingDuration) + , mRemainingDuration(follow->mRemainingDuration) , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ) , mCellId(follow->mCellId), mActive(follow->mActive), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = follow->mTargetId; mTargetActorId = follow->mTargetActorId; - // mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration. - // The exact value of mDuration only matters for repeating packages. - if (mRemainingDuration > 0) // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. - mDuration = 1; - else - mDuration = 0; } bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 39a10294b..865f4171b 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -86,16 +86,16 @@ namespace MWMechanics private: /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ - bool mAlwaysFollow; - bool mCommanded; - float mDuration; // Hours + const bool mAlwaysFollow; + const bool mCommanded; + const float mDuration; // Hours float mRemainingDuration; // Hours - float mX; - float mY; - float mZ; - std::string mCellId; + const float mX; + const float mY; + const float mZ; + const std::string mCellId; bool mActive; // have we spotted the target? - int mFollowIndex; + const int mFollowIndex; static int mFollowIndexCounter; }; diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index beaf2819f..4f785e237 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -37,11 +37,11 @@ namespace MWMechanics osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } private: - float mX; - float mY; - float mZ; + const float mX; + const float mY; + const float mZ; - bool mHidden; + const bool mHidden; }; } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 50a46432b..584131bbe 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -1,5 +1,7 @@ #include "aiwander.hpp" +#include + #include #include #include @@ -33,6 +35,8 @@ namespace MWMechanics // distance must be long enough that NPC will need to move to get there. static const int MINIMUM_WANDER_DISTANCE = DESTINATION_TOLERANCE * 2; + static const std::size_t MAX_IDLE_SIZE = 8; + const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = { std::string("idle2"), @@ -94,25 +98,28 @@ namespace MWMechanics { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; } + + std::vector getInitialIdle(const std::vector& idle) + { + std::vector result(MAX_IDLE_SIZE, 0); + std::copy_n(idle.begin(), std::min(MAX_IDLE_SIZE, idle.size()), result.begin()); + return result; + } + + std::vector getInitialIdle(const unsigned char (&idle)[MAX_IDLE_SIZE]) + { + return std::vector(std::begin(idle), std::end(idle)); + } } AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): - mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), + mDistance(std::max(0, distance)), + mDuration(std::max(0, duration)), + mRemainingDuration(duration), mTimeOfDay(timeOfDay), + mIdle(getInitialIdle(idle)), mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false) { - mIdle.resize(8, 0); - init(); - } - - void AiWander::init() - { - // NOTE: mDistance and mDuration must be set already - - if(mDistance < 0) - mDistance = 0; - if(mDuration < 0) - mDuration = 0; } /* @@ -235,7 +242,6 @@ namespace MWMechanics stopWalking(actor); // Reset package so it can be used again mRemainingDuration=mDuration; - init(); return true; } @@ -879,10 +885,11 @@ namespace MWMechanics } AiWander::AiWander (const ESM::AiSequence::AiWander* wander) - : mDistance(wander->mData.mDistance) - , mDuration(wander->mData.mDuration) + : mDistance(std::max(static_cast(0), wander->mData.mDistance)) + , mDuration(std::max(static_cast(0), wander->mData.mDuration)) , mRemainingDuration(wander->mDurationData.mRemainingDuration) , mTimeOfDay(wander->mData.mTimeOfDay) + , mIdle(getInitialIdle(wander->mData.mIdle)) , mRepeat(wander->mData.mShouldRepeat != 0) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition) , mHasDestination(false) @@ -891,11 +898,7 @@ namespace MWMechanics { if (mStoredInitialActorPosition) mInitialActorPosition = wander->mInitialActorPosition; - for (int i=0; i<8; ++i) - mIdle.push_back(wander->mData.mIdle[i]); if (mRemainingDuration <= 0 || mRemainingDuration >= 24) mRemainingDuration = mDuration; - - init(); } } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index bb5872eef..8eb735205 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -114,8 +114,6 @@ namespace MWMechanics } private: - // NOTE: mDistance and mDuration must be set already - void init(); void stopWalking(const MWWorld::Ptr& actor); /// Have the given actor play an idle animation @@ -136,12 +134,12 @@ namespace MWMechanics bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination); void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage); - int mDistance; // how far the actor can wander from the spawn point - int mDuration; + const int mDistance; // how far the actor can wander from the spawn point + const int mDuration; float mRemainingDuration; - int mTimeOfDay; - std::vector mIdle; - bool mRepeat; + const int mTimeOfDay; + const std::vector mIdle; + const bool mRepeat; bool mStoredInitialActorPosition; osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell From 81805b726337c8c2da9c104af72ecb428a86de0b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 3 Jun 2020 11:32:28 +0400 Subject: [PATCH 159/227] Introduce a separate class to control world date and time --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 19 +- apps/openmw/mwgui/waitdialog.cpp | 9 +- apps/openmw/mwstate/statemanagerimp.cpp | 6 +- apps/openmw/mwworld/datetimemanager.cpp | 227 ++++++++++++++++++++++++ apps/openmw/mwworld/datetimemanager.hpp | 43 +++++ apps/openmw/mwworld/globals.cpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 197 ++++---------------- apps/openmw/mwworld/worldimp.hpp | 30 +--- components/esm/defs.hpp | 8 + components/esm/savedgame.cpp | 1 - components/esm/savedgame.hpp | 12 +- 12 files changed, 338 insertions(+), 219 deletions(-) create mode 100644 apps/openmw/mwworld/datetimemanager.cpp create mode 100644 apps/openmw/mwworld/datetimemanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 68e3949ba..9072deb48 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -71,7 +71,7 @@ add_openmw_dir (mwworld actionequip timestamp actionalchemy cellstore actionapply actioneat store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager - cellpreloader + cellpreloader datetimemanager ) add_openmw_dir (mwphysics diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 6bf4cbaae..627e2f2b7 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -48,6 +48,7 @@ namespace ESM struct EffectList; struct CreatureLevList; struct ItemLevList; + struct TimeStamp; } namespace MWRender @@ -204,24 +205,14 @@ namespace MWBase virtual void advanceTime (double hours, bool incremental = false) = 0; ///< Advance in-game time. - virtual void setHour (double hour) = 0; - ///< Set in-game time hour. - - virtual void setMonth (int month) = 0; - ///< Set in-game time month. - - virtual void setDay (int day) = 0; - ///< Set in-game time day. - - virtual int getDay() const = 0; - virtual int getMonth() const = 0; - virtual int getYear() const = 0; - virtual std::string getMonthName (int month = -1) const = 0; ///< Return name of month (-1: current month) virtual MWWorld::TimeStamp getTimeStamp() const = 0; - ///< Return current in-game time stamp. + ///< Return current in-game time and number of day since new game start. + + virtual ESM::EpochTimeStamp getEpochTimeStamp() const = 0; + ///< Return current in-game date and time. virtual bool toggleSky() = 0; ///< \return Resulting mode diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 64f912298..18cc187c1 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -150,11 +150,10 @@ namespace MWGui if (hour >= 13) hour -= 12; if (hour == 0) hour = 12; - std::string dateTimeText = - MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getDay ()) + " " - + month + " (#{sDay} " + MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay()) - + ") " + MyGUI::utility::toString(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); - + ESM::EpochTimeStamp currentDate = MWBase::Environment::get().getWorld()->getEpochTimeStamp(); + int daysPassed = MWBase::Environment::get().getWorld()->getTimeStamp().getDay(); + std::string formattedHour = pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"; + std::string dateTimeText = Misc::StringUtils::format("%i %s (#{sDay} %i) %i %s", currentDate.mDay, month, daysPassed, hour, formattedHour); mDateTimeText->setCaptionWithReplacing (dateTimeText); } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index db83f72c1..9974b8f16 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -213,11 +213,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mPlayerClassId = classId; profile.mPlayerCell = world.getCellName(); - - profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); - profile.mInGameTime.mDay = world.getDay(); - profile.mInGameTime.mMonth = world.getMonth(); - profile.mInGameTime.mYear = world.getYear(); + profile.mInGameTime = world.getEpochTimeStamp(); profile.mTimePlayed = mTimePlayed; profile.mDescription = description; diff --git a/apps/openmw/mwworld/datetimemanager.cpp b/apps/openmw/mwworld/datetimemanager.cpp new file mode 100644 index 000000000..0894c974d --- /dev/null +++ b/apps/openmw/mwworld/datetimemanager.cpp @@ -0,0 +1,227 @@ +#include "datetimemanager.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "esmstore.hpp" +#include "globals.hpp" +#include "timestamp.hpp" + +namespace +{ + static int getDaysPerMonth(int month) + { + switch (month) + { + case 0: return 31; + case 1: return 28; + case 2: return 31; + case 3: return 30; + case 4: return 31; + case 5: return 30; + case 6: return 31; + case 7: return 31; + case 8: return 30; + case 9: return 31; + case 10: return 30; + case 11: return 31; + } + + throw std::runtime_error ("month out of range"); + } +} + +namespace MWWorld +{ + void DateTimeManager::setup(Globals& globalVariables) + { + mGameHour = globalVariables["gamehour"].getFloat(); + mDaysPassed = globalVariables["dayspassed"].getInteger(); + mDay = globalVariables["day"].getInteger(); + mMonth = globalVariables["month"].getInteger(); + mYear = globalVariables["year"].getInteger(); + mTimeScale = globalVariables["timescale"].getFloat(); + } + + void DateTimeManager::setHour(double hour) + { + if (hour < 0) + hour = 0; + + int days = static_cast(hour / 24); + hour = std::fmod(hour, 24); + mGameHour = static_cast(hour); + + if (days > 0) + setDay(days + mDay); + } + + void DateTimeManager::setDay(int day) + { + if (day < 1) + day = 1; + + int month = mMonth; + while (true) + { + int days = getDaysPerMonth(month); + if (day <= days) + break; + + if (month < 11) + { + ++month; + } + else + { + month = 0; + mYear++; + } + + day -= days; + } + + mDay = day; + mMonth = month; + } + + TimeStamp DateTimeManager::getTimeStamp() const + { + return TimeStamp(mGameHour, mDaysPassed); + } + + float DateTimeManager::getTimeScaleFactor() const + { + return mTimeScale; + } + + ESM::EpochTimeStamp DateTimeManager::getEpochTimeStamp() const + { + ESM::EpochTimeStamp timeStamp; + timeStamp.mGameHour = mGameHour; + timeStamp.mDay = mDay; + timeStamp.mMonth = mMonth; + timeStamp.mYear = mYear; + return timeStamp; + } + + void DateTimeManager::setMonth(int month) + { + if (month < 0) + month = 0; + + int years = month / 12; + month = month % 12; + + int days = getDaysPerMonth(month); + if (mDay > days) + mDay = days; + + mMonth = month; + + if (years > 0) + mYear += years; + } + + void DateTimeManager::advanceTime(double hours, Globals& globalVariables) + { + hours += mGameHour; + setHour(hours); + + int days = static_cast(hours / 24); + if (days > 0) + mDaysPassed += days; + + globalVariables["gamehour"].setFloat(mGameHour); + globalVariables["dayspassed"].setInteger(mDaysPassed); + globalVariables["day"].setInteger(mDay); + globalVariables["month"].setInteger(mMonth); + globalVariables["year"].setInteger(mYear); + } + + std::string DateTimeManager::getMonthName(int month) const + { + if (month == -1) + month = mMonth; + + const int months = 12; + if (month < 0 || month >= months) + return std::string(); + + static const char *monthNames[months] = + { + "sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand", + "sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed", + "sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar" + }; + + const ESM::GameSetting *setting = MWBase::Environment::get().getWorld()->getStore().get().find(monthNames[month]); + return setting->mValue.getString(); + } + + bool DateTimeManager::updateGlobalFloat(const std::string& name, float value) + { + if (name=="gamehour") + { + setHour(value); + return true; + } + else if (name=="day") + { + setDay(static_cast(value)); + return true; + } + else if (name=="month") + { + setMonth(static_cast(value)); + return true; + } + else if (name=="year") + { + mYear = static_cast(value); + } + else if (name=="timescale") + { + mTimeScale = value; + } + else if (name=="dayspassed") + { + mDaysPassed = static_cast(value); + } + + return false; + } + + bool DateTimeManager::updateGlobalInt(const std::string& name, int value) + { + if (name=="gamehour") + { + setHour(static_cast(value)); + return true; + } + else if (name=="day") + { + setDay(value); + return true; + } + else if (name=="month") + { + setMonth(value); + return true; + } + else if (name=="year") + { + mYear = value; + } + else if (name=="timescale") + { + mTimeScale = static_cast(value); + } + else if (name=="dayspassed") + { + mDaysPassed = value; + } + + return false; + } +} diff --git a/apps/openmw/mwworld/datetimemanager.hpp b/apps/openmw/mwworld/datetimemanager.hpp new file mode 100644 index 000000000..b460be746 --- /dev/null +++ b/apps/openmw/mwworld/datetimemanager.hpp @@ -0,0 +1,43 @@ +#ifndef GAME_MWWORLD_DATETIMEMANAGER_H +#define GAME_MWWORLD_DATETIMEMANAGER_H + +#include + +namespace ESM +{ + struct EpochTimeStamp; +} + +namespace MWWorld +{ + class Globals; + class TimeStamp; + + class DateTimeManager + { + int mDaysPassed = 0; + int mDay = 0; + int mMonth = 0; + int mYear = 0; + float mGameHour = 0.f; + float mTimeScale = 0.f; + + void setHour(double hour); + void setDay(int day); + void setMonth(int month); + + public: + std::string getMonthName(int month) const; + TimeStamp getTimeStamp() const; + ESM::EpochTimeStamp getEpochTimeStamp() const; + float getTimeScaleFactor() const; + + void advanceTime(double hours, Globals& globalVariables); + + void setup(Globals& globalVariables); + bool updateGlobalInt(const std::string& name, int value); + bool updateGlobalFloat(const std::string& name, float value); + }; +} + +#endif diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index 69ec5563b..8a481334e 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -2,10 +2,9 @@ #include -#include - #include #include +#include #include "esmstore.hpp" diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index aee98e7ea..c51266bab 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -60,6 +60,7 @@ #include "../mwphysics/object.hpp" #include "../mwphysics/constants.hpp" +#include "datetimemanager.hpp" #include "player.hpp" #include "manualref.hpp" #include "cellstore.hpp" @@ -121,33 +122,11 @@ namespace MWWorld LoadersContainer mLoaders; }; - int World::getDaysPerMonth (int month) const - { - switch (month) - { - case 0: return 31; - case 1: return 28; - case 2: return 31; - case 3: return 30; - case 4: return 31; - case 5: return 30; - case 6: return 31; - case 7: return 31; - case 8: return 30; - case 9: return 31; - case 10: return 30; - case 11: return 31; - } - - throw std::runtime_error ("month out of range"); - } - void World::adjustSky() { if (mSky && (isCellExterior() || isCellQuasiExterior())) { - mRendering->skySetDate (mDay->getInteger(), mMonth->getInteger()); - + updateSkyDate(); mRendering->setSkyEnabled(true); } else @@ -193,6 +172,8 @@ namespace MWWorld if (mEsm[0].getFormat() == 0) ensureNeededRecords(); + mCurrentDate.reset(new DateTimeManager()); + fillGlobalVariables(); mStore.setUp(true); @@ -227,13 +208,7 @@ namespace MWWorld void World::fillGlobalVariables() { mGlobalVariables.fill (mStore); - - mGameHour = &mGlobalVariables["gamehour"]; - mDaysPassed = &mGlobalVariables["dayspassed"]; - mDay = &mGlobalVariables["day"]; - mMonth = &mGlobalVariables["month"]; - mYear = &mGlobalVariables["year"]; - mTimeScale = &mGlobalVariables["timescale"]; + mCurrentDate->setup(mGlobalVariables); } void World::startNewGame (bool bypass) @@ -310,6 +285,7 @@ namespace MWWorld mPhysics->toggleCollisionMode(); MWBase::Environment::get().getWindowManager()->updatePlayer(); + mCurrentDate->setup(mGlobalVariables); } void World::clear() @@ -639,26 +615,20 @@ namespace MWWorld void World::setGlobalInt (const std::string& name, int value) { - if (name=="gamehour") - setHour (value); - else if (name=="day") - setDay (value); - else if (name=="month") - setMonth (value); - else - mGlobalVariables[name].setInteger (value); + bool dateUpdated = mCurrentDate->updateGlobalInt(name, value); + if (dateUpdated) + updateSkyDate(); + + mGlobalVariables[name].setInteger (value); } void World::setGlobalFloat (const std::string& name, float value) { - if (name=="gamehour") - setHour (value); - else if (name=="day") - setDay(static_cast(value)); - else if (name=="month") - setMonth(static_cast(value)); - else - mGlobalVariables[name].setFloat (value); + bool dateUpdated = mCurrentDate->updateGlobalFloat(name, value); + if (dateUpdated) + updateSkyDate(); + + mGlobalVariables[name].setFloat(value); } int World::getGlobalInt (const std::string& name) const @@ -676,6 +646,11 @@ namespace MWWorld return mGlobalVariables.getType (name); } + std::string World::getMonthName (int month) const + { + return mCurrentDate->getMonthName(month); + } + std::string World::getCellName (const MWWorld::CellStore *cell) const { if (!cell) @@ -894,130 +869,29 @@ namespace MWWorld } mWeatherManager->advanceTime (hours, incremental); + mCurrentDate->advanceTime(hours, mGlobalVariables); + updateSkyDate(); if (!incremental) { mRendering->notifyWorldSpaceChanged(); mProjectileManager->clear(); } - - hours += mGameHour->getFloat(); - - setHour (hours); - - int days = static_cast(hours / 24); - - if (days>0) - mDaysPassed->setInteger ( - days + mDaysPassed->getInteger()); - } - - void World::setHour (double hour) - { - if (hour<0) - hour = 0; - - int days = static_cast(hour / 24); - - hour = std::fmod (hour, 24); - - mGameHour->setFloat(static_cast(hour)); - - if (days>0) - setDay (days + mDay->getInteger()); - } - - void World::setDay (int day) - { - if (day<1) - day = 1; - - int month = mMonth->getInteger(); - - while (true) - { - int days = getDaysPerMonth (month); - if (day<=days) - break; - - if (month<11) - { - ++month; - } - else - { - month = 0; - mYear->setInteger(mYear->getInteger()+1); - } - - day -= days; - } - - mDay->setInteger(day); - mMonth->setInteger(month); - - mRendering->skySetDate(day, month); - } - - void World::setMonth (int month) - { - if (month<0) - month = 0; - - int years = month / 12; - month = month % 12; - - int days = getDaysPerMonth (month); - - if (mDay->getInteger()>days) - mDay->setInteger (days); - - mMonth->setInteger (month); - - if (years>0) - mYear->setInteger (years+mYear->getInteger()); - - mRendering->skySetDate (mDay->getInteger(), month); } - int World::getDay() const - { - return mDay->getInteger(); - } - - int World::getMonth() const - { - return mMonth->getInteger(); - } - - int World::getYear() const + float World::getTimeScaleFactor() const { - return mYear->getInteger(); + return mCurrentDate->getTimeScaleFactor(); } - std::string World::getMonthName (int month) const + TimeStamp World::getTimeStamp() const { - if (month==-1) - month = getMonth(); - - const int months = 12; - - if (month<0 || month>=months) - return ""; - - static const char *monthNames[months] = - { - "sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand", - "sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed", - "sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar" - }; - - return mStore.get().find (monthNames[month])->mValue.getString(); + return mCurrentDate->getTimeStamp(); } - TimeStamp World::getTimeStamp() const + ESM::EpochTimeStamp World::getEpochTimeStamp() const { - return TimeStamp (mGameHour->getFloat(), mDaysPassed->getInteger()); + return mCurrentDate->getEpochTimeStamp(); } bool World::toggleSky() @@ -1042,11 +916,6 @@ namespace MWWorld mRendering->skySetMoonColour (red); } - float World::getTimeScaleFactor() const - { - return mTimeScale->getFloat(); - } - void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) { mPhysics->clearQueuedMovement(); @@ -1089,6 +958,8 @@ namespace MWWorld changeToExteriorCell (position, adjustPlayerPos, changeEvent); else changeToInteriorCell (cellId.mWorldspace, position, adjustPlayerPos, changeEvent); + + mCurrentDate->setup(mGlobalVariables); } void World::markCellAsUnchanged() @@ -3968,4 +3839,10 @@ namespace MWWorld mNavigator->reportStats(frameNumber, stats); mPhysics->reportStats(frameNumber, stats); } + + void World::updateSkyDate() + { + ESM::EpochTimeStamp currentDate = mCurrentDate->getEpochTimeStamp(); + mRendering->skySetDate(currentDate.mDay, currentDate.mMonth); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index fa2f7778b..aeb6bbae4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -69,6 +69,7 @@ namespace MWPhysics namespace MWWorld { + class DateTimeManager; class WeatherManager; class Player; class ProjectileManager; @@ -85,13 +86,6 @@ namespace MWWorld LocalScripts mLocalScripts; MWWorld::Globals mGlobalVariables; - ESM::Variant* mGameHour; - ESM::Variant* mDaysPassed; - ESM::Variant* mDay; - ESM::Variant* mMonth; - ESM::Variant* mYear; - ESM::Variant* mTimeScale; - Cells mCells; std::string mCurrentWorldSpace; @@ -102,6 +96,7 @@ namespace MWWorld std::unique_ptr mRendering; std::unique_ptr mWorldScene; std::unique_ptr mWeatherManager; + std::unique_ptr mCurrentDate; std::shared_ptr mProjectileManager; bool mSky; @@ -139,7 +134,6 @@ namespace MWWorld World& operator= (const World&); void updateWeather(float duration, bool paused = false); - int getDaysPerMonth (int month) const; void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags); @@ -173,6 +167,8 @@ namespace MWWorld void fillGlobalVariables(); + void updateSkyDate(); + /** * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon) * @param fileCollections- Container which holds content file names and their paths @@ -318,24 +314,14 @@ namespace MWWorld void advanceTime (double hours, bool incremental = false) override; ///< Advance in-game time. - void setHour (double hour) override; - ///< Set in-game time hour. - - void setMonth (int month) override; - ///< Set in-game time month. - - void setDay (int day) override; - ///< Set in-game time day. - - int getDay() const override; - int getMonth() const override; - int getYear() const override; - std::string getMonthName (int month = -1) const override; ///< Return name of month (-1: current month) TimeStamp getTimeStamp() const override; - ///< Return current in-game time stamp. + ///< Return current in-game time and number of day since new game start. + + ESM::EpochTimeStamp getEpochTimeStamp() const override; + ///< Return current in-game date and time. bool toggleSky() override; ///< \return Resulting mode diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 0f0478faa..6c0c33526 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -14,6 +14,14 @@ struct TimeStamp int mDay; }; +struct EpochTimeStamp +{ + float mGameHour; + int mDay; + int mMonth; + int mYear; +}; + // Pixel color value. Standard four-byte rr,gg,bb,aa format. typedef uint32_t Color; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index cda411314..b5bf6e406 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -2,7 +2,6 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -#include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; int ESM::SavedGame::sCurrentFormat = 10; diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index aa0429657..26efae824 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -4,6 +4,8 @@ #include #include +#include "defs.hpp" + namespace ESM { class ESMReader; @@ -17,14 +19,6 @@ namespace ESM static int sCurrentFormat; - struct TimeStamp - { - float mGameHour; - int mDay; - int mMonth; - int mYear; - }; - std::vector mContentFiles; std::string mPlayerName; int mPlayerLevel; @@ -36,7 +30,7 @@ namespace ESM std::string mPlayerClassName; std::string mPlayerCell; - TimeStamp mInGameTime; + EpochTimeStamp mInGameTime; double mTimePlayed; std::string mDescription; std::vector mScreenshot; // raw jpg-encoded data From 5b34ef224bfe31e07c32c343f34e1f9a490f72dc Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 21:08:39 +0200 Subject: [PATCH 160/227] Replace AiPackage virtual methods by options --- apps/openmw/mwmechanics/aiactivate.cpp | 5 -- apps/openmw/mwmechanics/aiactivate.hpp | 3 +- apps/openmw/mwmechanics/aiavoiddoor.cpp | 10 ---- apps/openmw/mwmechanics/aiavoiddoor.hpp | 16 ++++--- apps/openmw/mwmechanics/aibreathe.cpp | 10 ---- apps/openmw/mwmechanics/aibreathe.hpp | 15 +++--- apps/openmw/mwmechanics/aicast.cpp | 10 ---- apps/openmw/mwmechanics/aicast.hpp | 14 ++++-- apps/openmw/mwmechanics/aicombat.cpp | 10 ---- apps/openmw/mwmechanics/aicombat.hpp | 14 ++++-- apps/openmw/mwmechanics/aiescort.cpp | 5 -- apps/openmw/mwmechanics/aiescort.hpp | 12 +++-- apps/openmw/mwmechanics/aiface.cpp | 10 ---- apps/openmw/mwmechanics/aiface.hpp | 16 ++++--- apps/openmw/mwmechanics/aifollow.cpp | 24 ++++------ apps/openmw/mwmechanics/aifollow.hpp | 16 ++++--- apps/openmw/mwmechanics/aipackage.cpp | 29 ++---------- apps/openmw/mwmechanics/aipackage.hpp | 55 +++++++++++++++++----- apps/openmw/mwmechanics/aipursue.cpp | 5 -- apps/openmw/mwmechanics/aipursue.hpp | 14 ++++-- apps/openmw/mwmechanics/aisequence.cpp | 8 +++- apps/openmw/mwmechanics/aitravel.cpp | 38 +++++++++++---- apps/openmw/mwmechanics/aitravel.hpp | 35 +++++++++++--- apps/openmw/mwmechanics/aiwander.cpp | 19 ++------ apps/openmw/mwmechanics/aiwander.hpp | 15 +++--- apps/openmw/mwmechanics/typedaipackage.hpp | 10 ++++ 26 files changed, 220 insertions(+), 198 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 6764eba23..b4ddf0c03 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -44,11 +44,6 @@ namespace MWMechanics return false; } - int AiActivate::getTypeId() const - { - return TypeIdActivate; - } - void AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const { std::unique_ptr activate(new ESM::AiSequence::AiActivate()); diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 5a96f4cdb..b263e74a6 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -29,7 +29,8 @@ namespace MWMechanics AiActivate(const ESM::AiSequence::AiActivate* activate); bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + + static constexpr TypeId getTypeId() { return TypeIdActivate; } void writeState(ESM::AiSequence::AiSequence& sequence) const final; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index 9cdb8d90b..d8517c5c9 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -72,16 +72,6 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont return false; } -int MWMechanics::AiAvoidDoor::getTypeId() const -{ - return TypeIdAvoidDoor; -} - -unsigned int MWMechanics::AiAvoidDoor::getPriority() const -{ - return 2; -} - bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const { return (actorPos - mLastPos).length2() < 10 * 10; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index fdbf7ebc7..72cde1026 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -24,12 +24,16 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; - - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr TypeId getTypeId() { return TypeIdAvoidDoor; } + + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 2; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } private: float mDuration; diff --git a/apps/openmw/mwmechanics/aibreathe.cpp b/apps/openmw/mwmechanics/aibreathe.cpp index 5cb81b3d8..15251e125 100644 --- a/apps/openmw/mwmechanics/aibreathe.cpp +++ b/apps/openmw/mwmechanics/aibreathe.cpp @@ -31,13 +31,3 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro return true; } - -int MWMechanics::AiBreathe::getTypeId() const -{ - return TypeIdBreathe; -} - -unsigned int MWMechanics::AiBreathe::getPriority() const -{ - return 2; -} diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp index 6e3bb912a..2a04ab2ad 100644 --- a/apps/openmw/mwmechanics/aibreathe.hpp +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -12,13 +12,16 @@ namespace MWMechanics public: bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdBreathe; } - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 2; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } }; } #endif - diff --git a/apps/openmw/mwmechanics/aicast.cpp b/apps/openmw/mwmechanics/aicast.cpp index cc4c03bf1..9ad7b4c56 100644 --- a/apps/openmw/mwmechanics/aicast.cpp +++ b/apps/openmw/mwmechanics/aicast.cpp @@ -89,13 +89,3 @@ MWWorld::Ptr MWMechanics::AiCast::getTarget() const return target; } - -int MWMechanics::AiCast::getTypeId() const -{ - return AiPackage::TypeIdCast; -} - -unsigned int MWMechanics::AiCast::getPriority() const -{ - return 3; -} diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index cdf7db2bf..22575c7bc 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -17,14 +17,18 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdCast; } MWWorld::Ptr getTarget() const final; - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 3; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } private: const std::string mTargetId; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 4a3c7aee6..883a8cc1c 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -406,16 +406,6 @@ namespace MWMechanics } } - int AiCombat::getTypeId() const - { - return TypeIdCombat; - } - - unsigned int AiCombat::getPriority() const - { - return 1; - } - MWWorld::Ptr AiCombat::getTarget() const { return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 2ef0298fc..ef8782ae1 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -104,18 +104,22 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdCombat; } - unsigned int getPriority() const final; + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 1; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } ///Returns target ID MWWorld::Ptr getTarget() const final; void writeState(ESM::AiSequence::AiSequence &sequence) const final; - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } - private: /// Returns true if combat should end bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController); diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 216547f58..5dc1e44db 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -95,11 +95,6 @@ namespace MWMechanics return false; } - int AiEscort::getTypeId() const - { - return TypeIdEscort; - } - void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const { std::unique_ptr escort(new ESM::AiSequence::AiEscort()); diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index c12de1fac..42558bf7c 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -32,11 +32,15 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdEscort; } - bool useVariableSpeed() const final { return true; } - - bool sideWithTarget() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mSideWithTarget = true; + return options; + } void writeState(ESM::AiSequence::AiSequence &sequence) const final; diff --git a/apps/openmw/mwmechanics/aiface.cpp b/apps/openmw/mwmechanics/aiface.cpp index 0bfd00c87..17b18babc 100644 --- a/apps/openmw/mwmechanics/aiface.cpp +++ b/apps/openmw/mwmechanics/aiface.cpp @@ -14,13 +14,3 @@ bool MWMechanics::AiFace::execute(const MWWorld::Ptr& actor, MWMechanics::Charac osg::Vec3f dir = osg::Vec3f(mTargetX, mTargetY, 0) - actor.getRefData().getPosition().asVec3(); return zTurn(actor, std::atan2(dir.x(), dir.y()), osg::DegreesToRadians(3.f)); } - -int MWMechanics::AiFace::getTypeId() const -{ - return AiPackage::TypeIdFace; -} - -unsigned int MWMechanics::AiFace::getPriority() const -{ - return 2; -} diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index 516dd18dc..3a9a482c0 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -12,12 +12,16 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; - - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr TypeId getTypeId() { return TypeIdFace; } + + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 2; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } private: const float mTargetX; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index eb5b595ab..a9e43b3c3 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -16,25 +16,24 @@ namespace MWMechanics { - int AiFollow::mFollowIndexCounter = 0; AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actorId; } AiFollow::AiFollow(const std::string &actorId, const std::string &cellId, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actorId; } AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actor.getCellRef().getRefId(); @@ -42,7 +41,7 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y, } AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actor.getCellRef().getRefId(); @@ -50,7 +49,8 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float d } AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded) -: mAlwaysFollow(true), mCommanded(commanded), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0) +: TypedAiPackage(makeDefaultOptions().withShouldCancelPreviousAi(!commanded)) +, mAlwaysFollow(true), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actor.getCellRef().getRefId(); @@ -58,7 +58,8 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded) } AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) - : mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded) + : TypedAiPackage(makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded)) + , mAlwaysFollow(follow->mAlwaysFollow) // mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration. // The exact value of mDuration only matters for repeating packages. // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. @@ -200,14 +201,9 @@ std::string AiFollow::getFollowedActor() return mTargetActorRefId; } -int AiFollow::getTypeId() const -{ - return TypeIdFollow; -} - bool AiFollow::isCommanded() const { - return mCommanded; + return !mOptions.mShouldCancelPreviousAi; } void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const @@ -221,7 +217,7 @@ void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const follow->mRemainingDuration = mRemainingDuration; follow->mCellId = mCellId; follow->mAlwaysFollow = mAlwaysFollow; - follow->mCommanded = mCommanded; + follow->mCommanded = isCommanded(); follow->mActive = mActive; ESM::AiSequence::AiPackageContainer package; diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 865f4171b..b4cf88be8 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -53,15 +53,18 @@ namespace MWMechanics AiFollow(const ESM::AiSequence::AiFollow* follow); - bool sideWithTarget() const final { return true; } - bool followTargetThroughDoors() const final { return true; } - bool shouldCancelPreviousAi() const final { return !mCommanded; } - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdFollow; } - bool useVariableSpeed() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mSideWithTarget = true; + options.mFollowTargetThroughDoors = true; + return options; + } /// Returns the actor being followed std::string getFollowedActor(); @@ -87,7 +90,6 @@ namespace MWMechanics /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ const bool mAlwaysFollow; - const bool mCommanded; const float mDuration; // Hours float mRemainingDuration; // Hours const float mX; diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index dca882a3b..66b41db54 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -24,7 +24,9 @@ #include -MWMechanics::AiPackage::AiPackage() : +MWMechanics::AiPackage::AiPackage(TypeId typeId, const Options& options) : + mTypeId(typeId), + mOptions(options), mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild mTargetActorRefId(""), mTargetActorId(-1), @@ -58,31 +60,6 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const return MWWorld::Ptr(); } -bool MWMechanics::AiPackage::sideWithTarget() const -{ - return false; -} - -bool MWMechanics::AiPackage::followTargetThroughDoors() const -{ - return false; -} - -bool MWMechanics::AiPackage::canCancel() const -{ - return true; -} - -bool MWMechanics::AiPackage::shouldCancelPreviousAi() const -{ - return true; -} - -bool MWMechanics::AiPackage::getRepeat() const -{ - return false; -} - void MWMechanics::AiPackage::reset() { // reset all members diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 873ad1c29..c32fb93aa 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -55,11 +55,39 @@ namespace MWMechanics TypeIdCast = 11 }; - ///Default constructor - AiPackage(); + struct Options + { + unsigned int mPriority = 0; + bool mUseVariableSpeed = false; + bool mSideWithTarget = false; + bool mFollowTargetThroughDoors = false; + bool mCanCancel = true; + bool mShouldCancelPreviousAi = true; + bool mRepeat = false; + bool mAlwaysActive = false; + + constexpr Options withRepeat(bool value) + { + mRepeat = value; + return *this; + } + + constexpr Options withShouldCancelPreviousAi(bool value) + { + mShouldCancelPreviousAi = value; + return *this; + } + }; + + AiPackage(TypeId typeId, const Options& options); virtual ~AiPackage() = default; + static constexpr Options makeDefaultOptions() + { + return Options{}; + } + ///Clones the package virtual std::unique_ptr clone() const = 0; @@ -69,13 +97,13 @@ namespace MWMechanics /// Returns the TypeID of the AiPackage /// \see enum TypeId - virtual int getTypeId() const = 0; + TypeId getTypeId() const { return mTypeId; } /// Higher number is higher priority (0 being the lowest) - virtual unsigned int getPriority() const {return 0;} + unsigned int getPriority() const { return mOptions.mPriority; } /// Check if package use movement with variable speed - virtual bool useVariableSpeed() const { return false;} + bool useVariableSpeed() const { return mOptions.mUseVariableSpeed; } virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {} @@ -89,24 +117,24 @@ namespace MWMechanics virtual osg::Vec3f getDestination(const MWWorld::Ptr& actor) const { return osg::Vec3f(0, 0, 0); }; /// Return true if having this AiPackage makes the actor side with the target in fights (default false) - virtual bool sideWithTarget() const; + bool sideWithTarget() const { return mOptions.mSideWithTarget; } /// Return true if the actor should follow the target through teleport doors (default false) - virtual bool followTargetThroughDoors() const; + bool followTargetThroughDoors() const { return mOptions.mFollowTargetThroughDoors; } /// Can this Ai package be canceled? (default true) - virtual bool canCancel() const; + bool canCancel() const { return mOptions.mCanCancel; } /// Upon adding this Ai package, should the Ai Sequence attempt to cancel previous Ai packages (default true)? - virtual bool shouldCancelPreviousAi() const; + bool shouldCancelPreviousAi() const { return mOptions.mShouldCancelPreviousAi; } /// Return true if this package should repeat. Currently only used for Wander packages. - virtual bool getRepeat() const; + bool getRepeat() const { return mOptions.mRepeat; } virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); } - // Return true if any loaded actor with this AI package must be active. - virtual bool alwaysActive() const { return false; } + /// Return true if any loaded actor with this AI package must be active. + bool alwaysActive() const { return mOptions.mAlwaysActive; } /// Reset pathfinding state void reset(); @@ -139,6 +167,9 @@ namespace MWMechanics DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; + const TypeId mTypeId; + const Options mOptions; + // TODO: all this does not belong here, move into temporary storage PathFinder mPathFinder; ObstacleCheck mObstacleCheck; diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 76b4ac0c9..7aa2a9554 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -66,11 +66,6 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte return false; } -int AiPursue::getTypeId() const -{ - return TypeIdPursue; -} - MWWorld::Ptr AiPursue::getTarget() const { return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 6898a8dd3..6031f84fb 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -27,14 +27,20 @@ namespace MWMechanics AiPursue(const ESM::AiSequence::AiPursue* pursue); bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + + static constexpr TypeId getTypeId() { return TypeIdPursue; } + + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } MWWorld::Ptr getTarget() const final; void writeState (ESM::AiSequence::AiSequence& sequence) const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } }; } #endif diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 9f5d4ccaf..4a23dc788 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -338,7 +338,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo dest = actor.getRefData().getPosition().asVec3(); } - MWMechanics::AiTravel travelPackage(dest.x(), dest.y(), dest.z(), true); + MWMechanics::AiInternalTravel travelPackage(dest.x(), dest.y(), dest.z()); stack(travelPackage, actor, false); } @@ -478,7 +478,11 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) } case ESM::AiSequence::Ai_Travel: { - package.reset(new AiTravel(static_cast(it->mPackage))); + const auto source = static_cast(it->mPackage); + if (source->mHidden) + package.reset(new AiInternalTravel(source)); + else + package.reset(new AiTravel(source)); break; } case ESM::AiSequence::Ai_Escort: diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index ea65f4670..b2a506ca6 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -27,14 +27,26 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2) namespace MWMechanics { - AiTravel::AiTravel(float x, float y, float z, bool hidden) - : mX(x),mY(y),mZ(z),mHidden(hidden) + AiTravel::AiTravel(float x, float y, float z, AiTravel*) + : mX(x), mY(y), mZ(z), mHidden(false) + { + } + + AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived) + : TypedAiPackage(derived), mX(x), mY(y), mZ(z), mHidden(true) + { + } + + AiTravel::AiTravel(float x, float y, float z) + : AiTravel(x, y, z, this) { } AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel) - : mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(travel->mHidden) + : mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(false) { + // Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type + assert(!travel->mHidden); } bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) @@ -78,11 +90,6 @@ namespace MWMechanics return false; } - int AiTravel::getTypeId() const - { - return mHidden ? TypeIdInternalTravel : TypeIdTravel; - } - void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state) { if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3())) @@ -107,5 +114,20 @@ namespace MWMechanics package.mPackage = travel.release(); sequence.mPackages.push_back(package); } + + AiInternalTravel::AiInternalTravel(float x, float y, float z) + : AiTravel(x, y, z, this) + { + } + + AiInternalTravel::AiInternalTravel(const ESM::AiSequence::AiTravel* travel) + : AiTravel(travel->mData.mX, travel->mData.mY, travel->mData.mZ, this) + { + } + + std::unique_ptr AiInternalTravel::clone() const + { + return std::make_unique(*this); + } } diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 4f785e237..3049801cd 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -13,12 +13,18 @@ namespace AiSequence namespace MWMechanics { + struct AiInternalTravel; + /// \brief Causes the AI to travel to the specified point - class AiTravel final : public TypedAiPackage + class AiTravel : public TypedAiPackage { public: - /// Default constructor - AiTravel(float x, float y, float z, bool hidden = false); + AiTravel(float x, float y, float z, AiTravel* derived); + + AiTravel(float x, float y, float z, AiInternalTravel* derived); + + AiTravel(float x, float y, float z); + AiTravel(const ESM::AiSequence::AiTravel* travel); /// Simulates the passing of time @@ -28,11 +34,15 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; - - bool useVariableSpeed() const final { return true; } + static constexpr TypeId getTypeId() { return TypeIdTravel; } - bool alwaysActive() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mAlwaysActive = true; + return options; + } osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } @@ -43,6 +53,17 @@ namespace MWMechanics const bool mHidden; }; + + struct AiInternalTravel final : public AiTravel + { + AiInternalTravel(float x, float y, float z); + + explicit AiInternalTravel(const ESM::AiSequence::AiTravel* travel); + + static constexpr TypeId getTypeId() { return TypeIdInternalTravel; } + + std::unique_ptr clone() const final; + }; } #endif diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 584131bbe..fd978717e 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -113,11 +113,12 @@ namespace MWMechanics } AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): + TypedAiPackage(makeDefaultOptions().withRepeat(repeat)), mDistance(std::max(0, distance)), mDuration(std::max(0, duration)), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(getInitialIdle(idle)), - mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), + mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false) { } @@ -309,11 +310,6 @@ namespace MWMechanics return false; // AiWander package not yet completed } - bool AiWander::getRepeat() const - { - return mRepeat; - } - osg::Vec3f AiWander::getDestination(const MWWorld::Ptr& actor) const { if (mHasDestination) @@ -599,11 +595,6 @@ namespace MWMechanics } } - int AiWander::getTypeId() const - { - return TypeIdWander; - } - void AiWander::stopWalking(const MWWorld::Ptr& actor) { mPathFinder.clearPath(); @@ -873,7 +864,7 @@ namespace MWMechanics assert (mIdle.size() == 8); for (int i=0; i<8; ++i) wander->mData.mIdle[i] = mIdle[i]; - wander->mData.mShouldRepeat = mRepeat; + wander->mData.mShouldRepeat = mOptions.mRepeat; wander->mStoredInitialActorPosition = mStoredInitialActorPosition; if (mStoredInitialActorPosition) wander->mInitialActorPosition = mInitialActorPosition; @@ -885,12 +876,12 @@ namespace MWMechanics } AiWander::AiWander (const ESM::AiSequence::AiWander* wander) - : mDistance(std::max(static_cast(0), wander->mData.mDistance)) + : TypedAiPackage(makeDefaultOptions().withRepeat(wander->mData.mShouldRepeat != 0)) + , mDistance(std::max(static_cast(0), wander->mData.mDistance)) , mDuration(std::max(static_cast(0), wander->mData.mDuration)) , mRemainingDuration(wander->mDurationData.mRemainingDuration) , mTimeOfDay(wander->mData.mTimeOfDay) , mIdle(getInitialIdle(wander->mData.mIdle)) - , mRepeat(wander->mData.mShouldRepeat != 0) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition) , mHasDestination(false) , mDestination(osg::Vec3f(0, 0, 0)) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 8eb735205..8171107d9 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -93,16 +93,20 @@ namespace MWMechanics bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdWander; } - bool useVariableSpeed() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mRepeat = false; + return options; + } void writeState(ESM::AiSequence::AiSequence &sequence) const final; void fastForward(const MWWorld::Ptr& actor, AiState& state) final; - bool getRepeat() const final; - osg::Vec3f getDestination(const MWWorld::Ptr& actor) const final; osg::Vec3f getDestination() const final @@ -139,7 +143,6 @@ namespace MWMechanics float mRemainingDuration; const int mTimeOfDay; const std::vector mIdle; - const bool mRepeat; bool mStoredInitialActorPosition; osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell @@ -174,7 +177,7 @@ namespace MWMechanics static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1]; static int OffsetToPreventOvercrowding(); - }; + }; } #endif diff --git a/apps/openmw/mwmechanics/typedaipackage.hpp b/apps/openmw/mwmechanics/typedaipackage.hpp index e2b5f8688..c959f4d68 100644 --- a/apps/openmw/mwmechanics/typedaipackage.hpp +++ b/apps/openmw/mwmechanics/typedaipackage.hpp @@ -8,6 +8,16 @@ namespace MWMechanics template struct TypedAiPackage : public AiPackage { + TypedAiPackage() : + AiPackage(T::getTypeId(), T::makeDefaultOptions()) {} + + TypedAiPackage(const Options& options) : + AiPackage(T::getTypeId(), options) {} + + template + TypedAiPackage(Derived*) : + AiPackage(Derived::getTypeId(), Derived::makeDefaultOptions()) {} + virtual std::unique_ptr clone() const override { return std::make_unique(*static_cast(this)); From 6de97e6bc2f0308a3a437d875b6eef01757c96a4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 6 Jun 2020 14:10:24 +0400 Subject: [PATCH 161/227] Remove redundant variables from RenderingManager --- apps/openmw/mwrender/renderingmanager.cpp | 12 ++++-------- apps/openmw/mwrender/renderingmanager.hpp | 2 -- components/terrain/world.hpp | 1 + 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 6b41854e0..3eb71db03 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -213,10 +213,8 @@ namespace MWRender , mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) , mDistantFog(false) - , mDistantTerrain(false) , mFieldOfViewOverridden(false) , mFieldOfViewOverride(0.f) - , mBorders(false) { resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); @@ -290,9 +288,7 @@ namespace MWRender DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog"); DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog"); DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); - mDistantFog = Settings::Manager::getBool("use distant fog", "Fog"); - mDistantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders"); const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders"); @@ -302,7 +298,7 @@ namespace MWRender mTerrainStorage = new TerrainStorage(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps); - if (mDistantTerrain) + if (Settings::Manager::getBool("distant terrain", "Terrain")) { const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain"); int compMapPower = Settings::Manager::getInt("composite map level", "Terrain"); @@ -558,9 +554,9 @@ namespace MWRender bool RenderingManager::toggleBorders() { - mBorders = !mBorders; - mTerrain->setBordersVisible(mBorders); - return mBorders; + bool borders = !mTerrain->getBordersVisible(); + mTerrain->setBordersVisible(borders); + return borders; } bool RenderingManager::toggleRenderMode(RenderMode mode) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 09cff26f1..30826fb38 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -299,12 +299,10 @@ namespace MWRender float mNearClip; float mViewDistance; bool mDistantFog : 1; - bool mDistantTerrain : 1; bool mFieldOfViewOverridden : 1; float mFieldOfViewOverride; float mFieldOfView; float mFirstPersonFieldOfView; - bool mBorders; void operator = (const RenderingManager&); RenderingManager(const RenderingManager&); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index a69d03ca9..618095a60 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -139,6 +139,7 @@ namespace Terrain virtual void enable(bool enabled) {} virtual void setBordersVisible(bool visible); + virtual bool getBordersVisible() { return mBorderVisible; } /// Create a View to use with preload feature. The caller is responsible for deleting the view. /// @note Thread safe. From 48758116d6f228dfab4ec82e9c26975e7bcefdb4 Mon Sep 17 00:00:00 2001 From: Fanael Linithien Date: Sat, 6 Jun 2020 14:24:33 +0200 Subject: [PATCH 162/227] Make sure the skill level up message box displays the value correctly Fixes regression introduced in 204d2acf2570073d9d9736493d79e9a1326d6136. --- apps/openmw/mwmechanics/npcstats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 1e9003a2f..0e4b3f44c 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -265,7 +265,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWindowManager()->playSound("skillraise"); std::string message = MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", ""); - message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), base); + message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), static_cast(base)); if (readBook) message = "#{sBookSkillMessage}\n" + message; From 1873da4c91189e45ca2b1023eae5e73acd40e38a Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 6 Jun 2020 15:45:48 +0300 Subject: [PATCH 163/227] Don't save to or read automove state from saved games (#5452) --- CHANGELOG.md | 1 + apps/essimporter/importercontext.hpp | 1 - apps/openmw/mwworld/player.cpp | 4 ---- components/esm/player.cpp | 8 +++----- components/esm/player.hpp | 3 +-- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c48660848..c381af951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Bug #5427: GetDistance unknown ID error is misleading Bug #5435: Enemies can't hurt the player when collision is off Bug #5441: Enemies can't push a player character when in critical strike stance + Bug #5452: Autowalk is being included in savegames Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5445: Handle NiLines diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index 1a91b7cea..179e00f08 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -63,7 +63,6 @@ namespace ESSImport , mHour(0.f) , mNextActorId(0) { - mPlayer.mAutoMove = 0; ESM::CellId playerCellId; playerCellId.mPaged = true; playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 11444c8eb..2b157f18a 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -356,8 +356,6 @@ namespace MWWorld else player.mHasMark = false; - player.mAutoMove = mAutoMove ? 1 : 0; - for (int i=0; i Date: Sat, 6 Jun 2020 19:09:18 +0300 Subject: [PATCH 164/227] Clean up magic bolts of actors that are gone (#5451) --- CHANGELOG.md | 1 + apps/openmw/mwworld/projectilemanager.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 298220699..a136994ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Bug #5427: GetDistance unknown ID error is misleading Bug #5435: Enemies can't hurt the player when collision is off Bug #5441: Enemies can't push a player character when in critical strike stance + Bug #5451: Magic projectiles don't disappear with the caster Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5445: Handle NiLines diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 38458fdb9..6ace82ea1 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -387,6 +387,18 @@ namespace MWWorld { for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) { + // If the actor caster is gone, the magic bolt needs to be removed from the scene during the next frame. + MWWorld::Ptr caster = it->getCaster(); + if (!caster.isEmpty() && caster.getClass().isActor()) + { + if (caster.getRefData().getCount() <= 0 || caster.getClass().getCreatureStats(caster).isDead()) + { + cleanupMagicBolt(*it); + it = mMagicBolts.erase(it); + continue; + } + } + osg::Quat orient = it->mNode->getAttitude(); static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get() .find("fTargetSpellMaxSpeed")->mValue.getFloat(); @@ -405,8 +417,6 @@ namespace MWWorld update(*it, duration); - MWWorld::Ptr caster = it->getCaster(); - // 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()) From e02b66cdf4b0ea50fd50203d153c5c88ef92c319 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 6 Jun 2020 20:04:09 +0300 Subject: [PATCH 165/227] Ignore bogus string arguments for Disable/Enable again --- components/compiler/extensions0.cpp | 4 ++-- components/compiler/lineparser.cpp | 10 +--------- components/compiler/lineparser.hpp | 3 +-- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 60b4a7451..3989ef0f4 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -247,8 +247,8 @@ namespace Compiler extensions.registerInstruction ("startscript", "c", opcodeStartScript, opcodeStartScriptExplicit); extensions.registerInstruction ("stopscript", "c", opcodeStopScript); extensions.registerFunction ("getsecondspassed", 'f', "", opcodeGetSecondsPassed); - extensions.registerInstruction ("enable", "", opcodeEnable, opcodeEnableExplicit); - extensions.registerInstruction ("disable", "", opcodeDisable, opcodeDisableExplicit); + extensions.registerInstruction ("enable", "x", opcodeEnable, opcodeEnableExplicit); + extensions.registerInstruction ("disable", "x", opcodeDisable, opcodeDisableExplicit); extensions.registerFunction ("getdisabled", 'l', "x", opcodeGetDisabled, opcodeGetDisabledExplicit); extensions.registerFunction ("xbox", 'l', "", opcodeXBox); extensions.registerFunction ("onactivate", 'l', "", opcodeOnActivate, opcodeOnActivateExplicit); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 326b5f9f6..829ad6f08 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -86,13 +86,6 @@ namespace Compiler bool LineParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { - if (mState==PotentialEndState) - { - getErrorHandler().warning ("Stray string argument", loc); - mState = EndState; - return true; - } - if (mState==SetState) { std::string name2 = Misc::StringUtils::lowerCase (name); @@ -445,8 +438,7 @@ namespace Compiler return true; } - if (code==Scanner::S_newline && - (mState==EndState || mState==BeginState || mState==PotentialEndState)) + if (code==Scanner::S_newline && (mState==EndState || mState==BeginState)) return false; if (code==Scanner::S_comma && mState==MessageState) diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp index 8f7f64bf2..cc32b9592 100644 --- a/components/compiler/lineparser.hpp +++ b/components/compiler/lineparser.hpp @@ -25,8 +25,7 @@ namespace Compiler SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState, SetMemberVarState, SetMemberVarState2, MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState, - EndState, PotentialEndState /* may have a stray string argument */, - PotentialExplicitState, ExplicitState, MemberState + EndState, PotentialExplicitState, ExplicitState, MemberState }; Locals& mLocals; From 5209f5ff6d2139b5d3d3a8af797bcdb5703741dd Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 6 Jun 2020 23:00:53 +0200 Subject: [PATCH 166/227] Mark all derived classes from ESM::ObjectsState and overriden functions as final --- components/esm/containerstate.hpp | 10 +++++----- components/esm/creaturelevliststate.hpp | 10 +++++----- components/esm/creaturestate.hpp | 10 +++++----- components/esm/doorstate.hpp | 10 +++++----- components/esm/npcstate.hpp | 10 +++++----- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/components/esm/containerstate.hpp b/components/esm/containerstate.hpp index 33818d4f1..b0c4da2d1 100644 --- a/components/esm/containerstate.hpp +++ b/components/esm/containerstate.hpp @@ -8,18 +8,18 @@ namespace ESM { // format 0, saved games only - struct ContainerState : public ObjectState + struct ContainerState final : public ObjectState { InventoryState mInventory; - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual ContainerState& asContainerState() + ContainerState& asContainerState() final { return *this; } - virtual const ContainerState& asContainerState() const + const ContainerState& asContainerState() const final { return *this; } diff --git a/components/esm/creaturelevliststate.hpp b/components/esm/creaturelevliststate.hpp index 4d0b726a0..ec0bc8667 100644 --- a/components/esm/creaturelevliststate.hpp +++ b/components/esm/creaturelevliststate.hpp @@ -7,19 +7,19 @@ namespace ESM { // format 0, saved games only - struct CreatureLevListState : public ObjectState + struct CreatureLevListState final : public ObjectState { int mSpawnActorId; bool mSpawn; - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual CreatureLevListState& asCreatureLevListState() + CreatureLevListState& asCreatureLevListState() final { return *this; } - virtual const CreatureLevListState& asCreatureLevListState() const + const CreatureLevListState& asCreatureLevListState() const final { return *this; } diff --git a/components/esm/creaturestate.hpp b/components/esm/creaturestate.hpp index ca0d2601a..3e9a44d56 100644 --- a/components/esm/creaturestate.hpp +++ b/components/esm/creaturestate.hpp @@ -9,7 +9,7 @@ namespace ESM { // format 0, saved games only - struct CreatureState : public ObjectState + struct CreatureState final : public ObjectState { InventoryState mInventory; CreatureStats mCreatureStats; @@ -17,14 +17,14 @@ namespace ESM /// Initialize to default state void blank(); - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual CreatureState& asCreatureState() + CreatureState& asCreatureState() final { return *this; } - virtual const CreatureState& asCreatureState() const + const CreatureState& asCreatureState() const final { return *this; } diff --git a/components/esm/doorstate.hpp b/components/esm/doorstate.hpp index 1251b9059..04ad110d6 100644 --- a/components/esm/doorstate.hpp +++ b/components/esm/doorstate.hpp @@ -7,18 +7,18 @@ namespace ESM { // format 0, saved games only - struct DoorState : public ObjectState + struct DoorState final : public ObjectState { int mDoorState = 0; - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual DoorState& asDoorState() + DoorState& asDoorState() final { return *this; } - virtual const DoorState& asDoorState() const + const DoorState& asDoorState() const final { return *this; } diff --git a/components/esm/npcstate.hpp b/components/esm/npcstate.hpp index 4ae026da6..6c0469050 100644 --- a/components/esm/npcstate.hpp +++ b/components/esm/npcstate.hpp @@ -10,7 +10,7 @@ namespace ESM { // format 0, saved games only - struct NpcState : public ObjectState + struct NpcState final : public ObjectState { InventoryState mInventory; NpcStats mNpcStats; @@ -19,14 +19,14 @@ namespace ESM /// Initialize to default state void blank(); - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual NpcState& asNpcState() + NpcState& asNpcState() final { return *this; } - virtual const NpcState& asNpcState() const + const NpcState& asNpcState() const final { return *this; } From 75e7a3e8b1cbe4d72859ca75b082a8f93e377667 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 7 Jun 2020 09:25:46 +0400 Subject: [PATCH 167/227] Do not store object position, if it is the same as in CellRef --- components/esm/defs.hpp | 20 ++++++++++++++++++++ components/esm/objectstate.cpp | 3 ++- components/esm/savedgame.cpp | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 6c0c33526..61b7b161b 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -57,6 +57,26 @@ struct Position }; #pragma pack(pop) +bool inline operator== (const Position& left, const Position& right) noexcept +{ + return left.pos[0] == right.pos[0] && + left.pos[1] == right.pos[1] && + left.pos[2] == right.pos[2] && + left.rot[0] == right.rot[0] && + left.rot[1] == right.rot[1] && + left.rot[2] == right.rot[2]; +} + +bool inline operator!= (const Position& left, const Position& right) noexcept +{ + return left.pos[0] != right.pos[0] || + left.pos[1] != right.pos[1] || + left.pos[2] != right.pos[2] || + left.rot[0] != right.rot[0] || + left.rot[1] != right.rot[1] || + left.rot[2] != right.rot[2]; +} + template struct FourCC { diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index a7a452c82..9709bf4ff 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -26,6 +26,7 @@ void ESM::ObjectState::load (ESMReader &esm) mCount = 1; esm.getHNOT (mCount, "COUN"); + mPosition = mRef.mPos; esm.getHNOT (mPosition, "POS_", 24); if (esm.isNextSub("LROT")) @@ -61,7 +62,7 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const if (mCount!=1) esm.writeHNT ("COUN", mCount); - if (!inInventory) + if (!inInventory && mPosition != mRef.mPos) esm.writeHNT ("POS_", mPosition, 24); if (mFlags != 0) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index d87607e44..76695dbe8 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -4,7 +4,7 @@ #include "esmwriter.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 11; +int ESM::SavedGame::sCurrentFormat = 12; void ESM::SavedGame::load (ESMReader &esm) { From 5d17a4c66e881d78ba4d3ebbd5149ebf14cc5cde Mon Sep 17 00:00:00 2001 From: foobar13372 Date: Sun, 7 Jun 2020 10:10:33 +0200 Subject: [PATCH 168/227] Update CHANGELOG.md Correctly name change (was probably a copy paste error) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 298220699..50103697e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -299,7 +299,7 @@ Feature #5147: Show spell magicka cost in spell buying window Feature #5170: Editor: Land shape editing, land selection Feature #5172: Editor: Delete instances/references with keypress in scene window - Feature #5193: Weapon sheathing + Feature #5193: Shields sheathing Feature #5201: Editor: Show tool outline in scene view, when using editmodes Feature #5219: Impelement TestCells console command Feature #5224: Handle NiKeyframeController for NiTriShape From 4afc332a0ccc07db0d737a8d57f966d4a2b826fd Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 7 Jun 2020 10:35:33 +0400 Subject: [PATCH 169/227] Add a FogManager --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/fogmanager.cpp | 104 ++++++++++++++++++++++ apps/openmw/mwrender/fogmanager.hpp | 39 ++++++++ apps/openmw/mwrender/renderingmanager.cpp | 86 +++--------------- apps/openmw/mwrender/renderingmanager.hpp | 14 +-- 5 files changed, 157 insertions(+), 88 deletions(-) create mode 100644 apps/openmw/mwrender/fogmanager.cpp create mode 100644 apps/openmw/mwrender/fogmanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a6b68df86..99bd1c2cd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -21,7 +21,7 @@ add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation - renderbin actoranimation landmanager navmesh actorspaths recastmesh + renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/fogmanager.cpp b/apps/openmw/mwrender/fogmanager.cpp new file mode 100644 index 000000000..837e6ad04 --- /dev/null +++ b/apps/openmw/mwrender/fogmanager.cpp @@ -0,0 +1,104 @@ +#include "fogmanager.hpp" + +#include + +#include +#include +#include +#include + +namespace +{ + float DLLandFogStart; + float DLLandFogEnd; + float DLUnderwaterFogStart; + float DLUnderwaterFogEnd; + float DLInteriorFogStart; + float DLInteriorFogEnd; +} + +namespace MWRender +{ + FogManager::FogManager() + : mLandFogStart(0.f) + , mLandFogEnd(std::numeric_limits::max()) + , mUnderwaterFogStart(0.f) + , mUnderwaterFogEnd(std::numeric_limits::max()) + , mFogColor(osg::Vec4f()) + , mDistantFog(Settings::Manager::getBool("use distant fog", "Fog")) + , mUnderwaterColor(Fallback::Map::getColour("Water_UnderwaterColor")) + , mUnderwaterWeight(Fallback::Map::getFloat("Water_UnderwaterColorWeight")) + , mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog")) + { + DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog"); + DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog"); + DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog"); + DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog"); + DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog"); + DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); + } + + void FogManager::configure(float viewDistance, const ESM::Cell *cell) + { + osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); + + if (mDistantFog) + { + float density = std::max(0.2f, cell->mAmbi.mFogDensity); + mLandFogStart = DLInteriorFogEnd * (1.0f - density) + DLInteriorFogStart*density; + mLandFogEnd = DLInteriorFogEnd; + mUnderwaterFogStart = DLUnderwaterFogStart; + mUnderwaterFogEnd = DLUnderwaterFogEnd; + mFogColor = color; + } + else + configure(viewDistance, cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color); + } + + void FogManager::configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color) + { + if (mDistantFog) + { + mLandFogStart = dlFactor * (DLLandFogStart - dlOffset * DLLandFogEnd); + mLandFogEnd = dlFactor * (1.0f - dlOffset) * DLLandFogEnd; + mUnderwaterFogStart = DLUnderwaterFogStart; + mUnderwaterFogEnd = DLUnderwaterFogEnd; + } + else + { + if (fogDepth == 0.0) + { + mLandFogStart = 0.0f; + mLandFogEnd = std::numeric_limits::max(); + } + else + { + mLandFogStart = viewDistance * (1 - fogDepth); + mLandFogEnd = viewDistance; + } + mUnderwaterFogStart = std::min(viewDistance, 6666.f) * (1 - underwaterFog); + mUnderwaterFogEnd = std::min(viewDistance, 6666.f); + } + mFogColor = color; + } + + float FogManager::getFogStart(bool isUnderwater) const + { + return isUnderwater ? mUnderwaterFogStart : mLandFogStart; + } + + float FogManager::getFogEnd(bool isUnderwater) const + { + return isUnderwater ? mUnderwaterFogEnd : mLandFogEnd; + } + + osg::Vec4f FogManager::getFogColor(bool isUnderwater) const + { + if (isUnderwater) + { + return mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight); + } + + return mFogColor; + } +} diff --git a/apps/openmw/mwrender/fogmanager.hpp b/apps/openmw/mwrender/fogmanager.hpp new file mode 100644 index 000000000..c3efd06ab --- /dev/null +++ b/apps/openmw/mwrender/fogmanager.hpp @@ -0,0 +1,39 @@ +#ifndef OPENMW_MWRENDER_FOGMANAGER_H +#define OPENMW_MWRENDER_FOGMANAGER_H + +#include + +namespace ESM +{ + struct Cell; +} + +namespace MWRender +{ + class FogManager + { + public: + FogManager(); + + void configure(float viewDistance, const ESM::Cell *cell); + void configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color); + + osg::Vec4f getFogColor(bool isUnderwater) const; + float getFogStart(bool isUnderwater) const; + float getFogEnd(bool isUnderwater) const; + + private: + float mLandFogStart; + float mLandFogEnd; + float mUnderwaterFogStart; + float mUnderwaterFogEnd; + osg::Vec4f mFogColor; + bool mDistantFog; + + osg::Vec4f mUnderwaterColor; + float mUnderwaterWeight; + float mUnderwaterIndoorFog; + }; +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3eb71db03..462f9fbb6 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -69,16 +69,7 @@ #include "navmesh.hpp" #include "actorspaths.hpp" #include "recastmesh.hpp" - -namespace -{ - float DLLandFogStart; - float DLLandFogEnd; - float DLUnderwaterFogStart; - float DLUnderwaterFogEnd; - float DLInteriorFogStart; - float DLInteriorFogEnd; -} +#include "fogmanager.hpp" namespace MWRender { @@ -204,15 +195,7 @@ namespace MWRender , mWorkQueue(workQueue) , mUnrefQueue(new SceneUtil::UnrefQueue) , mNavigator(navigator) - , mLandFogStart(0.f) - , mLandFogEnd(std::numeric_limits::max()) - , mUnderwaterFogStart(0.f) - , mUnderwaterFogEnd(std::numeric_limits::max()) - , mUnderwaterColor(Fallback::Map::getColour("Water_UnderwaterColor")) - , mUnderwaterWeight(Fallback::Map::getFloat("Water_UnderwaterColorWeight")) - , mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) - , mDistantFog(false) , mFieldOfViewOverridden(false) , mFieldOfViewOverride(0.f) { @@ -282,14 +265,6 @@ namespace MWRender mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem)); - DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog"); - DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog"); - DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog"); - DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog"); - DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog"); - DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); - mDistantFog = Settings::Manager::getBool("use distant fog", "Fog"); - const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders"); const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders"); const std::string specularMapPattern = Settings::Manager::getString("terrain specular map pattern", "Shaders"); @@ -345,8 +320,9 @@ namespace MWRender defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat); - mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); + mFog.reset(new FogManager()); + mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); mSky->setCamera(mViewer->getCamera()); mSky->setRainIntensityUniform(mWater->getRainIntensityUniform()); @@ -602,46 +578,12 @@ namespace MWRender void RenderingManager::configureFog(const ESM::Cell *cell) { - osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); - - if(mDistantFog) - { - float density = std::max(0.2f, cell->mAmbi.mFogDensity); - mLandFogStart = (DLInteriorFogEnd*(1.0f-density) + DLInteriorFogStart*density); - mLandFogEnd = DLInteriorFogEnd; - mUnderwaterFogStart = DLUnderwaterFogStart; - mUnderwaterFogEnd = DLUnderwaterFogEnd; - mFogColor = color; - } - else - configureFog(cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color); + mFog->configure(mViewDistance, cell); } void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color) { - if(mDistantFog) - { - mLandFogStart = dlFactor * (DLLandFogStart - dlOffset*DLLandFogEnd); - mLandFogEnd = dlFactor * (1.0f-dlOffset) * DLLandFogEnd; - mUnderwaterFogStart = DLUnderwaterFogStart; - mUnderwaterFogEnd = DLUnderwaterFogEnd; - } - else - { - if(fogDepth == 0.0) - { - mLandFogStart = 0.0f; - mLandFogEnd = std::numeric_limits::max(); - } - else - { - mLandFogStart = mViewDistance * (1 - fogDepth); - mLandFogEnd = mViewDistance; - } - mUnderwaterFogStart = std::min(mViewDistance, 6666.f) * (1 - underwaterFog); - mUnderwaterFogEnd = std::min(mViewDistance, 6666.f); - } - mFogColor = color; + mFog->configure(mViewDistance, fogDepth, underwaterFog, dlFactor, dlOffset, color); } SkyManager* RenderingManager::getSkyManager() @@ -670,19 +612,11 @@ namespace MWRender osg::Vec3f focal, cameraPos; mCamera->getPosition(focal, cameraPos); mCurrentCameraPos = cameraPos; - if (mWater->isUnderwater(cameraPos)) - { - setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight)); - mStateUpdater->setFogStart(mUnderwaterFogStart); - mStateUpdater->setFogEnd(mUnderwaterFogEnd); - } - else - { - setFogColor(mFogColor); - mStateUpdater->setFogStart(mLandFogStart); - mStateUpdater->setFogEnd(mLandFogEnd); - } + bool isUnderwater = mWater->isUnderwater(cameraPos); + mStateUpdater->setFogStart(mFog->getFogStart(isUnderwater)); + mStateUpdater->setFogEnd(mFog->getFogEnd(isUnderwater)); + setFogColor(mFog->getFogColor(isUnderwater)); } void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr) @@ -1331,7 +1265,7 @@ namespace MWRender else if (it->first == "Camera" && it->second == "viewing distance") { mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); - if(!mDistantFog) + if(!Settings::Manager::getBool("use distant fog", "Fog")) mStateUpdater->setFogEnd(mViewDistance); updateProjectionMatrix(); } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 30826fb38..13f09b359 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -73,6 +73,7 @@ namespace MWRender class StateUpdater; class EffectManager; + class FogManager; class SkyManager; class NpcAnimation; class Pathgrid; @@ -275,6 +276,7 @@ namespace MWRender std::unique_ptr mTerrain; TerrainStorage* mTerrainStorage; std::unique_ptr mSky; + std::unique_ptr mFog; std::unique_ptr mEffectManager; std::unique_ptr mShadowManager; osg::ref_ptr mPlayerAnimation; @@ -284,22 +286,12 @@ namespace MWRender osg::ref_ptr mStateUpdater; - float mLandFogStart; - float mLandFogEnd; - float mUnderwaterFogStart; - float mUnderwaterFogEnd; - osg::Vec4f mUnderwaterColor; - float mUnderwaterWeight; - float mUnderwaterIndoorFog; - osg::Vec4f mFogColor; - osg::Vec4f mAmbientColor; float mNightEyeFactor; float mNearClip; float mViewDistance; - bool mDistantFog : 1; - bool mFieldOfViewOverridden : 1; + bool mFieldOfViewOverridden; float mFieldOfViewOverride; float mFieldOfView; float mFirstPersonFieldOfView; From 3dd4023e8dbd74d6c0ed18acf2d74e9faab8a360 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 8 Jun 2020 11:29:38 +0400 Subject: [PATCH 170/227] Update active spells during rest --- apps/openmw/mwmechanics/actors.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6c88592f4..aa5aac80a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -2051,6 +2051,8 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { + iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); + if (iter->first.getClass().getCreatureStats(iter->first).isDead()) continue; From d997842f8d59bc56bdd51175897fea15269c4292 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 9 Jun 2020 10:07:37 +0400 Subject: [PATCH 171/227] Use FourCC to declare all ESM record names --- components/esm/defs.hpp | 90 ++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 61b7b161b..0f9cefab1 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -86,51 +86,51 @@ struct FourCC enum RecNameInts { // format 0 / legacy - REC_ACTI = 0x49544341, - REC_ALCH = 0x48434c41, - REC_APPA = 0x41505041, - REC_ARMO = 0x4f4d5241, - REC_BODY = 0x59444f42, - REC_BOOK = 0x4b4f4f42, - REC_BSGN = 0x4e475342, - REC_CELL = 0x4c4c4543, - REC_CLAS = 0x53414c43, - REC_CLOT = 0x544f4c43, - REC_CNTC = 0x43544e43, - REC_CONT = 0x544e4f43, - REC_CREA = 0x41455243, - REC_CREC = 0x43455243, - REC_DIAL = 0x4c414944, - REC_DOOR = 0x524f4f44, - REC_ENCH = 0x48434e45, - REC_FACT = 0x54434146, - REC_GLOB = 0x424f4c47, - REC_GMST = 0x54534d47, - REC_INFO = 0x4f464e49, - REC_INGR = 0x52474e49, - REC_LAND = 0x444e414c, - REC_LEVC = 0x4356454c, - REC_LEVI = 0x4956454c, - REC_LIGH = 0x4847494c, - REC_LOCK = 0x4b434f4c, - REC_LTEX = 0x5845544c, - REC_MGEF = 0x4645474d, - REC_MISC = 0x4353494d, - REC_NPC_ = 0x5f43504e, - REC_NPCC = 0x4343504e, - REC_PGRD = 0x44524750, - REC_PROB = 0x424f5250, - REC_RACE = 0x45434152, - REC_REGN = 0x4e474552, - REC_REPA = 0x41504552, - REC_SCPT = 0x54504353, - REC_SKIL = 0x4c494b53, - REC_SNDG = 0x47444e53, - REC_SOUN = 0x4e554f53, - REC_SPEL = 0x4c455053, - REC_SSCR = 0x52435353, - REC_STAT = 0x54415453, - REC_WEAP = 0x50414557, + REC_ACTI = FourCC<'A','C','T','I'>::value, + REC_ALCH = FourCC<'A','L','C','H'>::value, + REC_APPA = FourCC<'A','P','P','A'>::value, + REC_ARMO = FourCC<'A','R','M','O'>::value, + REC_BODY = FourCC<'B','O','D','Y'>::value, + REC_BOOK = FourCC<'B','O','O','K'>::value, + REC_BSGN = FourCC<'B','S','G','N'>::value, + REC_CELL = FourCC<'C','E','L','L'>::value, + REC_CLAS = FourCC<'C','L','A','S'>::value, + REC_CLOT = FourCC<'C','L','O','T'>::value, + REC_CNTC = FourCC<'C','N','T','C'>::value, + REC_CONT = FourCC<'C','O','N','T'>::value, + REC_CREA = FourCC<'C','R','E','A'>::value, + REC_CREC = FourCC<'C','R','E','C'>::value, + REC_DIAL = FourCC<'D','I','A','L'>::value, + REC_DOOR = FourCC<'D','O','O','R'>::value, + REC_ENCH = FourCC<'E','N','C','H'>::value, + REC_FACT = FourCC<'F','A','C','T'>::value, + REC_GLOB = FourCC<'G','L','O','B'>::value, + REC_GMST = FourCC<'G','M','S','T'>::value, + REC_INFO = FourCC<'I','N','F','O'>::value, + REC_INGR = FourCC<'I','N','G','R'>::value, + REC_LAND = FourCC<'L','A','N','D'>::value, + REC_LEVC = FourCC<'L','E','V','C'>::value, + REC_LEVI = FourCC<'L','E','V','I'>::value, + REC_LIGH = FourCC<'L','I','G','H'>::value, + REC_LOCK = FourCC<'L','O','C','K'>::value, + REC_LTEX = FourCC<'L','T','E','X'>::value, + REC_MGEF = FourCC<'M','G','E','F'>::value, + REC_MISC = FourCC<'M','I','S','C'>::value, + REC_NPC_ = FourCC<'N','P','C','_'>::value, + REC_NPCC = FourCC<'N','P','C','C'>::value, + REC_PGRD = FourCC<'P','G','R','D'>::value, + REC_PROB = FourCC<'P','R','O','B'>::value, + REC_RACE = FourCC<'R','A','C','E'>::value, + REC_REGN = FourCC<'R','E','G','N'>::value, + REC_REPA = FourCC<'R','E','P','A'>::value, + REC_SCPT = FourCC<'S','C','P','T'>::value, + REC_SKIL = FourCC<'S','K','I','L'>::value, + REC_SNDG = FourCC<'S','N','D','G'>::value, + REC_SOUN = FourCC<'S','O','U','N'>::value, + REC_SPEL = FourCC<'S','P','E','L'>::value, + REC_SSCR = FourCC<'S','S','C','R'>::value, + REC_STAT = FourCC<'S','T','A','T'>::value, + REC_WEAP = FourCC<'W','E','A','P'>::value, // format 0 - saved games REC_SAVE = FourCC<'S','A','V','E'>::value, From 1c50d258535de869989bd542ba534365b3fa6997 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 9 Jun 2020 15:27:38 +0300 Subject: [PATCH 172/227] Minor shader fixes Don't initialize uniform bool to false explicitly Attempt not to calculate specular lighting if the material specular color is black --- files/shaders/objects_fragment.glsl | 5 +++-- files/shaders/terrain_fragment.glsl | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 78cc8c1dd..6ce44cc82 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -49,7 +49,7 @@ uniform vec2 envMapLumaBias; uniform mat2 bumpMapMatrix; #endif -uniform bool simpleWater = false; +uniform bool simpleWater; varying float euclideanDepth; varying float linearDepth; @@ -181,7 +181,8 @@ void main() matSpec = passColor.xyz; #endif - gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; + if (matSpec != vec3(0.0)) + gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; #if @radialFog float depth = euclideanDepth; // For the less detailed mesh of simple water we need to recalculate depth on per-pixel basis diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index 477b1bf9e..68669dff9 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -90,7 +90,8 @@ void main() matSpec = passColor.xyz; #endif - gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing; + if (matSpec != vec3(0.0)) + gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing; #if @radialFog float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); From 91514aed5db64af4b40a29de6cd017b9b109e1e6 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 9 Jun 2020 21:56:54 +0300 Subject: [PATCH 173/227] Don't deliberately do redundant assignments --- files/shaders/lighting.glsl | 27 +++++++++++++++++---------- files/shaders/objects_fragment.glsl | 8 ++++++-- files/shaders/terrain_fragment.glsl | 4 +++- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/files/shaders/lighting.glsl b/files/shaders/lighting.glsl index e5587a448..1ed162eea 100644 --- a/files/shaders/lighting.glsl +++ b/files/shaders/lighting.glsl @@ -29,25 +29,27 @@ vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, float shadowing vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, out vec3 shadowDiffuse) #endif { - vec4 diffuse = gl_FrontMaterial.diffuse; - vec3 ambient = gl_FrontMaterial.ambient.xyz; - vec3 emission = gl_FrontMaterial.emission.xyz; + vec4 diffuse; + vec3 ambient; if (colorMode == ColorMode_AmbientAndDiffuse) { diffuse = vertexColor; ambient = vertexColor.xyz; } - else if (colorMode == ColorMode_Ambient) - { - ambient = vertexColor.xyz; - } else if (colorMode == ColorMode_Diffuse) { diffuse = vertexColor; + ambient = gl_FrontMaterial.ambient.xyz; } - else if (colorMode == ColorMode_Emission) + else if (colorMode == ColorMode_Ambient) { - emission = vertexColor.xyz; + diffuse = gl_FrontMaterial.diffuse; + ambient = vertexColor.xyz; + } + else + { + diffuse = gl_FrontMaterial.diffuse; + ambient = gl_FrontMaterial.ambient.xyz; } vec4 lightResult = vec4(0.0, 0.0, 0.0, diffuse.a); @@ -65,7 +67,12 @@ vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, out vec3 shadow lightResult.xyz += ambientLight + diffuseLight; } - lightResult.xyz += gl_LightModel.ambient.xyz * ambient + emission; + lightResult.xyz += gl_LightModel.ambient.xyz * ambient; + + if (colorMode == ColorMode_Emission) + lightResult.xyz += vertexColor.xyz; + else + lightResult.xyz += gl_FrontMaterial.emission.xyz; #if @clamp lightResult = clamp(lightResult, vec4(0.0), vec4(1.0)); diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 6ce44cc82..74a0cc80f 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -176,18 +176,22 @@ void main() vec3 matSpec = specTex.xyz; #else float shininess = gl_FrontMaterial.shininess; - vec3 matSpec = gl_FrontMaterial.specular.xyz; + vec3 matSpec; if (colorMode == ColorMode_Specular) matSpec = passColor.xyz; + else + matSpec = gl_FrontMaterial.specular.xyz; #endif if (matSpec != vec3(0.0)) gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; #if @radialFog - float depth = euclideanDepth; + float depth; // For the less detailed mesh of simple water we need to recalculate depth on per-pixel basis if (simpleWater) depth = length(passViewPos); + else + depth = euclideanDepth; float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); #else float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index 68669dff9..ffc19fac0 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -85,9 +85,11 @@ void main() vec3 matSpec = vec3(diffuseTex.a); #else float shininess = gl_FrontMaterial.shininess; - vec3 matSpec = gl_FrontMaterial.specular.xyz; + vec3 matSpec; if (colorMode == ColorMode_Specular) matSpec = passColor.xyz; + else + matSpec = gl_FrontMaterial.specular.xyz; #endif if (matSpec != vec3(0.0)) From aacb569acbaac22ee1be3022637de97efdbb015d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 10 Jun 2020 10:30:37 +0400 Subject: [PATCH 174/227] Use more C++11 in tools code --- apps/bsatool/bsatool.cpp | 23 +++++++---------- apps/esmtool/esmtool.cpp | 14 +++++----- apps/esmtool/labels.cpp | 2 +- apps/esmtool/record.cpp | 17 ++++++------ apps/essimporter/converter.cpp | 37 ++++++++++++--------------- apps/essimporter/converter.hpp | 12 ++++----- apps/essimporter/convertinventory.cpp | 13 +++++----- apps/essimporter/convertplayer.cpp | 15 +++++------ apps/essimporter/convertscri.cpp | 8 +++--- apps/essimporter/importer.cpp | 22 ++++++---------- apps/essimporter/importinventory.cpp | 2 -- 11 files changed, 72 insertions(+), 93 deletions(-) diff --git a/apps/bsatool/bsatool.cpp b/apps/bsatool/bsatool.cpp index 54e946cbc..ed86a3a42 100644 --- a/apps/bsatool/bsatool.cpp +++ b/apps/bsatool/bsatool.cpp @@ -183,19 +183,19 @@ int list(Bsa::BSAFile& bsa, Arguments& info) { // List all files const Bsa::BSAFile::FileList &files = bsa.getList(); - for(unsigned int i=0; iname; - - std::string extractPath (archivePath); + for (const auto &file : bsa.getList()) + { + std::string extractPath(file.name); replaceAll(extractPath, "\\", "/"); // Get the target path (the path the file will be extracted to) @@ -278,7 +273,7 @@ int extractAll(Bsa::BSAFile& bsa, Arguments& info) // Get a stream for the file to extract // (inefficient because getFile iter on the list again) - Files::IStreamPtr data = bsa.getFile(archivePath); + Files::IStreamPtr data = bsa.getFile(file.name); bfs::ofstream out(target, std::ios::binary); // Write the file to disk diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 59095c1df..7618bbeb8 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -352,12 +352,12 @@ int load(Arguments& info) std::cout << "Author: " << esm.getAuthor() << std::endl << "Description: " << esm.getDesc() << std::endl << "File format version: " << esm.getFVer() << std::endl; - std::vector m = esm.getGameFiles(); - if (!m.empty()) + std::vector masterData = esm.getGameFiles(); + if (!masterData.empty()) { std::cout << "Masters:" << std::endl; - for(unsigned int i=0;imType = type; @@ -728,10 +728,9 @@ void Record::print() << " (" << mData.mData.mAttribute[0] << ")" << std::endl; std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1]) << " (" << mData.mData.mAttribute[1] << ")" << std::endl; - for (int i = 0; i < 7; i++) - if (mData.mData.mSkills[i] != -1) - std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) - << " (" << mData.mData.mSkills[i] << ")" << std::endl; + for (int skill : mData.mData.mSkills) + if (skill != -1) + std::cout << " Skill: " << skillLabel(skill) << " (" << skill << ")" << std::endl; for (int i = 0; i != 10; i++) if (!mData.mRanks[i].empty()) { diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 900fbb05c..e0756602d 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -52,9 +52,7 @@ namespace // a dynamically created record e.g. player-enchanted weapon std::string index = indexedRefId.substr(indexedRefId.size()-8); - if(index.find_first_not_of("0123456789ABCDEF") == std::string::npos ) - return true; - return false; + return index.find_first_not_of("0123456789ABCDEF") == std::string::npos; } void splitIndexedRefId(const std::string& indexedRefId, int& refIndex, std::string& refId) @@ -139,12 +137,12 @@ namespace ESSImport image2->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE); memcpy(image2->data(), &data[0], data.size()); - for (std::set >::const_iterator it = mContext->mExploredCells.begin(); it != mContext->mExploredCells.end(); ++it) + for (const auto & exploredCell : mContext->mExploredCells) { - if (it->first > mContext->mGlobalMapState.mBounds.mMaxX - || it->first < mContext->mGlobalMapState.mBounds.mMinX - || it->second > mContext->mGlobalMapState.mBounds.mMaxY - || it->second < mContext->mGlobalMapState.mBounds.mMinY) + if (exploredCell.first > mContext->mGlobalMapState.mBounds.mMaxX + || exploredCell.first < mContext->mGlobalMapState.mBounds.mMinX + || exploredCell.second > mContext->mGlobalMapState.mBounds.mMaxY + || exploredCell.second < mContext->mGlobalMapState.mBounds.mMinY) { // out of bounds, I think this could happen, since the original engine had a fixed-size map continue; @@ -152,12 +150,12 @@ namespace ESSImport int imageLeftSrc = mGlobalMapImage->s()/2; int imageTopSrc = mGlobalMapImage->t()/2; - imageLeftSrc += it->first * cellSize; - imageTopSrc -= it->second * cellSize; + imageLeftSrc += exploredCell.first * cellSize; + imageTopSrc -= exploredCell.second * cellSize; int imageLeftDst = width/2; int imageTopDst = height/2; - imageLeftDst += it->first * cellSize; - imageTopDst -= it->second * cellSize; + imageLeftDst += exploredCell.first * cellSize; + imageTopDst -= exploredCell.second * cellSize; for (int x=0; x::const_iterator refIt = cell.mRefs.begin(); refIt != cell.mRefs.end(); ++refIt) + for (const auto & cellref : cell.mRefs) { - const CellRef& cellref = *refIt; ESM::CellRef out (cellref); // TODO: use mContext->mCreatures/mNpcs @@ -437,16 +434,16 @@ namespace ESSImport void ConvertCell::write(ESM::ESMWriter &esm) { - for (std::map::const_iterator it = mIntCells.begin(); it != mIntCells.end(); ++it) - writeCell(it->second, esm); + for (const auto & cell : mIntCells) + writeCell(cell.second, esm); - for (std::map, Cell>::const_iterator it = mExtCells.begin(); it != mExtCells.end(); ++it) - writeCell(it->second, esm); + for (const auto & cell : mExtCells) + writeCell(cell.second, esm); - for (std::vector::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it) + for (const auto & marker : mMarkers) { esm.startRecord(ESM::REC_MARK); - it->save(esm); + marker.save(esm); esm.endRecord(ESM::REC_MARK); } } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 2694ea570..30d0236aa 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -127,8 +127,8 @@ public: ESM::SpellState::SpellParams empty; // FIXME: player start spells and birthsign spells aren't listed here, // need to fix openmw to account for this - for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) - mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; + for (const auto & spell : npc.mSpells.mList) + mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[spell] = empty; // Clear the list now that we've written it, this prevents issues cropping up with // ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal. @@ -434,9 +434,9 @@ public: for (std::map >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it) { std::map, int> owners; - for (std::set::const_iterator ownerIt = it->second.begin(); ownerIt != it->second.end(); ++ownerIt) + for (const auto & ownerIt : it->second) { - owners.insert(std::make_pair(std::make_pair(ownerIt->first, ownerIt->second) + owners.insert(std::make_pair(std::make_pair(ownerIt.first, ownerIt.second) // Since OpenMW doesn't suffer from the owner contamination bug, // it needs a count argument. But for legacy savegames, we don't know // this count, so must assume all items of that ID are stolen, @@ -588,10 +588,10 @@ public: } virtual void write(ESM::ESMWriter &esm) { - for (std::vector::const_iterator it = mScripts.begin(); it != mScripts.end(); ++it) + for (const auto & script : mScripts) { esm.startRecord(ESM::REC_GSCR); - it->save(esm); + script.save(esm); esm.endRecord(ESM::REC_GSCR); } } diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp index 0799c8d11..79e09488c 100644 --- a/apps/essimporter/convertinventory.cpp +++ b/apps/essimporter/convertinventory.cpp @@ -9,21 +9,20 @@ namespace ESSImport void convertInventory(const Inventory &inventory, ESM::InventoryState &state) { int index = 0; - for (std::vector::const_iterator it = inventory.mItems.begin(); - it != inventory.mItems.end(); ++it) + for (const auto & item : inventory.mItems) { ESM::ObjectState objstate; objstate.blank(); - objstate.mRef = *it; - objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId); - objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile + objstate.mRef = item; + objstate.mRef.mRefID = Misc::StringUtils::lowerCase(item.mId); + objstate.mCount = std::abs(item.mCount); // restocking items have negative count in the savefile // openmw handles them differently, so no need to set any flags state.mItems.push_back(objstate); - if (it->mRelativeEquipmentSlot != -1) + if (item.mRelativeEquipmentSlot != -1) // Note we should really write the absolute slot here, which we do not know about // Not a big deal, OpenMW will auto-correct to a valid slot, the only problem is when // an item could be equipped in two different slots (e.g. equipped two rings) - state.mEquipmentSlots[index] = it->mRelativeEquipmentSlot; + state.mEquipmentSlots[index] = item.mRelativeEquipmentSlot; ++index; } } diff --git a/apps/essimporter/convertplayer.cpp b/apps/essimporter/convertplayer.cpp index 5e2da2b03..b3ccbca35 100644 --- a/apps/essimporter/convertplayer.cpp +++ b/apps/essimporter/convertplayer.cpp @@ -10,13 +10,13 @@ namespace ESSImport { out.mBirthsign = pcdt.mBirthsign; out.mObject.mNpcStats.mBounty = pcdt.mBounty; - for (std::vector::const_iterator it = pcdt.mFactions.begin(); it != pcdt.mFactions.end(); ++it) + for (const auto & essFaction : pcdt.mFactions) { ESM::NpcStats::Faction faction; - faction.mExpelled = (it->mFlags & 0x2) != 0; - faction.mRank = it->mRank; - faction.mReputation = it->mReputation; - out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction; + faction.mExpelled = (essFaction.mFlags & 0x2) != 0; + faction.mRank = essFaction.mRank; + faction.mReputation = essFaction.mReputation; + out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(essFaction.mFactionName.toString())] = faction; } for (int i=0; i<3; ++i) out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i]; @@ -35,10 +35,9 @@ namespace ESSImport teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled); levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled); - for (std::vector::const_iterator it = pcdt.mKnownDialogueTopics.begin(); - it != pcdt.mKnownDialogueTopics.end(); ++it) + for (const auto & knownDialogueTopic : pcdt.mKnownDialogueTopics) { - outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it)); + outDialogueTopics.push_back(Misc::StringUtils::lowerCase(knownDialogueTopic)); } controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled; diff --git a/apps/essimporter/convertscri.cpp b/apps/essimporter/convertscri.cpp index dbe35a010..eba48df77 100644 --- a/apps/essimporter/convertscri.cpp +++ b/apps/essimporter/convertscri.cpp @@ -1,18 +1,16 @@ #include "convertscri.hpp" -#include - namespace { template void storeVariables(const std::vector& variables, ESM::Locals& locals, const std::string& scriptname) { - for (typename std::vector::const_iterator it = variables.begin(); it != variables.end(); ++it) + for (const auto& variable : variables) { - ESM::Variant val(*it); + ESM::Variant val(variable); val.setType(VariantType); - locals.mVariables.push_back(std::make_pair(std::string(), val)); + locals.mVariables.emplace_back(std::string(), val); } } diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 36512579c..706512263 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -16,15 +16,12 @@ #include #include -#include #include #include #include #include #include -#include #include -#include #include @@ -49,7 +46,7 @@ namespace image->allocateImage(128, 128, 1, GL_RGB, GL_UNSIGNED_BYTE); // need to convert pixel format from BGRA to RGB as the jpg readerwriter doesn't support it otherwise - std::vector::const_iterator it = fileHeader.mSCRS.begin(); + auto it = fileHeader.mSCRS.begin(); for (int y=0; y<128; ++y) { for (int x=0; x<128; ++x) @@ -317,10 +314,9 @@ namespace ESSImport std::set unknownRecords; - for (std::map >::const_iterator it = converters.begin(); - it != converters.end(); ++it) + for (const auto & converter : converters) { - it->second->setContext(context); + converter.second->setContext(context); } while (esm.hasMoreRecs()) @@ -328,7 +324,7 @@ namespace ESSImport ESM::NAME n = esm.getRecName(); esm.getRecHeader(); - std::map >::iterator it = converters.find(n.intval); + auto it = converters.find(n.intval); if (it != converters.end()) { it->second->read(esm); @@ -358,17 +354,15 @@ namespace ESSImport writer.setDescription(""); writer.setRecordCount (0); - for (std::vector::const_iterator it = header.mMaster.begin(); - it != header.mMaster.end(); ++it) - writer.addMaster (it->name, 0); // not using the size information anyway -> use value of 0 + for (const auto & master : header.mMaster) + writer.addMaster(master.name, 0); // not using the size information anyway -> use value of 0 writer.save (stream); ESM::SavedGame profile; - for (std::vector::const_iterator it = header.mMaster.begin(); - it != header.mMaster.end(); ++it) + for (const auto & master : header.mMaster) { - profile.mContentFiles.push_back(it->name); + profile.mContentFiles.push_back(master.name); } profile.mDescription = esm.getDesc(); profile.mInGameTime.mDay = context.mDay; diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index cff114acb..dbf9ce0bd 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -4,8 +4,6 @@ #include -#include - namespace ESSImport { From 0d66369efbe5fe90e542571c88d8833d1eda4e5b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 10 Jun 2020 10:58:07 +0400 Subject: [PATCH 175/227] Use overrides, when needed --- apps/esmtool/record.hpp | 8 ++-- apps/essimporter/converter.hpp | 76 +++++++++++++++--------------- apps/essimporter/importacdt.hpp | 4 +- apps/essimporter/importcellref.hpp | 4 +- 4 files changed, 48 insertions(+), 44 deletions(-) diff --git a/apps/esmtool/record.hpp b/apps/esmtool/record.hpp index dca38409f..bbb3dd098 100644 --- a/apps/esmtool/record.hpp +++ b/apps/esmtool/record.hpp @@ -74,7 +74,7 @@ namespace EsmTool : mIsDeleted(false) {} - std::string getId() const { + std::string getId() const override { return mData.mId; } @@ -82,15 +82,15 @@ namespace EsmTool return mData; } - void save(ESM::ESMWriter &esm) { + void save(ESM::ESMWriter &esm) override { mData.save(esm, mIsDeleted); } - void load(ESM::ESMReader &esm) { + void load(ESM::ESMReader &esm) override { mData.load(esm, mIsDeleted); } - void print(); + void print() override; }; template<> std::string Record::getId() const; diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 30d0236aa..9a1923c2b 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -79,9 +79,9 @@ template class DefaultConverter : public Converter { public: - virtual int getStage() { return 0; } + int getStage() override { return 0; } - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { T record; bool isDeleted = false; @@ -90,7 +90,7 @@ public: mRecords[record.mId] = record; } - virtual void write(ESM::ESMWriter& esm) + void write(ESM::ESMWriter& esm) override { for (typename std::map::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it) { @@ -107,7 +107,7 @@ protected: class ConvertNPC : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { ESM::NPC npc; bool isDeleted = false; @@ -144,7 +144,7 @@ public: class ConvertCREA : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { // See comment in ConvertNPC ESM::Creature creature; @@ -162,7 +162,7 @@ public: class ConvertGlobal : public DefaultConverter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { ESM::Global global; bool isDeleted = false; @@ -183,7 +183,7 @@ public: class ConvertClass : public DefaultConverter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { ESM::Class class_; bool isDeleted = false; @@ -199,7 +199,7 @@ public: class ConvertBook : public DefaultConverter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { ESM::Book book; bool isDeleted = false; @@ -215,7 +215,7 @@ public: class ConvertNPCC : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { std::string id = esm.getHNString("NAME"); NPCC npcc; @@ -235,7 +235,7 @@ public: class ConvertREFR : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { REFR refr; refr.load(esm); @@ -261,7 +261,7 @@ public: } } } - virtual void write(ESM::ESMWriter& esm) + void write(ESM::ESMWriter& esm) override { esm.startRecord(ESM::REC_ASPL); esm.writeHNString("ID__", mSelectedSpell); @@ -280,14 +280,14 @@ public: mLevitationEnabled(true) {} - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { PCDT pcdt; pcdt.load(esm); convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState); } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { esm.startRecord(ESM::REC_ENAB); esm.writeHNT("TELE", mTeleportingEnabled); @@ -306,7 +306,7 @@ private: class ConvertCNTC : public Converter { - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { std::string id = esm.getHNString("NAME"); CNTC cntc; @@ -318,7 +318,7 @@ class ConvertCNTC : public Converter class ConvertCREC : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { std::string id = esm.getHNString("NAME"); CREC crec; @@ -330,8 +330,8 @@ public: class ConvertFMAP : public Converter { public: - virtual void read(ESM::ESMReader &esm); - virtual void write(ESM::ESMWriter &esm); + void read(ESM::ESMReader &esm) override; + void write(ESM::ESMWriter &esm) override; private: osg::ref_ptr mGlobalMapImage; @@ -340,8 +340,8 @@ private: class ConvertCell : public Converter { public: - virtual void read(ESM::ESMReader& esm); - virtual void write(ESM::ESMWriter& esm); + void read(ESM::ESMReader& esm) override; + void write(ESM::ESMWriter& esm) override; private: struct Cell @@ -362,7 +362,7 @@ private: class ConvertKLST : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { KLST klst; klst.load(esm); @@ -371,7 +371,7 @@ public: mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills; } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { esm.startRecord(ESM::REC_DCOU); for (std::map::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it) @@ -389,7 +389,7 @@ private: class ConvertFACT : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { ESM::Faction faction; bool isDeleted = false; @@ -409,7 +409,7 @@ public: class ConvertSTLN : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { std::string itemid = esm.getHNString("NAME"); Misc::StringUtils::lowerCaseInPlace(itemid); @@ -428,7 +428,7 @@ public: } } } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { ESM::StolenItems items; for (std::map >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it) @@ -467,7 +467,7 @@ private: class ConvertINFO : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { INFO info; info.load(esm); @@ -477,7 +477,7 @@ public: class ConvertDIAL : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { std::string id = esm.getHNString("NAME"); DIAL dial; @@ -485,7 +485,7 @@ public: if (dial.mIndex > 0) mDials[id] = dial; } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { for (std::map::const_iterator it = mDials.begin(); it != mDials.end(); ++it) { @@ -505,7 +505,7 @@ private: class ConvertQUES : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { std::string id = esm.getHNString("NAME"); QUES quest; @@ -516,7 +516,7 @@ public: class ConvertJOUR : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { JOUR journal; journal.load(esm); @@ -531,7 +531,7 @@ public: { } - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { mGame.load(esm); mHasGame = true; @@ -551,7 +551,7 @@ public: } } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { if (!mHasGame) return; @@ -578,7 +578,7 @@ private: class ConvertSCPT : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { SCPT script; script.load(esm); @@ -586,7 +586,7 @@ public: convertSCPT(script, out); mScripts.push_back(out); } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { for (const auto & script : mScripts) { @@ -603,9 +603,9 @@ private: class ConvertPROJ : public Converter { public: - virtual int getStage() override { return 2; } - virtual void read(ESM::ESMReader& esm) override; - virtual void write(ESM::ESMWriter& esm) override; + int getStage() override { return 2; } + void read(ESM::ESMReader& esm) override; + void write(ESM::ESMWriter& esm) override; private: void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam); PROJ mProj; @@ -614,8 +614,8 @@ private: class ConvertSPLM : public Converter { public: - virtual void read(ESM::ESMReader& esm) override; - virtual void write(ESM::ESMWriter& esm) override; + void read(ESM::ESMReader& esm) override; + void write(ESM::ESMWriter& esm) override; private: SPLM mSPLM; }; diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index bf48d1f78..354eca32d 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -86,7 +86,9 @@ namespace ESSImport bool mHasANIS; ANIS mANIS; // scripted animation state - void load(ESM::ESMReader& esm); + virtual void load(ESM::ESMReader& esm); + + virtual ~ActorData() = default; }; } diff --git a/apps/essimporter/importcellref.hpp b/apps/essimporter/importcellref.hpp index 556ed19bf..b115628d5 100644 --- a/apps/essimporter/importcellref.hpp +++ b/apps/essimporter/importcellref.hpp @@ -25,7 +25,9 @@ namespace ESSImport bool mDeleted; - void load(ESM::ESMReader& esm); + void load(ESM::ESMReader& esm) override; + + virtual ~CellRef() = default; }; } From 4a2fd1e14019b6817fd0737fa5278918c337bc77 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 11 Jun 2020 00:06:37 +0300 Subject: [PATCH 176/227] Revert the changes for "bug" #4551 --- apps/openmw/mwsound/soundmanagerimp.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index eff1cf0fd..162c176e6 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -147,10 +147,11 @@ namespace MWSound volume = static_cast(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0)); min = sound->mData.mMinRange; max = sound->mData.mMaxRange; - if (min == 0) + if (min == 0 && max == 0) + { min = fAudioDefaultMinDistance; - if (max == 0) max = fAudioDefaultMaxDistance; + } min *= fAudioMinDistanceMult; max *= fAudioMaxDistanceMult; From 095a45c714d568561f811b8b8305b48c77c71bd7 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 11 Jun 2020 21:43:51 +0200 Subject: [PATCH 177/227] Remove unused PathgridGraph::mIsExterior --- apps/openmw/mwmechanics/pathgrid.cpp | 2 -- apps/openmw/mwmechanics/pathgrid.hpp | 1 - 2 files changed, 3 deletions(-) diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index 7bcdc8926..ee1de3b5a 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -52,7 +52,6 @@ namespace MWMechanics PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell) : mCell(nullptr) , mPathgrid(nullptr) - , mIsExterior(0) , mGraph(0) , mIsGraphConstructed(false) , mSCCId(0) @@ -106,7 +105,6 @@ namespace MWMechanics return true; mCell = cell->getCell(); - mIsExterior = cell->getCell()->isExterior(); mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell->getCell()); if(!mPathgrid) return false; diff --git a/apps/openmw/mwmechanics/pathgrid.hpp b/apps/openmw/mwmechanics/pathgrid.hpp index 6b67bb507..050504617 100644 --- a/apps/openmw/mwmechanics/pathgrid.hpp +++ b/apps/openmw/mwmechanics/pathgrid.hpp @@ -44,7 +44,6 @@ namespace MWMechanics const ESM::Cell *mCell; const ESM::Pathgrid *mPathgrid; - bool mIsExterior; struct ConnectedPoint // edge { From c4cd3b2c4f488335355ae583e61ebdebd2a85c03 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 11 Jun 2020 23:23:30 +0200 Subject: [PATCH 178/227] Add pathgrid to navmesh as off mesh connection --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwmechanics/aicombat.cpp | 4 +- apps/openmw/mwmechanics/aipackage.cpp | 4 +- apps/openmw/mwmechanics/aiwander.cpp | 6 +- .../mwmechanics/coordinateconverter.cpp | 44 ------------ .../mwmechanics/coordinateconverter.hpp | 37 ---------- apps/openmw/mwmechanics/pathfinding.cpp | 8 ++- apps/openmw/mwrender/pathgrid.cpp | 4 +- apps/openmw/mwworld/scene.cpp | 9 ++- components/detournavigator/areatype.hpp | 2 + components/detournavigator/flags.hpp | 5 +- components/detournavigator/makenavmesh.cpp | 47 ++++++++++--- components/detournavigator/navigator.hpp | 10 +++ components/detournavigator/navigatorimpl.cpp | 29 +++++++- components/detournavigator/navigatorimpl.hpp | 7 ++ components/detournavigator/navigatorstub.hpp | 4 ++ components/detournavigator/navmeshmanager.cpp | 20 ++---- components/detournavigator/navmeshmanager.hpp | 4 +- .../detournavigator/offmeshconnection.hpp | 3 + .../offmeshconnectionsmanager.hpp | 47 ++++++------- components/misc/convert.hpp | 7 ++ components/misc/coordinateconverter.hpp | 68 +++++++++++++++++++ 22 files changed, 224 insertions(+), 147 deletions(-) delete mode 100644 apps/openmw/mwmechanics/coordinateconverter.cpp delete mode 100644 apps/openmw/mwmechanics/coordinateconverter.hpp create mode 100644 components/misc/coordinateconverter.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 99bd1c2cd..31ac64e9e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -86,7 +86,7 @@ add_openmw_dir (mwmechanics drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning - character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype spellutil tickableeffects + character actors objects aistate trading weaponpriority spellpriority weapontype spellutil tickableeffects spellabsorption linkedeffects ) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 883a8cc1c..a77539a1f 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -1,6 +1,7 @@ #include "aicombat.hpp" #include +#include #include @@ -21,7 +22,6 @@ #include "movement.hpp" #include "character.hpp" #include "aicombataction.hpp" -#include "coordinateconverter.hpp" #include "actorutil.hpp" namespace @@ -302,7 +302,7 @@ namespace MWMechanics if (pathgrid && !actor.getClass().isPureWaterCreature(actor)) { ESM::Pathgrid::PointList points; - CoordinateConverter coords(storage.mCell->getCell()); + Misc::CoordinateConverter coords(storage.mCell->getCell()); osg::Vec3f localPos = actor.getRefData().getPosition().asVec3(); coords.toLocal(localPos); diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 66b41db54..0c06697dc 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -20,7 +21,6 @@ #include "movement.hpp" #include "steering.hpp" #include "actorutil.hpp" -#include "coordinateconverter.hpp" #include @@ -341,7 +341,7 @@ bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position) if (playerCell->isExterior()) { // get actor's distance from origin of center cell - CoordinateConverter(playerCell).toLocal(position); + Misc::CoordinateConverter(playerCell).toLocal(position); // currently assumes 3 x 3 grid for exterior cells, with player at center cell. // ToDo: (Maybe) use "exterior cell load distance" setting to get count of actual active cells diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index fd978717e..015859c4b 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -21,7 +22,6 @@ #include "pathgrid.hpp" #include "creaturestats.hpp" #include "movement.hpp" -#include "coordinateconverter.hpp" #include "actorutil.hpp" namespace MWMechanics @@ -566,7 +566,7 @@ namespace MWMechanics void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell) { - CoordinateConverter(cell).toWorld(point); + Misc::CoordinateConverter(cell).toWorld(point); } void AiWander::trimAllowedNodes(std::vector& nodes, @@ -767,7 +767,7 @@ namespace MWMechanics { // get NPC's position in local (i.e. cell) coordinates osg::Vec3f npcPos(mInitialActorPosition); - CoordinateConverter(cell).toLocal(npcPos); + Misc::CoordinateConverter(cell).toLocal(npcPos); // Find closest pathgrid point int closestPointIndex = PathFinder::getClosestPoint(pathgrid, npcPos); diff --git a/apps/openmw/mwmechanics/coordinateconverter.cpp b/apps/openmw/mwmechanics/coordinateconverter.cpp deleted file mode 100644 index 04155ea49..000000000 --- a/apps/openmw/mwmechanics/coordinateconverter.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "coordinateconverter.hpp" - -#include -#include - -namespace MWMechanics -{ - CoordinateConverter::CoordinateConverter(const ESM::Cell* cell) - : mCellX(0), mCellY(0) - { - if (cell->isExterior()) - { - mCellX = cell->mData.mX * ESM::Land::REAL_SIZE; - mCellY = cell->mData.mY * ESM::Land::REAL_SIZE; - } - } - - void CoordinateConverter::toWorld(ESM::Pathgrid::Point& point) - { - point.mX += mCellX; - point.mY += mCellY; - } - - void CoordinateConverter::toWorld(osg::Vec3f& point) - { - point.x() += static_cast(mCellX); - point.y() += static_cast(mCellY); - } - - void CoordinateConverter::toLocal(osg::Vec3f& point) - { - point.x() -= static_cast(mCellX); - point.y() -= static_cast(mCellY); - } - - osg::Vec3f CoordinateConverter::toLocalVec3(const osg::Vec3f& point) - { - return osg::Vec3f( - point.x() - static_cast(mCellX), - point.y() - static_cast(mCellY), - point.z() - ); - } -} diff --git a/apps/openmw/mwmechanics/coordinateconverter.hpp b/apps/openmw/mwmechanics/coordinateconverter.hpp deleted file mode 100644 index f7dda33cb..000000000 --- a/apps/openmw/mwmechanics/coordinateconverter.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef GAME_MWMECHANICS_COORDINATECONVERTER_H -#define GAME_MWMECHANICS_COORDINATECONVERTER_H - -#include -#include - -namespace ESM -{ - struct Cell; -} - -namespace MWMechanics -{ - /// \brief convert coordinates between world and local cell - class CoordinateConverter - { - public: - CoordinateConverter(const ESM::Cell* cell); - - /// in-place conversion from local to world - void toWorld(ESM::Pathgrid::Point& point); - - /// in-place conversion from local to world - void toWorld(osg::Vec3f& point); - - /// in-place conversion from world to local - void toLocal(osg::Vec3f& point); - - osg::Vec3f toLocalVec3(const osg::Vec3f& point); - - private: - int mCellX; - int mCellY; - }; -} - -#endif diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index b072f55f8..824100c60 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -17,7 +18,6 @@ #include "../mwworld/class.hpp" #include "pathgrid.hpp" -#include "coordinateconverter.hpp" #include "actorutil.hpp" namespace @@ -164,7 +164,7 @@ namespace MWMechanics } // NOTE: getClosestPoint expects local coordinates - CoordinateConverter converter(mCell->getCell()); + Misc::CoordinateConverter converter(mCell->getCell()); // NOTE: It is possible that getClosestPoint returns a pathgrind point index // that is unreachable in some situations. e.g. actor is standing @@ -331,6 +331,10 @@ namespace MWMechanics if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor)) buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); + if (mPath.empty()) + buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, + flags | DetourNavigator::Flag_usePathgrid, std::back_inserter(mPath)); + if (mPath.empty()) buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath)); diff --git a/apps/openmw/mwrender/pathgrid.cpp b/apps/openmw/mwrender/pathgrid.cpp index 797794457..c20e81bb2 100644 --- a/apps/openmw/mwrender/pathgrid.cpp +++ b/apps/openmw/mwrender/pathgrid.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/pathfinding.hpp" -#include "../mwmechanics/coordinateconverter.hpp" #include "vismask.hpp" @@ -105,7 +105,7 @@ void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store) if (!pathgrid) return; osg::Vec3f cellPathGridPos(0, 0, 0); - MWMechanics::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos); + Misc::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos); osg::ref_ptr cellPathGrid = new osg::PositionAttitudeTransform; cellPathGrid->setPosition(cellPathGridPos); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index a366f9a75..1e1d39bec 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -365,6 +365,9 @@ namespace MWWorld if ((*iter)->getCell()->hasWater()) navigator->removeWater(osg::Vec2i(cellX, cellY)); + if (const auto pathgrid = world->getStore().get().search(*(*iter)->getCell())) + navigator->removePathgrid(*pathgrid); + const auto player = world->getPlayerPtr(); navigator->update(player.getRefData().getPosition().asVec3()); @@ -393,7 +396,8 @@ namespace MWWorld float verts = ESM::Land::LAND_SIZE; float worldsize = ESM::Land::REAL_SIZE; - const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); + const auto world = MWBase::Environment::get().getWorld(); + const auto navigator = world->getNavigator(); const int cellX = cell->getCell()->getGridX(); const int cellY = cell->getCell()->getGridY(); @@ -419,6 +423,9 @@ namespace MWWorld heightField->getCollisionObject()->getWorldTransform()); } + if (const auto pathgrid = world->getStore().get().search(*cell->getCell())) + navigator->addPathgrid(*cell->getCell(), *pathgrid); + // register local scripts // do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell); diff --git a/components/detournavigator/areatype.hpp b/components/detournavigator/areatype.hpp index 0daa524ca..962a67847 100644 --- a/components/detournavigator/areatype.hpp +++ b/components/detournavigator/areatype.hpp @@ -9,6 +9,8 @@ namespace DetourNavigator { AreaType_null = RC_NULL_AREA, AreaType_water, + AreaType_door, + AreaType_pathgrid, AreaType_ground = RC_WALKABLE_AREA, }; } diff --git a/components/detournavigator/flags.hpp b/components/detournavigator/flags.hpp index 684d4fbba..887fd4264 100644 --- a/components/detournavigator/flags.hpp +++ b/components/detournavigator/flags.hpp @@ -13,6 +13,7 @@ namespace DetourNavigator Flag_walk = 1 << 0, Flag_swim = 1 << 1, Flag_openDoor = 1 << 2, + Flag_usePathgrid = 1 << 3, }; inline std::ostream& operator <<(std::ostream& stream, const Flag value) @@ -27,6 +28,8 @@ namespace DetourNavigator return stream << "swim"; case Flag_openDoor: return stream << "openDoor"; + case Flag_usePathgrid: + return stream << "usePathgrid"; } return stream; @@ -45,7 +48,7 @@ namespace DetourNavigator else { bool first = true; - for (const auto flag : {Flag_walk, Flag_swim, Flag_openDoor}) + for (const auto flag : {Flag_walk, Flag_swim, Flag_openDoor, Flag_usePathgrid}) { if (value.mValue & flag) { diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index ae26ced7e..beee95113 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -98,6 +98,42 @@ namespace return result; } + Flag getFlag(AreaType areaType) + { + switch (areaType) + { + case AreaType_null: + return Flag_none; + case AreaType_ground: + return Flag_walk; + case AreaType_water: + return Flag_swim; + case AreaType_door: + return Flag_openDoor; + case AreaType_pathgrid: + return Flag_usePathgrid; + } + return Flag_none; + } + + std::vector getOffMeshConAreas(const std::vector& connections) + { + std::vector result; + result.reserve(connections.size()); + std::transform(connections.begin(), connections.end(), std::back_inserter(result), + [] (const OffMeshConnection& v) { return v.mAreaType; }); + return result; + } + + std::vector getOffMeshFlags(const std::vector& connections) + { + std::vector result; + result.reserve(connections.size()); + std::transform(connections.begin(), connections.end(), std::back_inserter(result), + [] (const OffMeshConnection& v) { return getFlag(v.mAreaType); }); + return result; + } + rcConfig makeConfig(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, const Settings& settings) { @@ -334,12 +370,7 @@ namespace void setPolyMeshFlags(rcPolyMesh& polyMesh) { for (int i = 0; i < polyMesh.npolys; ++i) - { - if (polyMesh.areas[i] == AreaType_ground) - polyMesh.flags[i] = Flag_walk; - else if (polyMesh.areas[i] == AreaType_water) - polyMesh.flags[i] = Flag_swim; - } + polyMesh.flags[i] = getFlag(static_cast(polyMesh.areas[i])); } bool fillPolyMesh(rcContext& context, const rcConfig& config, rcHeightfield& solid, rcPolyMesh& polyMesh, @@ -395,8 +426,8 @@ namespace const auto offMeshConVerts = getOffMeshVerts(offMeshConnections); const std::vector offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents)); const std::vector offMeshConDir(offMeshConnections.size(), DT_OFFMESH_CON_BIDIR); - const std::vector offMeshConAreas(offMeshConnections.size(), AreaType_ground); - const std::vector offMeshConFlags(offMeshConnections.size(), Flag_openDoor); + const std::vector offMeshConAreas = getOffMeshConAreas(offMeshConnections); + const std::vector offMeshConFlags = getOffMeshFlags(offMeshConnections); dtNavMeshCreateParams params; params.verts = polyMesh.verts; diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 99f1e258d..cfdf92232 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -9,6 +9,12 @@ #include "recastmesh.hpp" #include "recastmeshtiles.hpp" +namespace ESM +{ + struct Cell; + struct Pathgrid; +} + namespace DetourNavigator { struct ObjectShapes @@ -139,6 +145,10 @@ namespace DetourNavigator */ virtual bool removeWater(const osg::Vec2i& cellPosition) = 0; + virtual void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) = 0; + + virtual void removePathgrid(const ESM::Pathgrid& pathgrid) = 0; + /** * @brief update start background navmesh update using current scene state. * @param playerPosition setup initial point to order build tiles of navmesh. diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 3ecfd8b51..d7889629e 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -2,6 +2,9 @@ #include "debug.hpp" #include "settingsutils.hpp" +#include +#include + #include namespace DetourNavigator @@ -54,7 +57,8 @@ namespace DetourNavigator mNavMeshManager.addOffMeshConnection( id, toNavMeshCoordinates(mSettings, shapes.mConnectionStart), - toNavMeshCoordinates(mSettings, shapes.mConnectionEnd) + toNavMeshCoordinates(mSettings, shapes.mConnectionEnd), + AreaType_door ); return true; } @@ -95,7 +99,7 @@ namespace DetourNavigator const auto water = mWaterIds.find(id); if (water != mWaterIds.end()) result = mNavMeshManager.removeObject(water->second) || result; - mNavMeshManager.removeOffMeshConnection(id); + mNavMeshManager.removeOffMeshConnections(id); return result; } @@ -111,6 +115,27 @@ namespace DetourNavigator return mNavMeshManager.removeWater(cellPosition); } + void NavigatorImpl::addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) + { + Misc::CoordinateConverter converter(&cell); + for (auto edge : pathgrid.mEdges) + { + const auto src = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV0])); + const auto dst = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV1])); + mNavMeshManager.addOffMeshConnection( + ObjectId(&pathgrid), + toNavMeshCoordinates(mSettings, src), + toNavMeshCoordinates(mSettings, dst), + AreaType_pathgrid + ); + } + } + + void NavigatorImpl::removePathgrid(const ESM::Pathgrid& pathgrid) + { + mNavMeshManager.removeOffMeshConnections(ObjectId(&pathgrid)); + } + void NavigatorImpl::update(const osg::Vec3f& playerPosition) { removeUnusedNavMeshes(); diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index be291f501..66a4d8bb3 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -4,6 +4,8 @@ #include "navigator.hpp" #include "navmeshmanager.hpp" +#include + namespace DetourNavigator { class NavigatorImpl final : public Navigator @@ -38,6 +40,10 @@ namespace DetourNavigator bool removeWater(const osg::Vec2i& cellPosition) override; + void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) final; + + void removePathgrid(const ESM::Pathgrid& pathgrid) final; + void update(const osg::Vec3f& playerPosition) override; void wait() override; @@ -58,6 +64,7 @@ namespace DetourNavigator std::map mAgents; std::unordered_map mAvoidIds; std::unordered_map mWaterIds; + std::multimap mOffMeshConnectionIds; void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId); void updateWaterShapeId(const ObjectId id, const ObjectId waterId); diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index ee23e67be..9c379338f 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -60,6 +60,10 @@ namespace DetourNavigator return false; } + void addPathgrid(const ESM::Cell& /*cell*/, const ESM::Pathgrid& /*pathgrid*/) final {} + + void removePathgrid(const ESM::Pathgrid& /*pathgrid*/) final {} + void update(const osg::Vec3f& /*playerPosition*/) override {} void wait() override {} diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index d88d9706a..43d330648 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -110,10 +110,9 @@ namespace DetourNavigator return true; } - void NavMeshManager::addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end) + void NavMeshManager::addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end, const AreaType areaType) { - if (!mOffMeshConnectionsManager.add(id, OffMeshConnection {start, end})) - return; + mOffMeshConnectionsManager.add(id, OffMeshConnection {start, end, areaType}); const auto startTilePosition = getTilePosition(mSettings, start); const auto endTilePosition = getTilePosition(mSettings, end); @@ -124,18 +123,11 @@ namespace DetourNavigator addChangedTile(endTilePosition, ChangeType::add); } - void NavMeshManager::removeOffMeshConnection(const ObjectId id) + void NavMeshManager::removeOffMeshConnections(const ObjectId id) { - if (const auto connection = mOffMeshConnectionsManager.remove(id)) - { - const auto startTilePosition = getTilePosition(mSettings, connection->mStart); - const auto endTilePosition = getTilePosition(mSettings, connection->mEnd); - - addChangedTile(startTilePosition, ChangeType::remove); - - if (startTilePosition != endTilePosition) - addChangedTile(endTilePosition, ChangeType::remove); - } + const auto changedTiles = mOffMeshConnectionsManager.remove(id); + for (const auto& tile : changedTiles) + addChangedTile(tile, ChangeType::update); } void NavMeshManager::update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents) diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index a6bdca09b..f3861f8f2 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -39,9 +39,9 @@ namespace DetourNavigator bool reset(const osg::Vec3f& agentHalfExtents); - void addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end); + void addOffMeshConnection(const ObjectId id, const osg::Vec3f& start, const osg::Vec3f& end, const AreaType areaType); - void removeOffMeshConnection(const ObjectId id); + void removeOffMeshConnections(const ObjectId id); void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents); diff --git a/components/detournavigator/offmeshconnection.hpp b/components/detournavigator/offmeshconnection.hpp index 60e8ecbbb..ca999dbdb 100644 --- a/components/detournavigator/offmeshconnection.hpp +++ b/components/detournavigator/offmeshconnection.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_OFFMESHCONNECTION_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_OFFMESHCONNECTION_H +#include "areatype.hpp" + #include namespace DetourNavigator @@ -9,6 +11,7 @@ namespace DetourNavigator { osg::Vec3f mStart; osg::Vec3f mEnd; + AreaType mAreaType; }; } diff --git a/components/detournavigator/offmeshconnectionsmanager.hpp b/components/detournavigator/offmeshconnectionsmanager.hpp index 155ce3296..de707f3a8 100644 --- a/components/detournavigator/offmeshconnectionsmanager.hpp +++ b/components/detournavigator/offmeshconnectionsmanager.hpp @@ -11,14 +11,11 @@ #include -#include - #include #include -#include -#include #include #include +#include namespace DetourNavigator { @@ -29,12 +26,11 @@ namespace DetourNavigator : mSettings(settings) {} - bool add(const ObjectId id, const OffMeshConnection& value) + void add(const ObjectId id, const OffMeshConnection& value) { const auto values = mValues.lock(); - if (!values->mById.insert(std::make_pair(id, value)).second) - return false; + values->mById.insert(std::make_pair(id, value)); const auto startTilePosition = getTilePosition(mSettings, value.mStart); const auto endTilePosition = getTilePosition(mSettings, value.mEnd); @@ -43,32 +39,32 @@ namespace DetourNavigator if (startTilePosition != endTilePosition) values->mByTilePosition[endTilePosition].insert(id); - - return true; } - boost::optional remove(const ObjectId id) + std::set remove(const ObjectId id) { const auto values = mValues.lock(); - const auto itById = values->mById.find(id); + const auto byId = values->mById.equal_range(id); - if (itById == values->mById.end()) - return boost::none; + if (byId.first == byId.second) { + return {}; + } - const auto result = itById->second; + std::set removed; - values->mById.erase(itById); + std::for_each(byId.first, byId.second, [&] (const auto& v) { + const auto startTilePosition = getTilePosition(mSettings, v.second.mStart); + const auto endTilePosition = getTilePosition(mSettings, v.second.mEnd); - const auto startTilePosition = getTilePosition(mSettings, result.mStart); - const auto endTilePosition = getTilePosition(mSettings, result.mEnd); + removed.emplace(startTilePosition); + if (startTilePosition != endTilePosition) + removed.emplace(endTilePosition); + }); - removeByTilePosition(values->mByTilePosition, startTilePosition, id); + values->mById.erase(byId.first, byId.second); - if (startTilePosition != endTilePosition) - removeByTilePosition(values->mByTilePosition, endTilePosition, id); - - return result; + return removed; } std::vector get(const TilePosition& tilePosition) @@ -85,9 +81,8 @@ namespace DetourNavigator std::for_each(itByTilePosition->second.begin(), itByTilePosition->second.end(), [&] (const ObjectId v) { - const auto itById = values->mById.find(v); - if (itById != values->mById.end()) - result.push_back(itById->second); + const auto byId = values->mById.equal_range(v); + std::for_each(byId.first, byId.second, [&] (const auto& v) { result.push_back(v.second); }); }); return result; @@ -96,7 +91,7 @@ namespace DetourNavigator private: struct Values { - std::unordered_map mById; + std::multimap mById; std::map> mByTilePosition; }; diff --git a/components/misc/convert.hpp b/components/misc/convert.hpp index c5671f016..c5784d33a 100644 --- a/components/misc/convert.hpp +++ b/components/misc/convert.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_COMPONENTS_MISC_CONVERT_H #define OPENMW_COMPONENTS_MISC_CONVERT_H +#include + #include #include #include @@ -21,6 +23,11 @@ namespace Convert return osg::Vec3f(value.x(), value.y(), value.z()); } + inline osg::Vec3f makeOsgVec3f(const ESM::Pathgrid::Point& value) + { + return osg::Vec3f(value.mX, value.mY, value.mZ); + } + inline btVector3 toBullet(const osg::Vec3f& vec) { return btVector3(vec.x(), vec.y(), vec.z()); diff --git a/components/misc/coordinateconverter.hpp b/components/misc/coordinateconverter.hpp new file mode 100644 index 000000000..190641415 --- /dev/null +++ b/components/misc/coordinateconverter.hpp @@ -0,0 +1,68 @@ +#ifndef OPENMW_COMPONENTS_MISC_COORDINATECONVERTER_H +#define OPENMW_COMPONENTS_MISC_COORDINATECONVERTER_H + +#include +#include +#include +#include + +namespace Misc +{ + /// \brief convert coordinates between world and local cell + class CoordinateConverter + { + public: + CoordinateConverter(bool exterior, int cellX, int cellY) + : mCellX(exterior ? cellX * ESM::Land::REAL_SIZE : 0), + mCellY(exterior ? cellY * ESM::Land::REAL_SIZE : 0) + { + } + + explicit CoordinateConverter(const ESM::Cell* cell) + : CoordinateConverter(cell->isExterior(), cell->mData.mX, cell->mData.mY) + { + } + + /// in-place conversion from local to world + void toWorld(ESM::Pathgrid::Point& point) const + { + point.mX += mCellX; + point.mY += mCellY; + } + + ESM::Pathgrid::Point toWorldPoint(ESM::Pathgrid::Point point) const + { + toWorld(point); + return point; + } + + /// in-place conversion from local to world + void toWorld(osg::Vec3f& point) const + { + point.x() += static_cast(mCellX); + point.y() += static_cast(mCellY); + } + + /// in-place conversion from world to local + void toLocal(osg::Vec3f& point) const + { + point.x() -= static_cast(mCellX); + point.y() -= static_cast(mCellY); + } + + osg::Vec3f toLocalVec3(const osg::Vec3f& point) const + { + return osg::Vec3f( + point.x() - static_cast(mCellX), + point.y() - static_cast(mCellY), + point.z() + ); + } + + private: + int mCellX; + int mCellY; + }; +} + +#endif From d863267d5cb5f4062937f86c37af3b0c8f9479cf Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 12 Jun 2020 21:04:11 +0200 Subject: [PATCH 179/227] Do not fallback to direct path without pathgrid Assume this might happen only due buildPath call when navmesh can't provide path. --- apps/openmw/mwmechanics/pathfinding.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 824100c60..d00f2615e 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -158,10 +158,7 @@ namespace MWMechanics // Maybe there is no pathgrid for this cell. Just go to destination and let // physics take care of any blockages. if(!pathgrid || pathgrid->mPoints.empty()) - { - *out++ = endPoint; return; - } // NOTE: getClosestPoint expects local coordinates Misc::CoordinateConverter converter(mCell->getCell()); @@ -179,6 +176,9 @@ namespace MWMechanics endPointInLocalCoords, startNode); + if (!endNode.second) + return; + // if it's shorter for actor to travel from start to end, than to travel from either // start or end to nearest pathgrid point, just travel from start to end. float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2(); @@ -249,8 +249,7 @@ namespace MWMechanics // unreachable pathgrid point. // // The AI routines will have to deal with such situations. - if(endNode.second) - *out++ = endPoint; + *out++ = endPoint; } float PathFinder::getZAngleToNext(float x, float y) const @@ -328,16 +327,21 @@ namespace MWMechanics mPath.clear(); mCell = cell; + bool hasNavMesh = false; + if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor)) - buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); + hasNavMesh = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); - if (mPath.empty()) + if (hasNavMesh && mPath.empty()) buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags | DetourNavigator::Flag_usePathgrid, std::back_inserter(mPath)); if (mPath.empty()) buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath)); + if (!hasNavMesh && mPath.empty()) + mPath.push_back(endPoint); + mConstructed = true; } From d684f1a78f165ad51643cceef49626da2810709f Mon Sep 17 00:00:00 2001 From: bzzt Date: Thu, 13 Jun 2019 13:37:00 +0000 Subject: [PATCH 180/227] terrainbased objectpaging Signed-off-by: Bret Curtis --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/objectpaging.cpp | 302 ++++++++++++++++++++++ apps/openmw/mwrender/objectpaging.hpp | 44 ++++ apps/openmw/mwrender/renderingmanager.cpp | 13 + apps/openmw/mwrender/renderingmanager.hpp | 4 + apps/openmw/mwworld/cellpreloader.cpp | 8 +- apps/openmw/mwworld/cellpreloader.hpp | 6 +- apps/openmw/mwworld/esmstore.cpp | 4 + apps/openmw/mwworld/esmstore.hpp | 10 + apps/openmw/mwworld/scene.cpp | 80 +++--- apps/openmw/mwworld/scene.hpp | 13 +- apps/openmw/mwworld/store.cpp | 25 ++ apps/openmw/mwworld/store.hpp | 2 + components/resource/scenemanager.cpp | 11 +- components/resource/scenemanager.hpp | 2 +- components/sceneutil/optimizer.cpp | 92 ++++--- components/sceneutil/optimizer.hpp | 27 +- components/sceneutil/riggeometry.cpp | 2 +- components/sceneutil/riggeometry.hpp | 2 +- components/terrain/chunkmanager.cpp | 14 +- components/terrain/chunkmanager.hpp | 12 +- components/terrain/quadtreeworld.cpp | 53 +++- components/terrain/quadtreeworld.hpp | 13 +- components/terrain/terraingrid.cpp | 14 +- components/terrain/viewdata.cpp | 12 +- components/terrain/viewdata.hpp | 6 +- components/terrain/world.cpp | 2 +- components/terrain/world.hpp | 6 +- files/settings-default.cfg | 9 + 29 files changed, 648 insertions(+), 142 deletions(-) create mode 100644 apps/openmw/mwrender/objectpaging.cpp create mode 100644 apps/openmw/mwrender/objectpaging.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 31ac64e9e..a9f3d24e2 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -21,7 +21,7 @@ add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation - renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager + renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp new file mode 100644 index 000000000..6453889bc --- /dev/null +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -0,0 +1,302 @@ +#include "objectpaging.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "apps/openmw/mwworld/esmstore.hpp" +#include "apps/openmw/mwbase/environment.hpp" +#include "apps/openmw/mwbase/world.hpp" + +#include "vismask.hpp" + +namespace MWRender +{ + + bool typeFilter(int type, bool far) + { + switch (type) + { + case ESM::REC_STAT: + case ESM::REC_ACTI: + case ESM::REC_DOOR: + return true; + case ESM::REC_CONT: + return far ? false : true; + default: + return false; + } + } + + const std::string& getModel(int type, const std::string& id, const MWWorld::ESMStore& store) + { + switch (type) + { + case ESM::REC_STAT: + return store.get().searchStatic(id)->mModel; + case ESM::REC_ACTI: + return store.get().searchStatic(id)->mModel; + case ESM::REC_DOOR: + return store.get().searchStatic(id)->mModel; + case ESM::REC_CONT: + return store.get().searchStatic(id)->mModel; + default: throw std::exception(); + } + } + + osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) + { + if (!far)return nullptr; + ChunkId id = std::make_tuple(center, size); + + osg::ref_ptr obj = mCache->getRefFromObjectCache(id); + if (obj) + return obj->asNode(); + else + { + osg::ref_ptr node = createChunk(size, center, viewPoint); + mCache->addEntryToObjectCache(id, node.get()); + return node; + } + } + + class CanOptimizeCallback : public SceneUtil::Optimizer::IsOperationPermissibleForObjectCallback + { + public: + virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Drawable* node,unsigned int option) const + { + return true; + } + virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Node* node,unsigned int option) const + { + return true; + } + }; + + class CopyOp : public osg::CopyOp + { + public: + CopyOp() : mDistance(0.f) { + setCopyFlags(osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES + #if OSG_MIN_VERSION_REQUIRED(3,5,6) + |osg::CopyOp::DEEP_COPY_ARRAYS|osg::CopyOp::DEEP_COPY_PRIMITIVES // damned vbogarbage racing + #endif + ); + } + + float mDistance; + + virtual osg::Node* operator() (const osg::Node* node) const + { + if (dynamic_cast(node)) + return nullptr; + if (dynamic_cast(node)) + return nullptr; + + if (const osg::Switch* sw = node->asSwitch()) + { + osg::Group* n = new osg::Group; + for (unsigned int i=0; igetNumChildren(); ++i) + if (sw->getValue(i)) + n->addChild(operator()(sw->getChild(i))); + n->setDataVariance(osg::Object::STATIC); + return n; + } + if (const osg::LOD* lod = dynamic_cast(node)) + { + osg::Group* n = new osg::Group; + for (unsigned int i=0; igetNumChildren(); ++i) + if (lod->getMinRange(i) <= mDistance && mDistance < lod->getMaxRange(i)) + n->addChild(operator()(lod->getChild(i))); + n->setDataVariance(osg::Object::STATIC); + return n; + } + + osg::Node* n = osg::CopyOp::operator()(node); + if (n) { + n->setDataVariance(osg::Object::STATIC); + n->setUserDataContainer(nullptr); + n->setName(""); + } + return n; + } + virtual osg::Drawable* operator() (const osg::Drawable* drawable) const + { + if (dynamic_cast(drawable)) + return nullptr; + + if (const SceneUtil::RigGeometry* rig = dynamic_cast(drawable)) + return osg::CopyOp::operator()(rig->getSourceGeometry()); + if (const SceneUtil::MorphGeometry* morph = dynamic_cast(drawable)) + return osg::CopyOp::operator()(morph->getSourceGeometry()); + + return osg::CopyOp::operator()(drawable); + } + virtual osg::Callback* operator() (const osg::Callback* callback) const + { + return nullptr; + } + }; + + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) + : GenericResourceManager(nullptr) + , mSceneManager(sceneManager) + { + mMergeGeometry = Settings::Manager::getBool("object paging merge geometry", "Terrain"); + mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); + } + + osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint) + { + osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f)); + + osg::ref_ptr group = new osg::Group; + + osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE; + osg::Vec3f relativeViewPoint = viewPoint - worldCenter; + + std::vector refs; + std::vector esm; + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + + for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX) + { + for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY) + { + const ESM::Cell* cell = store.get().searchStatic(cellX, cellY); + if (!cell) continue; + for (size_t i=0; imContextList.size(); ++i) + { + try + { + unsigned int index = cell->mContextList.at(i).index; + if (esm.size()<=index) + esm.resize(index+1); + cell->restore(esm[index], i); + ESM::CellRef ref; + ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile; + bool deleted = false; + while(cell->getNextRef(esm[index], ref, deleted)) + { + if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; + int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + if (!typeFilter(type,size>=2)) continue; + if (deleted) continue; + refs.push_back(ref); + } + } + catch (std::exception& e) + { + continue; + } + } + for (ESM::CellRefTracker::const_iterator it = cell->mLeasedRefs.begin(); it != cell->mLeasedRefs.end(); ++it) + { + ESM::CellRef ref = it->first; + bool deleted = it->second; + if (deleted) continue; + int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + if (!typeFilter(type,size>=2)) continue; + refs.push_back(ref); + } + } + } + + osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); + osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); + for (const ESM::CellRef& ref : refs) + { + std::string id = Misc::StringUtils::lowerCase(ref.mRefID); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player + + int type = store.findStatic(id); + std::string model = "meshes/" + getModel(type, id, store); +/* + bool useAnim = type != ESM::REC_STAT; + if (useAnim) + model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); +*/ + if (model.empty()) continue; + + osg::Vec3f pos = ref.mPos.asVec3(); + if (size < 1.f) + { + osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE; + cellPos.x() = std::max(cellPos.x(), std::floor(minBound.x())); + cellPos.x() = std::min(cellPos.x(), std::ceil(maxBound.x())); + cellPos.y() = std::max(cellPos.y(), std::floor(minBound.y())); + cellPos.y() = std::min(cellPos.y(), std::ceil(maxBound.y())); + if (cellPos.x() < minBound.x() || cellPos.x() > maxBound.x() || cellPos.y() < minBound.y() || cellPos.y() > maxBound.y()) + continue; + } + + osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); + + float d = (viewPoint - pos).length(); + + if (cnode->getBound().radius() * ref.mScale < d*mMinSize) + continue; + + CopyOp co = CopyOp(); + co.mDistance = d; + osg::ref_ptr node = osg::clone(cnode.get(), co); + node->setUserDataContainer(nullptr); + + osg::Matrixf matrix; + matrix.preMultTranslate(pos - worldCenter); + matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * + osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * + osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); + matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); + osg::ref_ptr trans = new osg::MatrixTransform(matrix); + trans->addChild(node); + trans->setDataVariance(osg::Object::STATIC); + + group->addChild(trans); + } + + if (mMergeGeometry) + { + SceneUtil::Optimizer optimizer; + if ((relativeViewPoint - group->getBound().center()).length2() > group->getBound().radius2()) + { + optimizer.setViewPoint(relativeViewPoint); + optimizer.setMergeAlphaBlending(true); + } + optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback); + unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY; + optimizer.optimize(group, options); + } + + auto ico = mSceneManager->getIncrementalCompileOperation(); + if (ico) ico->add(group); + else group->getBound(); + + group->setNodeMask(Mask_Static); + + return group; + } + + unsigned int ObjectPaging::getNodeMask() + { + return Mask_Static; + } + +} diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp new file mode 100644 index 000000000..6d66d078d --- /dev/null +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -0,0 +1,44 @@ +#ifndef OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H +#define OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H + +#include +#include +#include + +namespace Resource +{ + class SceneManager; +} +namespace MWWorld +{ + class ESMStore; +} + +namespace MWRender +{ + + typedef std::tuple ChunkId; // Center, Size + + class ObjectPaging : public Resource::GenericResourceManager, public Terrain::QuadTreeWorld::ChunkManager + { + public: + ObjectPaging(Resource::SceneManager* sceneManager); + ~ObjectPaging() = default; + + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) override; + + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint); + + virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } + + virtual unsigned int getNodeMask() override; + + private: + Resource::SceneManager* mSceneManager; + bool mMergeGeometry; + float mMinSize; + }; + +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 462f9fbb6..9c92da767 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -70,6 +70,8 @@ #include "actorspaths.hpp" #include "recastmesh.hpp" #include "fogmanager.hpp" +#include "objectpaging.hpp" + namespace MWRender { @@ -286,6 +288,12 @@ namespace MWRender mTerrain.reset(new Terrain::QuadTreeWorld( sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug, compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize)); + if (Settings::Manager::getBool("object paging", "Terrain")) + { + mObjectPaging.reset(new ObjectPaging(mResourceSystem->getSceneManager())); + static_cast(mTerrain.get())->addChunkManager(mObjectPaging.get()); + mResourceSystem->addResourceManager(mObjectPaging.get()); + } } else mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug)); @@ -1467,4 +1475,9 @@ namespace MWRender mRecastMesh->update(mNavigator.getRecastMeshTiles(), mNavigator.getSettings()); } + + void RenderingManager::setActiveGrid(const osg::Vec4i &grid) + { + mTerrain->setActiveGrid(grid); + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 13f09b359..dff76f95e 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -84,6 +84,7 @@ namespace MWRender class NavMesh; class ActorsPaths; class RecastMesh; + class ObjectPaging; class RenderingManager : public MWRender::RenderingInterface { @@ -237,6 +238,8 @@ namespace MWRender void setNavMeshNumber(const std::size_t value); + void setActiveGrid(const osg::Vec4i &grid); + private: void updateProjectionMatrix(); void updateTextureFiltering(); @@ -275,6 +278,7 @@ namespace MWRender std::unique_ptr mWater; std::unique_ptr mTerrain; TerrainStorage* mTerrainStorage; + std::unique_ptr mObjectPaging; std::unique_ptr mSky; std::unique_ptr mFog; std::unique_ptr mEffectManager; diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 9a96e9806..60b9a3220 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -172,7 +172,7 @@ namespace MWWorld class TerrainPreloadItem : public SceneUtil::WorkItem { public: - TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) + TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) : mAbort(false) , mTerrainViews(views) , mWorld(world) @@ -191,7 +191,7 @@ namespace MWWorld for (unsigned int i=0; ireset(); - mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort); + mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort); } } @@ -204,7 +204,7 @@ namespace MWWorld std::atomic mAbort; std::vector > mTerrainViews; Terrain::World* mWorld; - std::vector mPreloadPositions; + std::vector mPreloadPositions; }; /// Worker thread item: update the resource system's cache, effectively deleting unused entries. @@ -415,7 +415,7 @@ namespace MWWorld mUnrefQueue = unrefQueue; } - void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) + void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) return; diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 0501a4f9b..ef2817019 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include namespace Resource @@ -68,7 +69,8 @@ namespace MWWorld void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue); - void setTerrainPreloadPositions(const std::vector& positions); + typedef std::pair PositionCellGrid; + void setTerrainPreloadPositions(const std::vector& positions); private: Resource::ResourceSystem* mResourceSystem; @@ -105,7 +107,7 @@ namespace MWWorld PreloadMap mPreloadCells; std::vector > mTerrainViews; - std::vector mTerrainPreloadPositions; + std::vector mTerrainPreloadPositions; osg::ref_ptr mTerrainPreloadItem; osg::ref_ptr mUpdateCacheItem; }; diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index f86226640..278b8532e 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -136,6 +136,10 @@ void ESMStore::setUp(bool validateRecords) mIds[*record] = storeIt->first; } } + + if (mStaticIds.empty()) + mStaticIds = mIds; + mSkills.setUp(); mMagicEffects.setUp(); mAttributes.setUp(); diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index fe1cfc708..24364bfb1 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -68,6 +68,8 @@ namespace MWWorld // Lookup of all IDs. Makes looking up references faster. Just // maps the id name to the record type. std::map mIds; + std::map mStaticIds; + std::map mStores; ESM::NPC mPlayerTemplate; @@ -99,6 +101,14 @@ namespace MWWorld } return it->second; } + int findStatic(const std::string &id) const + { + std::map::const_iterator it = mStaticIds.find(id); + if (it == mStaticIds.end()) { + return 0; + } + return it->second; + } ESMStore() : mDynamicCount(0) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 1e1d39bec..127b56c94 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -286,28 +286,6 @@ namespace MWWorld ::updateObjectScale(ptr, *mPhysics, mRendering); } - void Scene::getGridCenter(int &cellX, int &cellY) - { - int maxX = std::numeric_limits::min(); - int maxY = std::numeric_limits::min(); - int minX = std::numeric_limits::max(); - int minY = std::numeric_limits::max(); - CellStoreCollection::iterator iter = mActiveCells.begin(); - while (iter!=mActiveCells.end()) - { - assert ((*iter)->getCell()->isExterior()); - int x = (*iter)->getCell()->getGridX(); - int y = (*iter)->getCell()->getGridY(); - maxX = std::max(x, maxX); - maxY = std::max(y, maxY); - minX = std::min(x, minX); - minY = std::min(y, minY); - ++iter; - } - cellX = (minX + maxX) / 2; - cellY = (minY + maxY) / 2; - } - void Scene::update (float duration, bool paused) { mPreloadTimer += duration; @@ -488,6 +466,27 @@ namespace MWWorld mPreloader->clear(); } + osg::Vec4i Scene::gridCenterToBounds(const osg::Vec2i& centerCell) const + { + return osg::Vec4i(centerCell.x()-mHalfGridSize,centerCell.y()-mHalfGridSize,centerCell.x()+mHalfGridSize+1,centerCell.y()+mHalfGridSize+1); + } + + osg::Vec2i Scene::getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i* currentGridCenter) const + { + if (currentGridCenter) + { + float centerX, centerY; + MWBase::Environment::get().getWorld()->indexToPosition(currentGridCenter->x(), currentGridCenter->y(), centerX, centerY, true); + float distance = std::max(std::abs(centerY-pos.x()), std::abs(centerY-pos.y())); + const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold + if (distance <= maxDistance) + return *currentGridCenter; + } + osg::Vec2i newCenter; + MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newCenter.x(), newCenter.y()); + return newCenter; + } + void Scene::playerMoved(const osg::Vec3f &pos) { const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); @@ -497,19 +496,9 @@ namespace MWWorld if (!mCurrentCell || !mCurrentCell->isExterior()) return; - // figure out the center of the current cell grid (*not* necessarily mCurrentCell, which is the cell the player is in) - int cellX, cellY; - getGridCenter(cellX, cellY); - float centerX, centerY; - MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); - const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold - float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); - if (distance > maxDistance) - { - int newX, newY; - MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newX, newY); - changeCellGrid(newX, newY); - } + osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter); + if (newCell != mCurrentGridCenter) + changeCellGrid(newCell.x(), newCell.y()); } void Scene::changeCellGrid (int playerCellX, int playerCellY, bool changeEvent) @@ -612,6 +601,9 @@ namespace MWWorld CellStore* current = MWBase::Environment::get().getWorld()->getExterior(playerCellX, playerCellY); MWBase::Environment::get().getWindowManager()->changeCell(current); + mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); + mRendering.setActiveGrid(gridCenterToBounds(mCurrentGridCenter)); + if (changeEvent) mCellChanged = true; } @@ -983,7 +975,7 @@ namespace MWWorld void Scene::preloadCells(float dt) { - std::vector exteriorPositions; + std::vector exteriorPositions; const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); @@ -991,7 +983,7 @@ namespace MWWorld osg::Vec3f predictedPos = playerPos + moved / dt * mPredictionTime; if (mCurrentCell->isExterior()) - exteriorPositions.push_back(predictedPos); + exteriorPositions.emplace_back(predictedPos, gridCenterToBounds(getNewGridCenter(predictedPos, &mCurrentGridCenter))); mLastPlayerPos = playerPos; @@ -1008,7 +1000,7 @@ namespace MWWorld mPreloader->setTerrainPreloadPositions(exteriorPositions); } - void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions) + void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions) { std::vector teleportDoors; for (const MWWorld::CellStore* cellStore : mActiveCells) @@ -1042,7 +1034,7 @@ namespace MWWorld int x,y; MWBase::Environment::get().getWorld()->positionToIndex (pos.x(), pos.y(), x, y); preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); - exteriorPositions.push_back(pos); + exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); } } catch (std::exception& e) @@ -1062,7 +1054,7 @@ namespace MWWorld int cellX,cellY; - getGridCenter(cellX,cellY); + cellX = mCurrentGridCenter.x(); cellY = mCurrentGridCenter.y(); float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); @@ -1110,8 +1102,8 @@ namespace MWWorld void Scene::preloadTerrain(const osg::Vec3f &pos) { - std::vector vec; - vec.push_back(pos); + std::vector vec; + vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); mPreloader->setTerrainPreloadPositions(vec); } @@ -1145,7 +1137,7 @@ namespace MWWorld std::vector mList; }; - void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/, std::vector& exteriorPositions) // ignore predictedPos here since opening dialogue with travel service takes extra time + void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/, std::vector& exteriorPositions) // ignore predictedPos here since opening dialogue with travel service takes extra time { const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3()); @@ -1166,7 +1158,7 @@ namespace MWWorld int x,y; MWBase::Environment::get().getWorld()->positionToIndex( pos.x(), pos.y(), x, y); preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); - exteriorPositions.push_back(pos); + exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); } } } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index da795f84b..9b8403c38 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -1,6 +1,9 @@ #ifndef GAME_MWWORLD_SCENE_H #define GAME_MWWORLD_SCENE_H +#include +#include + #include "ptr.hpp" #include "globals.hpp" @@ -86,16 +89,20 @@ namespace MWWorld osg::Vec3f mLastPlayerPos; void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false); + osg::Vec2i mCurrentGridCenter; // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center void changeCellGrid (int playerCellX, int playerCellY, bool changeEvent = true); - void getGridCenter(int& cellX, int& cellY); + typedef std::pair PositionCellGrid; void preloadCells(float dt); - void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); - void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + + osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; + osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; public: diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index a414585ef..93f11d4fd 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -151,6 +151,19 @@ namespace MWWorld return 0; } + template + const T *Store::searchStatic(const std::string &id) const + { + std::string idLower = Misc::StringUtils::lowerCase(id); + typename std::map::const_iterator it = mStatic.find(idLower); + + if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + return &(it->second); + } + + return 0; + } + template bool Store::isDynamic(const std::string &id) const { @@ -582,6 +595,18 @@ namespace MWWorld return 0; } + const ESM::Cell *Store::searchStatic(int x, int y) const + { + ESM::Cell cell; + cell.mData.mX = x, cell.mData.mY = y; + + std::pair key(x, y); + DynamicExt::const_iterator it = mExt.find(key); + if (it != mExt.end()) { + return &(it->second); + } + return 0; + } const ESM::Cell *Store::searchOrCreate(int x, int y) { std::pair key(x, y); diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 64c01589e..f119fa928 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -167,6 +167,7 @@ namespace MWWorld void setUp(); const T *search(const std::string &id) const; + const T *searchStatic(const std::string &id) const; /** * Does the record with this ID come from the dynamic store? @@ -297,6 +298,7 @@ namespace MWWorld const ESM::Cell *search(const std::string &id) const; const ESM::Cell *search(int x, int y) const; + const ESM::Cell *searchStatic(int x, int y) const; const ESM::Cell *searchOrCreate(int x, int y); const ESM::Cell *find(const std::string &id) const; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 61a40ee4b..1709a3d14 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -466,7 +466,7 @@ namespace Resource return options; } - osg::ref_ptr SceneManager::getTemplate(const std::string &name) + osg::ref_ptr SceneManager::getTemplate(const std::string &name, bool compile) { std::string normalized = name; mVFS->normalizeFilename(normalized); @@ -529,7 +529,7 @@ namespace Resource optimizer.optimize(loaded, options); } - if (mIncrementalCompileOperation) + if (compile && mIncrementalCompileOperation) mIncrementalCompileOperation->add(loaded); else loaded->getBound(); @@ -713,6 +713,13 @@ namespace Resource mSharedStateMutex.lock(); mSharedStateManager->prune(); mSharedStateMutex.unlock(); + + if (mIncrementalCompileOperation) + { + OpenThreads::ScopedLock lock(*mIncrementalCompileOperation->getToCompiledMutex()); + while (mIncrementalCompileOperation->getToCompile().size() > 1000) + mIncrementalCompileOperation->getToCompile().pop_front(); + } } void SceneManager::clearCache() diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 1c1c60a58..bbe88c9a0 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -81,7 +81,7 @@ namespace Resource /// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead. /// If even the error marker mesh can not be found, an exception is thrown. /// @note Thread safe. - osg::ref_ptr getTemplate(const std::string& name); + osg::ref_ptr getTemplate(const std::string& name, bool compile=true); /// Create an instance of the given scene template and cache it for later use, so that future calls to getInstance() can simply /// return this cached object instead of creating a new one. diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 487126627..7bcccdbe3 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -103,7 +103,9 @@ void Optimizer::optimize(osg::Node* node, unsigned int options) osg::Timer_t startTick = osg::Timer::instance()->tick(); MergeGeometryVisitor mgv(this); - mgv.setTargetMaximumNumberOfVertices(10000); + mgv.setTargetMaximumNumberOfVertices(1000000); + mgv.setMergeAlphaBlending(_mergeAlphaBlending); + mgv.setViewPoint(_viewPoint); node->accept(mgv); osg::Timer_t endTick = osg::Timer::instance()->tick(); @@ -988,6 +990,17 @@ struct LessGeometry } }; +struct LessGeometryViewPoint +{ + osg::Vec3f _viewPoint; + bool operator() (const osg::ref_ptr& lhs,const osg::ref_ptr& rhs) const + { + float len1 = (lhs->getBoundingBox().center() - _viewPoint).length2(); + float len2 = (rhs->getBoundingBox().center() - _viewPoint).length2(); + return len2 < len1; + } +}; + struct LessGeometryPrimitiveType { bool operator() (const osg::ref_ptr& lhs,const osg::ref_ptr& rhs) const @@ -1055,16 +1068,16 @@ bool isAbleToMerge(const osg::Geometry& g1, const osg::Geometry& g2) void Optimizer::MergeGeometryVisitor::pushStateSet(osg::StateSet *stateSet) { _stateSetStack.push_back(stateSet); - checkAllowedToMerge(); + checkAlphaBlendingActive(); } void Optimizer::MergeGeometryVisitor::popStateSet() { _stateSetStack.pop_back(); - checkAllowedToMerge(); + checkAlphaBlendingActive(); } -void Optimizer::MergeGeometryVisitor::checkAllowedToMerge() +void Optimizer::MergeGeometryVisitor::checkAlphaBlendingActive() { int renderingHint = 0; bool override = false; @@ -1080,7 +1093,7 @@ void Optimizer::MergeGeometryVisitor::checkAllowedToMerge() override = true; } // Can't merge Geometry that are using a transparent sorting bin as that would cause the sorting to break. - _allowedToMerge = renderingHint != osg::StateSet::TRANSPARENT_BIN; + _alphaBlendingActive = renderingHint == osg::StateSet::TRANSPARENT_BIN; } void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) @@ -1088,7 +1101,7 @@ void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) if (group.getStateSet()) pushStateSet(group.getStateSet()); - if (_allowedToMerge) + if (!_alphaBlendingActive || _mergeAlphaBlending) mergeGroup(group); traverse(group); @@ -1097,6 +1110,14 @@ void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) popStateSet(); } +osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps) +{ + if (ps->referenceCount() <= 1) + return ps; + ps = dynamic_cast(ps->clone(osg::CopyOp::DEEP_COPY_ALL)); + return ps; +} + bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (!isOperationPermissibleForObject(&group)) return false; @@ -1120,7 +1141,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) osg::Geometry* geom = child->asGeometry(); if (geom) { - if (!geometryContainsSharedArrays(*geom) && + if ( geom->getDataVariance()!=osg::Object::DYNAMIC && isOperationPermissibleForObject(geom)) { @@ -1254,6 +1275,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) DuplicateList& duplicateList = *mitr; if (!duplicateList.empty()) { + if (_alphaBlendingActive) + { + LessGeometryViewPoint lgvp; + lgvp._viewPoint = _viewPoint; + std::sort(duplicateList.begin(), duplicateList.end(), lgvp); + } DuplicateList::iterator ditr = duplicateList.begin(); osg::ref_ptr lhs = *ditr++; group.addChild(lhs.get()); @@ -1290,10 +1317,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (prim->getNumIndices()==3) { + prim = clonePrimitive(prim); (*itr) = prim; prim->setMode(osg::PrimitiveSet::TRIANGLES); } else if (prim->getNumIndices()==4) { + prim = clonePrimitive(prim); (*itr) = prim; prim->setMode(osg::PrimitiveSet::QUADS); } } @@ -1320,6 +1349,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) #if 1 bool doneCombine = false; + std::set toremove; + osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList(); unsigned int lhsNo=0; unsigned int rhsNo=1; @@ -1348,6 +1379,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (combine) { + lhs = clonePrimitive(lhs); + primitives[lhsNo] = lhs; switch(lhs->getType()) { @@ -1375,7 +1408,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (combine) { // make this primitive set as invalid and needing cleaning up. - rhs->setMode(0xffffff); + toremove.insert(rhs); doneCombine = true; ++rhsNo; } @@ -1390,7 +1423,6 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (doneCombine) { // now need to clean up primitiveset so it no longer contains the rhs combined primitives. - // first swap with a empty primitiveSet to empty it completely. osg::Geometry::PrimitiveSetList oldPrimitives; primitives.swap(oldPrimitives); @@ -1400,7 +1432,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) pitr != oldPrimitives.end(); ++pitr) { - if ((*pitr)->getMode()!=0xffffff) primitives.push_back(*pitr); + if (!toremove.count(*pitr)) primitives.push_back(*pitr); } } #endif @@ -1479,34 +1511,6 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) return false; } -bool Optimizer::MergeGeometryVisitor::geometryContainsSharedArrays(osg::Geometry& geom) -{ - if (geom.getVertexArray() && geom.getVertexArray()->referenceCount()>1) return true; - if (geom.getNormalArray() && geom.getNormalArray()->referenceCount()>1) return true; - if (geom.getColorArray() && geom.getColorArray()->referenceCount()>1) return true; - if (geom.getSecondaryColorArray() && geom.getSecondaryColorArray()->referenceCount()>1) return true; - if (geom.getFogCoordArray() && geom.getFogCoordArray()->referenceCount()>1) return true; - - - for(unsigned int unit=0;unitreferenceCount()>1) return true; - } - - // shift the indices of the incoming primitives to account for the pre existing geometry. - for(osg::Geometry::PrimitiveSetList::iterator primItr=geom.getPrimitiveSetList().begin(); - primItr!=geom.getPrimitiveSetList().end(); - ++primItr) - { - if ((*primItr)->referenceCount()>1) return true; - } - - - return false; -} - - class MergeArrayVisitor : public osg::ArrayVisitor { protected: @@ -1574,6 +1578,8 @@ class MergeArrayVisitor : public osg::ArrayVisitor bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs) { + if (lhs.containsSharedArrays()) + lhs.duplicateSharedArrays(); MergeArrayVisitor merger; @@ -1661,7 +1667,6 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom } } - // shift the indices of the incoming primitives to account for the pre existing geometry. osg::Geometry::PrimitiveSetList::iterator primItr; for(primItr=rhs.getPrimitiveSetList().begin(); primItr!=rhs.getPrimitiveSetList().end(); ++primItr) @@ -1697,7 +1702,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom } else { - primitive->offsetIndices(base); + (*primItr) = clonePrimitive(primitive); + (*primItr)->offsetIndices(base); } } break; @@ -1722,7 +1728,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom } else { - primitive->offsetIndices(base); + (*primItr) = clonePrimitive(primitive); + (*primItr)->offsetIndices(base); } } break; @@ -1731,7 +1738,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType): case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType): default: - primitive->offsetIndices(base); + (*primItr) = clonePrimitive(primitive); + (*primItr)->offsetIndices(base); break; } } diff --git a/components/sceneutil/optimizer.hpp b/components/sceneutil/optimizer.hpp index 6dd4394d1..d7c83e898 100644 --- a/components/sceneutil/optimizer.hpp +++ b/components/sceneutil/optimizer.hpp @@ -65,7 +65,7 @@ class Optimizer public: - Optimizer() {} + Optimizer() : _mergeAlphaBlending(false) {} virtual ~Optimizer() {} enum OptimizationOptions @@ -118,6 +118,9 @@ class Optimizer STATIC_OBJECT_DETECTION }; + void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; } + void setViewPoint(const osg::Vec3f& viewPoint) { _viewPoint = viewPoint; } + /** Reset internal data to initial state - the getPermissibleOptionsMap is cleared.*/ void reset(); @@ -252,6 +255,9 @@ class Optimizer typedef std::map PermissibleOptimizationsMap; PermissibleOptimizationsMap _permissibleOptimizationsMap; + osg::Vec3f _viewPoint; + bool _mergeAlphaBlending; + public: /** Flatten Static Transform nodes by applying their transform to the @@ -371,7 +377,16 @@ class Optimizer /// default to traversing all children. MergeGeometryVisitor(Optimizer* optimizer=0) : BaseOptimizerVisitor(optimizer, MERGE_GEOMETRY), - _targetMaximumNumberOfVertices(10000), _allowedToMerge(true) {} + _targetMaximumNumberOfVertices(10000), _alphaBlendingActive(false), _mergeAlphaBlending(false) {} + + void setMergeAlphaBlending(bool merge) + { + _mergeAlphaBlending = merge; + } + void setViewPoint(const osg::Vec3f& viewPoint) + { + _viewPoint = viewPoint; + } void setTargetMaximumNumberOfVertices(unsigned int num) { @@ -385,15 +400,13 @@ class Optimizer void pushStateSet(osg::StateSet* stateSet); void popStateSet(); - void checkAllowedToMerge(); + void checkAlphaBlendingActive(); virtual void apply(osg::Group& group); virtual void apply(osg::Billboard&) { /* don't do anything*/ } bool mergeGroup(osg::Group& group); - static bool geometryContainsSharedArrays(osg::Geometry& geom); - static bool mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs); static bool mergePrimitive(osg::DrawArrays& lhs,osg::DrawArrays& rhs); @@ -406,7 +419,9 @@ class Optimizer unsigned int _targetMaximumNumberOfVertices; std::vector _stateSetStack; - bool _allowedToMerge; + bool _alphaBlendingActive; + bool _mergeAlphaBlending; + osg::Vec3f _viewPoint; }; }; diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 97c8e1d39..627353b99 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -106,7 +106,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) } } -osg::ref_ptr RigGeometry::getSourceGeometry() +osg::ref_ptr RigGeometry::getSourceGeometry() const { return mSourceGeometry; } diff --git a/components/sceneutil/riggeometry.hpp b/components/sceneutil/riggeometry.hpp index a393530ec..801c172b3 100644 --- a/components/sceneutil/riggeometry.hpp +++ b/components/sceneutil/riggeometry.hpp @@ -44,7 +44,7 @@ namespace SceneUtil /// @note The source geometry will not be modified. void setSourceGeometry(osg::ref_ptr sourceGeom); - osg::ref_ptr getSourceGeometry(); + osg::ref_ptr getSourceGeometry() const; virtual void accept(osg::NodeVisitor &nv); virtual bool supports(const osg::PrimitiveFunctor&) const { return true; } diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 95c1ca491..1dc62cfb8 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include "terraindrawable.hpp" @@ -35,7 +34,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T } -osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags) +osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) { ChunkId id = std::make_tuple(center, lod, lodFlags); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); @@ -163,10 +162,6 @@ std::vector > ChunkManager::createPasses(float chunk osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags) { - osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize(); - osg::ref_ptr transform (new SceneUtil::PositionAttitudeTransform); - transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f)); - osg::ref_ptr positions (new osg::Vec3Array); osg::ref_ptr normals (new osg::Vec3Array); osg::ref_ptr colors (new osg::Vec4ubArray); @@ -224,16 +219,15 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->setPasses(createPasses(chunkSize, chunkCenter, false)); } - transform->addChild(geometry); - transform->getBound(); - geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts); if (mSceneManager->getIncrementalCompileOperation()) { mSceneManager->getIncrementalCompileOperation()->add(geometry); } - return transform; + geometry->setNodeMask(mNodeMask); + + return geometry; } } diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index be83e158c..d6f4dd98e 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -6,6 +6,7 @@ #include #include "buffercache.hpp" +#include "quadtreeworld.hpp" namespace osg { @@ -29,23 +30,28 @@ namespace Terrain typedef std::tuple ChunkId; // Center, Lod, Lod Flags /// @brief Handles loading and caching of terrain chunks - class ChunkManager : public Resource::GenericResourceManager + class ChunkManager : public Resource::GenericResourceManager, public QuadTreeWorld::ChunkManager { public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint); void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; } void setCompositeMapLevel(float level) { mCompositeMapLevel = level; } void setMaxCompositeGeometrySize(float maxCompGeometrySize) { mMaxCompGeometrySize = maxCompGeometrySize; } + void setNodeMask(unsigned int mask) { mNodeMask = mask; } + virtual unsigned int getNodeMask() override { return mNodeMask; } + void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; void clearCache() override; void releaseGLObjects(osg::State* state) override; + virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } + private: osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); @@ -61,6 +67,8 @@ namespace Terrain CompositeMapRenderer* mCompositeMapRenderer; BufferCache mBufferCache; + unsigned int mNodeMask; + unsigned int mCompositeMapSize; float mCompositeMapLevel; float mMaxCompGeometrySize; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index c43a9a21b..842dced20 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -9,6 +9,7 @@ #include #include +#include #include "quadtreenode.hpp" #include "storage.hpp" @@ -63,6 +64,12 @@ public: int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); + if (node->getSize()>1 && dist < (8192+1024)*1.41421356237) + { + // to prevent making chunks who will cross the activegrid border + return false; + } + return nativeLodLevel <= lodLevel; } @@ -231,6 +238,7 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour mChunkManager->setCompositeMapSize(compMapResolution); mChunkManager->setCompositeMapLevel(compMapLevel); mChunkManager->setMaxCompositeGeometrySize(maxCompGeometrySize); + mChunkManagers.push_back(mChunkManager.get()); } QuadTreeWorld::~QuadTreeWorld() @@ -289,7 +297,7 @@ unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewD return lodFlags; } -void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, ChunkManager* chunkManager) +void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector& chunkManagers) { if (!vd->hasChanged() && entry.mRenderingNode) return; @@ -308,7 +316,20 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, C } if (!entry.mRenderingNode) - entry.mRenderingNode = chunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags); + { + auto pat = new SceneUtil::PositionAttitudeTransform; + pat->setPosition(osg::Vec3f(entry.mNode->getCenter().x()*cellWorldSize, entry.mNode->getCenter().y()*cellWorldSize, 0.f)); + + const osg::Vec2f& center = entry.mNode->getCenter(); + bool far = (center.x() <= gridbounds.x() || center.y() <= gridbounds.y() || center.x() >= gridbounds.z() || center.y() >= gridbounds.w()); + + for (QuadTreeWorld::ChunkManager* m : chunkManagers) + { + osg::Node* n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint()); + if (n) pat->addChild(n); + } + entry.mRenderingNode = pat; + } } void updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil::CullVisitor* cv, float cellworldsize, bool outofworld) @@ -385,7 +406,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) bool needsUpdate = true; ViewData* vd = nullptr; if (isCullVisitor) - vd = mViewDataMap->getViewData(static_cast(&nv)->getCurrentCamera(), nv.getViewPoint(), needsUpdate); + vd = mViewDataMap->getViewData(static_cast(&nv)->getCurrentCamera(), nv.getViewPoint(), mActiveGrid, needsUpdate); else { static ViewData sIntersectionViewData; @@ -432,12 +453,12 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) } } + const float cellWorldSize = mStorage->getCellWorldSize(); + for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - - loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); - + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers); entry.mRenderingNode->accept(nv); } @@ -490,13 +511,17 @@ void QuadTreeWorld::enable(bool enabled) void QuadTreeWorld::cacheCell(View *view, int x, int y) { ensureQuadTreeBuilt(); + osg::Vec4i grid (x,y,x+1,y+1); ViewData* vd = static_cast(view); + vd->setActiveGrid(grid); mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5f,y+0.5f)); + const float cellWorldSize = mStorage->getCellWorldSize(); + for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); } } @@ -505,18 +530,21 @@ View* QuadTreeWorld::createView() return new ViewData; } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic &abort) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort) { ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); + vd->setActiveGrid(grid); mRootNode->traverseNodes(vd, viewPoint, mLodCallback, mViewDistance); + const float cellWorldSize = mStorage->getCellWorldSize(); + for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); } vd->markUnchanged(); } @@ -526,7 +554,7 @@ void QuadTreeWorld::storeView(const View* view, double referenceTime) osg::ref_ptr dummy = new osg::DummyObject; const ViewData* vd = static_cast(view); bool needsUpdate = false; - ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), needsUpdate); + ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), vd->getActiveGrid(), needsUpdate); stored->copyFrom(*vd); stored->setLastUsageTimeStamp(referenceTime); } @@ -556,5 +584,10 @@ void QuadTreeWorld::unloadCell(int x, int y) World::unloadCell(x,y); } +void QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m) +{ + mChunkManagers.push_back(m); + mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask()); +} } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index bcb671ee1..7634ea868 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -38,11 +38,20 @@ namespace Terrain virtual void unloadCell(int x, int y); View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint, std::atomic& abort); + void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort); void storeView(const View* view, double referenceTime); void reportStats(unsigned int frameNumber, osg::Stats* stats); + class ChunkManager + { + public: + virtual ~ChunkManager(){} + virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) = 0; + virtual unsigned int getNodeMask() { return 0; } + }; + void addChunkManager(ChunkManager*); + private: void ensureQuadTreeBuilt(); @@ -51,6 +60,8 @@ namespace Terrain osg::ref_ptr mViewDataMap; osg::ref_ptr mLodCallback; + std::vector mChunkManagers; + OpenThreads::Mutex mQuadTreeMutex; bool mQuadTreeBuilt; float mLodFactor; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index a0e5e4718..4398d1a14 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -5,9 +5,10 @@ #include #include +#include #include "chunkmanager.hpp" #include "compositemaprenderer.hpp" - +#include "storage.hpp" namespace Terrain { @@ -57,12 +58,17 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu } else { - osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0); + osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f()); if (!node) return nullptr; + + const float cellWorldSize = mStorage->getCellWorldSize(); + osg::ref_ptr pat = new SceneUtil::PositionAttitudeTransform; + pat->setPosition(osg::Vec3f(chunkCenter.x()*cellWorldSize, chunkCenter.y()*cellWorldSize, 0.f)); + pat->addChild(node); if (parent) - parent->addChild(node); - return node; + parent->addChild(pat); + return pat; } } diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index d07a0e356..6399425a4 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -24,6 +24,7 @@ void ViewData::copyFrom(const ViewData& other) mChanged = other.mChanged; mHasViewPoint = other.mHasViewPoint; mViewPoint = other.mViewPoint; + mActiveGrid = other.mActiveGrid; } void ViewData::add(QuadTreeNode *node) @@ -118,12 +119,12 @@ bool ViewData::Entry::set(QuadTreeNode *node) } } -bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist) +bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist, const osg::Vec4i& activeGrid) { - return vd->hasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist; + return vd->hasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist && vd->getActiveGrid() == activeGrid; } -ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, bool& needsUpdate) +ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate) { Map::const_iterator found = mViews.find(viewer); ViewData* vd = nullptr; @@ -135,11 +136,11 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo else vd = found->second; - if (!suitable(vd, viewPoint, mReuseDistance)) + if (!suitable(vd, viewPoint, mReuseDistance, activeGrid)) { for (Map::const_iterator other = mViews.begin(); other != mViews.end(); ++other) { - if (suitable(other->second, viewPoint, mReuseDistance) && other->second->getNumEntries()) + if (suitable(other->second, viewPoint, mReuseDistance, activeGrid) && other->second->getNumEntries()) { vd->copyFrom(*other->second); needsUpdate = false; @@ -147,6 +148,7 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo } } vd->setViewPoint(viewPoint); + vd->setActiveGrid(activeGrid); needsUpdate = true; } else diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 7f9f14af5..87b45f57b 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -57,6 +57,9 @@ namespace Terrain void setViewPoint(const osg::Vec3f& viewPoint); const osg::Vec3f& getViewPoint() const; + void setActiveGrid(const osg::Vec4i &grid) { if (grid != mActiveGrid) {mActiveGrid = grid;mEntries.clear();mNumEntries=0;} } + const osg::Vec4i &getActiveGrid() const { return mActiveGrid;} + private: std::vector mEntries; unsigned int mNumEntries; @@ -64,6 +67,7 @@ namespace Terrain bool mChanged; osg::Vec3f mViewPoint; bool mHasViewPoint; + osg::Vec4i mActiveGrid; }; class ViewDataMap : public osg::Referenced @@ -75,7 +79,7 @@ namespace Terrain , mExpiryDelay(1.f) {} - ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, bool& needsUpdate); + ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate); ViewData* createOrReuseView(); diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 2d53f4090..b57fa1cef 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -23,7 +23,6 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); - mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); osg::ref_ptr material (new osg::Material); material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); @@ -48,6 +47,7 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer)); + mChunkManager->setNodeMask(nodeMask); mCellBorder.reset(new CellBorder(this,mTerrainRoot.get(),borderMask)); mResourceSystem->addResourceManager(mChunkManager.get()); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 618095a60..e55932169 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -147,7 +147,7 @@ namespace Terrain /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. - virtual void preload(View* view, const osg::Vec3f& viewPoint, std::atomic& abort) {} + virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort) {} /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// @note Not thread safe. @@ -161,6 +161,8 @@ namespace Terrain osg::Callback* getHeightCullCallback(float highz, unsigned int mask); + void setActiveGrid(const osg::Vec4i &grid) { mActiveGrid = grid; } + protected: Storage* mStorage; @@ -181,6 +183,8 @@ namespace Terrain std::set> mLoadedCells; osg::ref_ptr mHeightCullCallback; + + osg::Vec4i mActiveGrid; }; } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 5b587776c..2653f2fc4 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -106,6 +106,15 @@ composite map resolution = 512 # Controls the maximum size of composite geometry, should be >= 1.0. With low values there will be many small chunks, with high values - lesser count of bigger chunks. max composite geometry size = 4.0 +# Load far objects on terrain +object paging = true + +# Turn off to save memory but worse FPS +object paging merge geometry = true + +# Cull objects smaller than this size divided by distance +object paging min size = 0.01 + [Fog] # If true, use extended fog parameters for distant terrain not controlled by From c0f128bcb3b084e101c167836a14b6015367a4ea Mon Sep 17 00:00:00 2001 From: bzzt Date: Sat, 3 Aug 2019 13:37:00 +0000 Subject: [PATCH 181/227] disablesupprort Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 31 +++++++++++--- apps/openmw/mwrender/objectpaging.hpp | 8 ++++ apps/openmw/mwrender/renderingmanager.cpp | 7 ++++ apps/openmw/mwrender/renderingmanager.hpp | 3 ++ apps/openmw/mwworld/cellstore.cpp | 49 +++++++++++++---------- apps/openmw/mwworld/worldimp.cpp | 22 ++++++---- 6 files changed, 84 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 6453889bc..8a8015372 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -171,7 +171,7 @@ namespace MWRender osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE; osg::Vec3f relativeViewPoint = viewPoint - worldCenter; - std::vector refs; + std::map refs; std::vector esm; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); @@ -197,8 +197,8 @@ namespace MWRender if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); if (!typeFilter(type,size>=2)) continue; - if (deleted) continue; - refs.push_back(ref); + if (deleted) { refs.erase(ref.mRefNum); continue; } + refs[ref.mRefNum] = ref; } } catch (std::exception& e) @@ -210,18 +210,25 @@ namespace MWRender { ESM::CellRef ref = it->first; bool deleted = it->second; - if (deleted) continue; + if (deleted) { refs.erase(ref.mRefNum); continue; } int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); if (!typeFilter(type,size>=2)) continue; - refs.push_back(ref); + refs[ref.mRefNum] = ref; } } } + { + OpenThreads::ScopedLock lock(mDisabledMutex); + for (auto disabled : mDisabled) + refs.erase(disabled); + } + osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); - for (const ESM::CellRef& ref : refs) + for (const auto& pair : refs) { + const ESM::CellRef& ref = pair.second; std::string id = Misc::StringUtils::lowerCase(ref.mRefID); if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player @@ -299,4 +306,16 @@ namespace MWRender return Mask_Static; } + void ObjectPaging::enableObject(const ESM::RefNum & refnum, bool enabled) + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (enabled) mDisabled.erase(refnum); + else mDisabled.insert(refnum); + } + + void ObjectPaging::clear() + { + OpenThreads::ScopedLock lock(mDisabledMutex); + mDisabled.clear(); + } } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 6d66d078d..8c3c2b493 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -5,6 +5,8 @@ #include #include +#include + namespace Resource { class SceneManager; @@ -33,10 +35,16 @@ namespace MWRender virtual unsigned int getNodeMask() override; + void enableObject(const ESM::RefNum & refnum, bool enabled); + void clear(); + private: Resource::SceneManager* mSceneManager; bool mMergeGeometry; float mMinSize; + + OpenThreads::Mutex mDisabledMutex; + std::set mDisabled; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9c92da767..41cfc56a4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1119,6 +1119,8 @@ namespace MWRender mSky->setMoonColour(false); notifyWorldSpaceChanged(); + if (mObjectPaging) + mObjectPaging->clear(); } MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) @@ -1480,4 +1482,9 @@ namespace MWRender { mTerrain->setActiveGrid(grid); } + void RenderingManager::pagingEnableObject(const ESM::RefNum & refnum, bool enabled) + { + if (mObjectPaging) + mObjectPaging->enableObject(refnum, enabled); + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index dff76f95e..6bf122232 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -42,6 +42,7 @@ namespace osgViewer namespace ESM { struct Cell; + struct RefNum; } namespace Terrain @@ -240,6 +241,8 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); + void pagingEnableObject(const ESM::RefNum & refnum, bool enabled); + private: void updateProjectionMatrix(); void updateTextureFiltering(); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 599f345b8..6b737f202 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -106,7 +106,7 @@ namespace template void readReferenceCollection (ESM::ESMReader& reader, - MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap) + MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap, MWWorld::CellStore* cellstore) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -142,6 +142,11 @@ namespace { // overwrite existing reference iter->load (state); + if (!iter->mData.isEnabled()) + { + iter->mData.enable(); + MWBase::Environment::get().getWorld()->disable(MWWorld::Ptr(&*iter, cellstore)); + } return; } @@ -809,107 +814,107 @@ namespace MWWorld { case ESM::REC_ACTI: - readReferenceCollection (reader, mActivators, cref, contentFileMap); + readReferenceCollection (reader, mActivators, cref, contentFileMap, this); break; case ESM::REC_ALCH: - readReferenceCollection (reader, mPotions, cref, contentFileMap); + readReferenceCollection (reader, mPotions, cref, contentFileMap, this); break; case ESM::REC_APPA: - readReferenceCollection (reader, mAppas, cref, contentFileMap); + readReferenceCollection (reader, mAppas, cref, contentFileMap, this); break; case ESM::REC_ARMO: - readReferenceCollection (reader, mArmors, cref, contentFileMap); + readReferenceCollection (reader, mArmors, cref, contentFileMap, this); break; case ESM::REC_BOOK: - readReferenceCollection (reader, mBooks, cref, contentFileMap); + readReferenceCollection (reader, mBooks, cref, contentFileMap, this); break; case ESM::REC_CLOT: - readReferenceCollection (reader, mClothes, cref, contentFileMap); + readReferenceCollection (reader, mClothes, cref, contentFileMap, this); break; case ESM::REC_CONT: - readReferenceCollection (reader, mContainers, cref, contentFileMap); + readReferenceCollection (reader, mContainers, cref, contentFileMap, this); break; case ESM::REC_CREA: - readReferenceCollection (reader, mCreatures, cref, contentFileMap); + readReferenceCollection (reader, mCreatures, cref, contentFileMap, this); break; case ESM::REC_DOOR: - readReferenceCollection (reader, mDoors, cref, contentFileMap); + readReferenceCollection (reader, mDoors, cref, contentFileMap, this); break; case ESM::REC_INGR: - readReferenceCollection (reader, mIngreds, cref, contentFileMap); + readReferenceCollection (reader, mIngreds, cref, contentFileMap, this); break; case ESM::REC_LEVC: - readReferenceCollection (reader, mCreatureLists, cref, contentFileMap); + readReferenceCollection (reader, mCreatureLists, cref, contentFileMap, this); break; case ESM::REC_LEVI: - readReferenceCollection (reader, mItemLists, cref, contentFileMap); + readReferenceCollection (reader, mItemLists, cref, contentFileMap, this); break; case ESM::REC_LIGH: - readReferenceCollection (reader, mLights, cref, contentFileMap); + readReferenceCollection (reader, mLights, cref, contentFileMap, this); break; case ESM::REC_LOCK: - readReferenceCollection (reader, mLockpicks, cref, contentFileMap); + readReferenceCollection (reader, mLockpicks, cref, contentFileMap, this); break; case ESM::REC_MISC: - readReferenceCollection (reader, mMiscItems, cref, contentFileMap); + readReferenceCollection (reader, mMiscItems, cref, contentFileMap, this); break; case ESM::REC_NPC_: - readReferenceCollection (reader, mNpcs, cref, contentFileMap); + readReferenceCollection (reader, mNpcs, cref, contentFileMap, this); break; case ESM::REC_PROB: - readReferenceCollection (reader, mProbes, cref, contentFileMap); + readReferenceCollection (reader, mProbes, cref, contentFileMap, this); break; case ESM::REC_REPA: - readReferenceCollection (reader, mRepairs, cref, contentFileMap); + readReferenceCollection (reader, mRepairs, cref, contentFileMap, this); break; case ESM::REC_STAT: - readReferenceCollection (reader, mStatics, cref, contentFileMap); + readReferenceCollection (reader, mStatics, cref, contentFileMap, this); break; case ESM::REC_WEAP: - readReferenceCollection (reader, mWeapons, cref, contentFileMap); + readReferenceCollection (reader, mWeapons, cref, contentFileMap, this); break; case ESM::REC_BODY: - readReferenceCollection (reader, mBodyParts, cref, contentFileMap); + readReferenceCollection (reader, mBodyParts, cref, contentFileMap, this); break; default: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c51266bab..0817a4226 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -814,6 +814,9 @@ namespace MWWorld if(mWorldScene->getActiveCells().find (reference.getCell()) != mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->addObjectToScene (reference); + + if (reference.getCellRef().getRefNum().hasContentFile()) + mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), true); } } @@ -838,20 +841,23 @@ namespace MWWorld void World::disable (const Ptr& reference) { + if (!reference.getRefData().isEnabled()) + return; + // disable is a no-op for items in containers if (!reference.isInCell()) return; - if (reference.getRefData().isEnabled()) - { - if (reference == getPlayerPtr()) - throw std::runtime_error("can not disable player object"); + if (reference == getPlayerPtr()) + throw std::runtime_error("can not disable player object"); - reference.getRefData().disable(); + reference.getRefData().disable(); - if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) - mWorldScene->removeObjectFromScene (reference); - } + if (reference.getCellRef().getRefNum().hasContentFile()) + mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), false); + + if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) + mWorldScene->removeObjectFromScene (reference); } void World::advanceTime (double hours, bool incremental) From ce505a9bb355233ff1dfbe4017aa4e66711a86e4 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Thu, 30 Apr 2020 13:37:00 +0000 Subject: [PATCH 182/227] crashfix + optimiziation Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 233 +++++++++++++++++++------- apps/openmw/mwrender/objectpaging.hpp | 6 +- apps/openmw/mwworld/scene.cpp | 2 +- components/sceneutil/optimizer.cpp | 102 +++++++++-- components/terrain/chunkmanager.cpp | 8 +- components/terrain/chunkmanager.hpp | 4 +- components/terrain/quadtreeworld.cpp | 42 +++-- components/terrain/quadtreeworld.hpp | 3 +- components/terrain/terraingrid.cpp | 2 +- files/settings-default.cfg | 4 +- 10 files changed, 300 insertions(+), 106 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 8a8015372..b529336eb 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -1,5 +1,7 @@ #include "objectpaging.hpp" +#include + #include #include #include @@ -14,7 +16,6 @@ #include #include -#include #include #include @@ -60,7 +61,7 @@ namespace MWRender } } - osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) + osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) { if (!far)return nullptr; ChunkId id = std::make_tuple(center, size); @@ -70,7 +71,7 @@ namespace MWRender return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, viewPoint); + osg::ref_ptr node = createChunk(size, center, viewPoint, compile); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -92,12 +93,11 @@ namespace MWRender class CopyOp : public osg::CopyOp { public: - CopyOp() : mDistance(0.f) { - setCopyFlags(osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES - #if OSG_MIN_VERSION_REQUIRED(3,5,6) - |osg::CopyOp::DEEP_COPY_ARRAYS|osg::CopyOp::DEEP_COPY_PRIMITIVES // damned vbogarbage racing - #endif - ); + CopyOp(bool deep) : mDistance(0.f) { + unsigned int flags = osg::CopyOp::DEEP_COPY_NODES; + if (deep) + flags |= osg::CopyOp::DEEP_COPY_DRAWABLES; + setCopyFlags(flags); } float mDistance; @@ -128,12 +128,13 @@ namespace MWRender return n; } - osg::Node* n = osg::CopyOp::operator()(node); - if (n) { - n->setDataVariance(osg::Object::STATIC); - n->setUserDataContainer(nullptr); - n->setName(""); - } + if (const osg::Drawable* d = node->asDrawable()) + return operator()(d); + + osg::Node* n = osg::clone(node, *this); + n->setDataVariance(osg::Object::STATIC); + n->setUserDataContainer(nullptr); + n->setName(""); return n; } virtual osg::Drawable* operator() (const osg::Drawable* drawable) const @@ -142,11 +143,20 @@ namespace MWRender return nullptr; if (const SceneUtil::RigGeometry* rig = dynamic_cast(drawable)) - return osg::CopyOp::operator()(rig->getSourceGeometry()); + return operator()(rig->getSourceGeometry()); if (const SceneUtil::MorphGeometry* morph = dynamic_cast(drawable)) - return osg::CopyOp::operator()(morph->getSourceGeometry()); + return operator()(morph->getSourceGeometry()); - return osg::CopyOp::operator()(drawable); + if (getCopyFlags() & DEEP_COPY_DRAWABLES) + { + osg::Drawable* d = osg::clone(drawable, *this); + d->setDataVariance(osg::Object::STATIC); + d->setUserDataContainer(nullptr); + d->setName(""); + return d; + } + else + return osg::CopyOp::operator()(drawable); } virtual osg::Callback* operator() (const osg::Callback* callback) const { @@ -154,20 +164,77 @@ namespace MWRender } }; + class TemplateRef : public osg::Object + { + public: + TemplateRef() {} + TemplateRef(const TemplateRef& copy, const osg::CopyOp&) : mObjects(copy.mObjects) {} + META_Object(MWRender, TemplateRef) + std::vector> mObjects; + }; + + class AnalyzeVisitor : public osg::NodeVisitor + { + public: + AnalyzeVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mCurrentStateSet(nullptr) {} + + typedef std::unordered_map StateSetCounter; + struct Result + { + StateSetCounter mStateSetCounter; + unsigned int mNumVerts = 0; + }; + + virtual void apply(osg::Node& node) + { + if (node.getStateSet()) + mCurrentStateSet = node.getStateSet(); + traverse(node); + } + virtual void apply(osg::Geometry& geom) + { + mResult.mNumVerts += geom.getVertexArray()->getNumElements(); + ++mResult.mStateSetCounter[mCurrentStateSet]; + ++mGlobalStateSetCounter[mCurrentStateSet]; + } + Result retrieveResult() + { + Result result = mResult; + mResult = Result(); + mCurrentStateSet = nullptr; + return result; + } + float getMergeBenefit(const Result& result) + { + if (result.mStateSetCounter.empty()) return 1; + float mergeBenefit = 0; + for (auto pair : result.mStateSetCounter) + { + mergeBenefit += mGlobalStateSetCounter[pair.first]; + } + mergeBenefit /= result.mStateSetCounter.size(); + return mergeBenefit; + } + + Result mResult; + osg::StateSet* mCurrentStateSet; + StateSetCounter mGlobalStateSetCounter; + }; + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) { - mMergeGeometry = Settings::Manager::getBool("object paging merge geometry", "Terrain"); + mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); } - osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint) + osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile) { osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f)); - osg::ref_ptr group = new osg::Group; - osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE; osg::Vec3f relativeViewPoint = viewPoint - worldCenter; @@ -226,21 +293,18 @@ namespace MWRender osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); + struct InstanceList + { + std::vector mInstances; + AnalyzeVisitor::Result mAnalyzeResult; + }; + typedef std::map, InstanceList> NodeMap; + NodeMap nodes; + AnalyzeVisitor analyzeVisitor; + for (const auto& pair : refs) { const ESM::CellRef& ref = pair.second; - std::string id = Misc::StringUtils::lowerCase(ref.mRefID); - if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") - continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - - int type = store.findStatic(id); - std::string model = "meshes/" + getModel(type, id, store); -/* - bool useAnim = type != ESM::REC_STAT; - if (useAnim) - model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); -*/ - if (model.empty()) continue; osg::Vec3f pos = ref.mPos.asVec3(); if (size < 1.f) @@ -254,50 +318,101 @@ namespace MWRender continue; } - osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); + std::string id = Misc::StringUtils::lowerCase(ref.mRefID); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - float d = (viewPoint - pos).length(); + int type = store.findStatic(id); + std::string model = getModel(type, id, store); + if (model.empty()) continue; + model = "meshes/" + model; +/* + bool useAnim = type != ESM::REC_STAT; + if (useAnim) + model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); +*/ + osg::ref_ptr cnode = mSceneManager->getTemplate(model, compile); + float d = (viewPoint - pos).length(); if (cnode->getBound().radius() * ref.mScale < d*mMinSize) continue; - CopyOp co = CopyOp(); - co.mDistance = d; - osg::ref_ptr node = osg::clone(cnode.get(), co); - node->setUserDataContainer(nullptr); - - osg::Matrixf matrix; - matrix.preMultTranslate(pos - worldCenter); - matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * - osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * - osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); - matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); - osg::ref_ptr trans = new osg::MatrixTransform(matrix); - trans->addChild(node); - trans->setDataVariance(osg::Object::STATIC); - - group->addChild(trans); + auto emplaced = nodes.emplace(cnode, InstanceList()); + if (emplaced.second) + { + const_cast(cnode.get())->accept(analyzeVisitor); + emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); + } + emplaced.first->second.mInstances.push_back(&ref); } - if (mMergeGeometry) + osg::ref_ptr group = new osg::Group; + osg::ref_ptr mergeGroup = new osg::Group; + osg::ref_ptr templateRefs = new TemplateRef; + for (const auto& pair : nodes) + { + const osg::Node* cnode = pair.first; + + // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache + templateRefs->mObjects.push_back(cnode); + + const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult; + + float mergeCost = analyzeResult.mNumVerts * size; + float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; + bool merge = mergeBenefit > mergeCost; + + for (auto cref : pair.second.mInstances) + { + const ESM::CellRef& ref = *cref; + osg::Vec3f pos = ref.mPos.asVec3(); + float d = (viewPoint - pos).length(); + + CopyOp co = CopyOp(merge); + co.mDistance = d; + osg::ref_ptr node = osg::clone(cnode, co); + node->setUserDataContainer(nullptr); + + osg::Matrixf matrix; + matrix.preMultTranslate(pos - worldCenter); + matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * + osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * + osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); + matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); + osg::ref_ptr trans = new osg::MatrixTransform(matrix); + trans->addChild(node); + trans->setDataVariance(osg::Object::STATIC); + + if (merge) + mergeGroup->addChild(trans); + else + group->addChild(trans); + } + } + + if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - group->getBound().center()).length2() > group->getBound().radius2()) + if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); } optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback); unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY; - optimizer.optimize(group, options); - } + optimizer.optimize(mergeGroup, options); + + group->addChild(mergeGroup); - auto ico = mSceneManager->getIncrementalCompileOperation(); - if (ico) ico->add(group); - else group->getBound(); + auto ico = mSceneManager->getIncrementalCompileOperation(); + if (compile && ico) ico->add(mergeGroup); + } + group->getBound(); group->setNodeMask(Mask_Static); + group->getOrCreateUserDataContainer()->addUserObject(templateRefs); + return group; } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 8c3c2b493..16de6e8bc 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -27,9 +27,9 @@ namespace MWRender ObjectPaging(Resource::SceneManager* sceneManager); ~ObjectPaging() = default; - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) override; + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) override; - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile); virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } @@ -40,7 +40,7 @@ namespace MWRender private: Resource::SceneManager* mSceneManager; - bool mMergeGeometry; + float mMergeFactor; float mMinSize; OpenThreads::Mutex mDisabledMutex; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 127b56c94..1dac18eaa 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -477,7 +477,7 @@ namespace MWWorld { float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(currentGridCenter->x(), currentGridCenter->y(), centerX, centerY, true); - float distance = std::max(std::abs(centerY-pos.x()), std::abs(centerY-pos.y())); + float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold if (distance <= maxDistance) return *currentGridCenter; diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 7bcccdbe3..985fd9ee2 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -18,6 +18,7 @@ #include "optimizer.hpp" +#include #include #include #include @@ -587,17 +588,36 @@ void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Node& node) traverse(node); } +bool needvbo(const osg::Geometry* geom) +{ +#if OSG_MIN_VERSION_REQUIRED(3,5,6) + return true; +#else + return geom->getUseVertexBufferObjects(); +#endif +} + +osg::Array* cloneArray(osg::Array* array, osg::VertexBufferObject*& vbo, const osg::Geometry* geom) +{ + array = osg::clone(array, osg::CopyOp::DEEP_COPY_ALL); + if (!vbo && needvbo(geom)) + vbo = new osg::VertexBufferObject; + if (vbo) + array->setVertexBufferObject(vbo); + return array; +} void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Drawable& drawable) { osg::Geometry *geometry = drawable.asGeometry(); if((geometry) && (isOperationPermissibleForObject(&drawable))) { + osg::VertexBufferObject* vbo = nullptr; if(geometry->getVertexArray() && geometry->getVertexArray()->referenceCount() > 1) { - geometry->setVertexArray(dynamic_cast(geometry->getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL))); + geometry->setVertexArray(cloneArray(geometry->getVertexArray(), vbo, geometry)); } if(geometry->getNormalArray() && geometry->getNormalArray()->referenceCount() > 1) { - geometry->setNormalArray(dynamic_cast(geometry->getNormalArray()->clone(osg::CopyOp::DEEP_COPY_ALL))); + geometry->setNormalArray(cloneArray(geometry->getNormalArray(), vbo, geometry)); } } _drawableSet.insert(&drawable); @@ -1110,14 +1130,30 @@ void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) popStateSet(); } -osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps) +osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps, osg::ElementBufferObject*& ebo, const osg::Geometry* geom) { if (ps->referenceCount() <= 1) return ps; - ps = dynamic_cast(ps->clone(osg::CopyOp::DEEP_COPY_ALL)); + ps = osg::clone(ps, osg::CopyOp::DEEP_COPY_ALL); + + osg::DrawElements* drawElements = ps->getDrawElements(); + if (!drawElements) return ps; + + if (!ebo && needvbo(geom)) + ebo = new osg::ElementBufferObject; + if (ebo) + drawElements->setElementBufferObject(ebo); + return ps; } +bool containsSharedPrimitives(const osg::Geometry* geom) +{ + for (unsigned int i=0; igetNumPrimitiveSets(); ++i) + if (geom->getPrimitiveSet(i)->referenceCount() > 1) return true; + return false; +} + bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (!isOperationPermissibleForObject(&group)) return false; @@ -1305,6 +1341,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (!drawable) continue; osg::Geometry* geom = drawable->asGeometry(); + osg::ElementBufferObject* ebo = nullptr; if (geom) { osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList(); @@ -1317,12 +1354,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (prim->getNumIndices()==3) { - prim = clonePrimitive(prim); (*itr) = prim; + prim = clonePrimitive(prim, ebo, geom); (*itr) = prim; prim->setMode(osg::PrimitiveSet::TRIANGLES); } else if (prim->getNumIndices()==4) { - prim = clonePrimitive(prim); (*itr) = prim; + prim = clonePrimitive(prim, ebo, geom); (*itr) = prim; prim->setMode(osg::PrimitiveSet::QUADS); } } @@ -1337,6 +1374,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (!drawable) continue; osg::Geometry* geom = drawable->asGeometry(); + osg::ElementBufferObject* ebo = nullptr; if (geom) { if (geom->getNumPrimitiveSets()>0 && @@ -1379,7 +1417,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (combine) { - lhs = clonePrimitive(lhs); + lhs = clonePrimitive(lhs, ebo, geom); primitives[lhsNo] = lhs; switch(lhs->getType()) @@ -1499,6 +1537,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) } } #endif + if (doneCombine && !geom->containsSharedArrays() && !containsSharedPrimitives(geom)) + { + // prefer to use vbo for merged geometries as vbo uses less memory than display lists. + geom->setUseVertexBufferObjects(true); + geom->setUseDisplayList(false); + } } } @@ -1578,16 +1622,14 @@ class MergeArrayVisitor : public osg::ArrayVisitor bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs) { - if (lhs.containsSharedArrays()) - lhs.duplicateSharedArrays(); - MergeArrayVisitor merger; - + osg::VertexBufferObject* vbo = nullptr; unsigned int base = 0; if (lhs.getVertexArray() && rhs.getVertexArray()) { - base = lhs.getVertexArray()->getNumElements(); + if (lhs.getVertexArray()->referenceCount() > 1) + lhs.setVertexArray(cloneArray(lhs.getVertexArray(), vbo, &lhs)); if (!merger.merge(lhs.getVertexArray(),rhs.getVertexArray())) { OSG_DEBUG << "MergeGeometry: vertex array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getNormalArray()->referenceCount() > 1) + lhs.setNormalArray(cloneArray(lhs.getNormalArray(), vbo, &lhs)); if (!merger.merge(lhs.getNormalArray(),rhs.getNormalArray())) { OSG_DEBUG << "MergeGeometry: normal array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getColorArray()->referenceCount() > 1) + lhs.setColorArray(cloneArray(lhs.getColorArray(), vbo, &lhs)); if (!merger.merge(lhs.getColorArray(),rhs.getColorArray())) { OSG_DEBUG << "MergeGeometry: color array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getSecondaryColorArray()->referenceCount() > 1) + lhs.setSecondaryColorArray(cloneArray(lhs.getSecondaryColorArray(), vbo, &lhs)); if (!merger.merge(lhs.getSecondaryColorArray(),rhs.getSecondaryColorArray())) { OSG_DEBUG << "MergeGeometry: secondary color array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getFogCoordArray()->referenceCount() > 1) + lhs.setFogCoordArray(cloneArray(lhs.getFogCoordArray(), vbo, &lhs)); if (!merger.merge(lhs.getFogCoordArray(),rhs.getFogCoordArray())) { OSG_DEBUG << "MergeGeometry: fog coord array not merged. Some data may be lost." <referenceCount() > 1) + lhs.setTexCoordArray(unit, cloneArray(lhs.getTexCoordArray(unit), vbo, &lhs)); if (!merger.merge(lhs.getTexCoordArray(unit),rhs.getTexCoordArray(unit))) { OSG_DEBUG << "MergeGeometry: tex coord array not merged. Some data may be lost." <referenceCount() > 1) + lhs.setVertexAttribArray(unit, cloneArray(lhs.getVertexAttribArray(unit), vbo, &lhs)); if (!merger.merge(lhs.getVertexAttribArray(unit),rhs.getVertexAttribArray(unit))) { OSG_DEBUG << "MergeGeometry: vertex attrib array not merged. Some data may be lost." <getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; @@ -1696,13 +1758,18 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom { // must promote to a DrawElementsUShort osg::DrawElementsUShort* new_primitive = new osg::DrawElementsUShort(primitive->getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; } else { - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); } } @@ -1722,13 +1789,18 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom { // must promote to a DrawElementsUInt osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUShort->begin(),primitiveUShort->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; } else { - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); } } @@ -1738,7 +1810,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType): case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType): default: - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); break; } diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 1dc62cfb8..5f80b9d36 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -34,7 +34,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T } -osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) +osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) { ChunkId id = std::make_tuple(center, lod, lodFlags); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); @@ -42,7 +42,7 @@ osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f &cen return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, lod, lodFlags); + osg::ref_ptr node = createChunk(size, center, lod, lodFlags, compile); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -160,7 +160,7 @@ std::vector > ChunkManager::createPasses(float chunk return ::Terrain::createPasses(useShaders, &mSceneManager->getShaderManager(), layers, blendmapTextures, blendmapScale, blendmapScale); } -osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags) +osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags, bool compile) { osg::ref_ptr positions (new osg::Vec3Array); osg::ref_ptr normals (new osg::Vec3Array); @@ -221,7 +221,7 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts); - if (mSceneManager->getIncrementalCompileOperation()) + if (compile && mSceneManager->getIncrementalCompileOperation()) { mSceneManager->getIncrementalCompileOperation()->add(geometry); } diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index d6f4dd98e..e323da6a4 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -35,7 +35,7 @@ namespace Terrain public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile); void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; } void setCompositeMapLevel(float level) { mCompositeMapLevel = level; } @@ -53,7 +53,7 @@ namespace Terrain virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } private: - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool compile); osg::ref_ptr createCompositeMapRTT(); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 842dced20..d252149b2 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -53,9 +53,10 @@ namespace Terrain class DefaultLodCallback : public LodCallback { public: - DefaultLodCallback(float factor, float minSize) + DefaultLodCallback(float factor, float minSize, const osg::Vec4i& grid) : mFactor(factor) , mMinSize(minSize) + , mActiveGrid(grid) { } @@ -64,20 +65,27 @@ public: int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); - if (node->getSize()>1 && dist < (8192+1024)*1.41421356237) + if (node->getSize()>1) { + float halfSize = node->getSize()/2; + const osg::Vec2f& center = node->getCenter(); + osg::Vec4i nodeBounds (static_cast(center.x() - halfSize), static_cast(center.y() - halfSize), static_cast(center.x() + halfSize), static_cast(center.y() + halfSize)); + bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) <= std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) <= std::min(nodeBounds.w(), mActiveGrid.w())); // to prevent making chunks who will cross the activegrid border - return false; + if (intersects) + return false; } - return nativeLodLevel <= lodLevel; } private: float mFactor; float mMinSize; + osg::Vec4i mActiveGrid; }; +const float MIN_SIZE = 1/8.f; + class RootNode : public QuadTreeNode { public: @@ -297,7 +305,7 @@ unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewD return lodFlags; } -void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector& chunkManagers) +void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector& chunkManagers, bool compile) { if (!vd->hasChanged() && entry.mRenderingNode) return; @@ -325,7 +333,7 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, f for (QuadTreeWorld::ChunkManager* m : chunkManagers) { - osg::Node* n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint()); + osg::ref_ptr n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint(), compile); if (n) pat->addChild(n); } entry.mRenderingNode = pat; @@ -411,6 +419,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) { static ViewData sIntersectionViewData; vd = &sIntersectionViewData; + vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. } if (needsUpdate) @@ -430,7 +439,10 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5,y+0.5)); } else - mRootNode->traverseNodes(vd, cv->getViewPoint(), mLodCallback, mViewDistance); + { + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); + mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); + } } else { @@ -458,16 +470,13 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers, false); entry.mRenderingNode->accept(nv); } if (isCullVisitor) updateWaterCullingView(mHeightCullCallback, vd, static_cast(&nv), mStorage->getCellWorldSize(), !isGridEmpty()); - if (!isCullVisitor) - vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. - vd->markUnchanged(); double referenceTime = nv.getFrameStamp() ? nv.getFrameStamp()->getReferenceTime() : 0.0; @@ -484,9 +493,7 @@ void QuadTreeWorld::ensureQuadTreeBuilt() if (mQuadTreeBuilt) return; - const float minSize = 1/8.f; - mLodCallback = new DefaultLodCallback(mLodFactor, minSize); - QuadTreeBuilder builder(mStorage, minSize); + QuadTreeBuilder builder(mStorage, MIN_SIZE); builder.build(); mRootNode = builder.getRootNode(); @@ -521,7 +528,7 @@ void QuadTreeWorld::cacheCell(View *view, int x, int y) for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); } } @@ -537,14 +544,15 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); vd->setActiveGrid(grid); - mRootNode->traverseNodes(vd, viewPoint, mLodCallback, mViewDistance); + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, grid); + mRootNode->traverseNodes(vd, viewPoint, &lodCallback, mViewDistance); const float cellWorldSize = mStorage->getCellWorldSize(); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); } vd->markUnchanged(); } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 7634ea868..201054852 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -47,7 +47,7 @@ namespace Terrain { public: virtual ~ChunkManager(){} - virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) = 0; + virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) = 0; virtual unsigned int getNodeMask() { return 0; } }; void addChunkManager(ChunkManager*); @@ -58,7 +58,6 @@ namespace Terrain osg::ref_ptr mRootNode; osg::ref_ptr mViewDataMap; - osg::ref_ptr mLodCallback; std::vector mChunkManagers; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 4398d1a14..5f99cd97e 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -58,7 +58,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu } else { - osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f()); + osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f(), true); if (!node) return nullptr; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 2653f2fc4..ec5e87016 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -109,8 +109,8 @@ max composite geometry size = 4.0 # Load far objects on terrain object paging = true -# Turn off to save memory but worse FPS -object paging merge geometry = true +# Affects the likelyhood of objects being merged. A higher value means merging is more likely and improves FPS at the cost of memory. +object paging merge factor = 1500 # Cull objects smaller than this size divided by distance object paging min size = 0.01 From cf439581e1e84bb1a6a3a7ae0ef7ddba1dc3887c Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sun, 3 May 2020 13:37:00 +0000 Subject: [PATCH 183/227] comply by elsid review Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 12 +++++++----- apps/openmw/mwrender/objectpaging.hpp | 2 -- components/resource/scenemanager.cpp | 10 ++++++++-- components/terrain/chunkmanager.hpp | 2 -- components/terrain/quadtreeworld.cpp | 2 +- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index b529336eb..560edfcb4 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -45,7 +45,7 @@ namespace MWRender } } - const std::string& getModel(int type, const std::string& id, const MWWorld::ESMStore& store) + std::string getModel(int type, const std::string& id, const MWWorld::ESMStore& store) { switch (type) { @@ -57,7 +57,8 @@ namespace MWRender return store.get().searchStatic(id)->mModel; case ESM::REC_CONT: return store.get().searchStatic(id)->mModel; - default: throw std::exception(); + default: + return std::string(); } } @@ -340,7 +341,7 @@ namespace MWRender auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { - const_cast(cnode.get())->accept(analyzeVisitor); + const_cast(cnode.get())->accept(analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); } emplaced.first->second.mInstances.push_back(&ref); @@ -410,8 +411,9 @@ namespace MWRender group->getBound(); group->setNodeMask(Mask_Static); - - group->getOrCreateUserDataContainer()->addUserObject(templateRefs); + osg::UserDataContainer* udc = group->getOrCreateUserDataContainer(); + udc->addUserObject(templateRefs); + udc->addUserObject(mergeGroup); // for ICO ref counting return group; } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 16de6e8bc..56f393c7b 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -31,8 +31,6 @@ namespace MWRender osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile); - virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } - virtual unsigned int getNodeMask() override; void enableObject(const ESM::RefNum & refnum, bool enabled); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 1709a3d14..f4a605bca 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -717,8 +717,14 @@ namespace Resource if (mIncrementalCompileOperation) { OpenThreads::ScopedLock lock(*mIncrementalCompileOperation->getToCompiledMutex()); - while (mIncrementalCompileOperation->getToCompile().size() > 1000) - mIncrementalCompileOperation->getToCompile().pop_front(); + osgUtil::IncrementalCompileOperation::CompileSets& sets = mIncrementalCompileOperation->getToCompile(); + for(osgUtil::IncrementalCompileOperation::CompileSets::iterator it = sets.begin(); it != sets.end();) + { + if ((*it)->_subgraphToCompile->referenceCount() <= 2) + it = sets.erase(it); + else + ++it; + } } } diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index e323da6a4..87770fafb 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -50,8 +50,6 @@ namespace Terrain void releaseGLObjects(osg::State* state) override; - virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } - private: osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool compile); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index d252149b2..69291f6d8 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -325,7 +325,7 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, f if (!entry.mRenderingNode) { - auto pat = new SceneUtil::PositionAttitudeTransform; + osg::ref_ptr pat = new SceneUtil::PositionAttitudeTransform; pat->setPosition(osg::Vec3f(entry.mNode->getCenter().x()*cellWorldSize, entry.mNode->getCenter().y()*cellWorldSize, 0.f)); const osg::Vec2f& center = entry.mNode->getCenter(); From 1f891ca46def28a9d6cc4eab51676476cef4f6e2 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 4 May 2020 13:37:00 +0000 Subject: [PATCH 184/227] billboarding support for tree mods Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 87 ++++++++++++++++++++++----- components/terrain/world.cpp | 2 + 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 560edfcb4..6393bfc76 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -94,14 +94,16 @@ namespace MWRender class CopyOp : public osg::CopyOp { public: - CopyOp(bool deep) : mDistance(0.f) { + CopyOp(bool deep) : mSqrDistance(0.f) { unsigned int flags = osg::CopyOp::DEEP_COPY_NODES; if (deep) flags |= osg::CopyOp::DEEP_COPY_DRAWABLES; setCopyFlags(flags); } - float mDistance; + float mSqrDistance; + osg::Vec3f mViewVector; + mutable std::vector mNodePath; virtual osg::Node* operator() (const osg::Node* node) const { @@ -123,7 +125,7 @@ namespace MWRender { osg::Group* n = new osg::Group; for (unsigned int i=0; igetNumChildren(); ++i) - if (lod->getMinRange(i) <= mDistance && mDistance < lod->getMaxRange(i)) + if (lod->getMinRange(i) * lod->getMinRange(i) <= mSqrDistance && mSqrDistance < lod->getMaxRange(i) * lod->getMaxRange(i)) n->addChild(operator()(lod->getChild(i))); n->setDataVariance(osg::Object::STATIC); return n; @@ -132,11 +134,64 @@ namespace MWRender if (const osg::Drawable* d = node->asDrawable()) return operator()(d); - osg::Node* n = osg::clone(node, *this); - n->setDataVariance(osg::Object::STATIC); - n->setUserDataContainer(nullptr); - n->setName(""); - return n; + mNodePath.push_back(node); + + osg::Node* cloned = osg::clone(node, *this); + cloned->setDataVariance(osg::Object::STATIC); + cloned->setUserDataContainer(nullptr); + cloned->setName(""); + + mNodePath.pop_back(); + + handleCallbacks(node, cloned); + + return cloned; + } + void handleCallbacks(const osg::Node* node, osg::Node *cloned) const + { + const osg::Callback* callback = node->getCullCallback(); + while (callback) + { + if (callback->className() == std::string("BillboardCallback")) + handleBillboard(cloned); + callback = callback->getNestedCallback(); + } + } + void handleBillboard(osg::Node* node) const + { + osg::Transform* transform = node->asTransform(); + if (!transform) return; + osg::MatrixTransform* matrixTransform = transform->asMatrixTransform(); + if (!matrixTransform) return; + + osg::Matrix worldToLocal = osg::Matrix::identity(); + for (auto node : mNodePath) + if (const osg::Transform* t = node->asTransform()) + t->computeWorldToLocalMatrix(worldToLocal, nullptr); + worldToLocal = osg::Matrix::orthoNormal(worldToLocal); + + osg::Matrix billboardMatrix; + osg::Vec3f viewVector = -(mViewVector + worldToLocal.getTrans()); + viewVector.normalize(); + osg::Vec3f right = viewVector ^ osg::Vec3f(0,0,1); + right.normalize(); + osg::Vec3f up = right ^ viewVector; + up.normalize(); + billboardMatrix.makeLookAt(osg::Vec3f(0,0,0), viewVector, up); + billboardMatrix.invert(billboardMatrix); + + const osg::Matrix& oldMatrix = matrixTransform->getMatrix(); + float mag[3]; // attempt to preserve scale + for (int i=0;i<3;++i) + mag[i] = std::sqrt(oldMatrix(0,i) * oldMatrix(0,i) + oldMatrix(1,i) * oldMatrix(1,i) + oldMatrix(2,i) * oldMatrix(2,i)); + osg::Matrix newMatrix; + worldToLocal.setTrans(0,0,0); + newMatrix *= worldToLocal; + newMatrix.preMult(billboardMatrix); + newMatrix.preMultScale(osg::Vec3f(mag[0], mag[1], mag[2])); + newMatrix.setTrans(oldMatrix.getTrans()); + + matrixTransform->setMatrix(newMatrix); } virtual osg::Drawable* operator() (const osg::Drawable* drawable) const { @@ -367,12 +422,6 @@ namespace MWRender { const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); - float d = (viewPoint - pos).length(); - - CopyOp co = CopyOp(merge); - co.mDistance = d; - osg::ref_ptr node = osg::clone(cnode, co); - node->setUserDataContainer(nullptr); osg::Matrixf matrix; matrix.preMultTranslate(pos - worldCenter); @@ -381,9 +430,17 @@ namespace MWRender osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); osg::ref_ptr trans = new osg::MatrixTransform(matrix); - trans->addChild(node); trans->setDataVariance(osg::Object::STATIC); + CopyOp co = CopyOp(merge); + co.mNodePath.push_back(trans); + co.mSqrDistance = (viewPoint - pos).length2(); + co.mViewVector = (viewPoint - worldCenter); + osg::ref_ptr node = osg::clone(cnode, co); + node->setUserDataContainer(nullptr); + + trans->addChild(node); + if (merge) mergeGroup->addChild(trans); else diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index b57fa1cef..fcacb442a 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -23,6 +23,8 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); + mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); + osg::ref_ptr material (new osg::Material); material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); From 69514dfd46ede1f2b561e0ffcbe85432c3866016 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 4 May 2020 13:37:00 +0000 Subject: [PATCH 185/227] ico redundency fix + stats counter Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 17 +++++++++++++---- apps/openmw/mwrender/objectpaging.hpp | 2 ++ components/resource/scenemanager.cpp | 7 ++++++- components/resource/stats.cpp | 1 + 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 6393bfc76..28644cecf 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -463,14 +463,17 @@ namespace MWRender group->addChild(mergeGroup); auto ico = mSceneManager->getIncrementalCompileOperation(); - if (compile && ico) ico->add(mergeGroup); + if (compile && ico) + { + auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(mergeGroup); + ico->add(compileSet); + compileSet->_subgraphToCompile = group; // for ref counting in SceneManager::updateCache + } } group->getBound(); group->setNodeMask(Mask_Static); - osg::UserDataContainer* udc = group->getOrCreateUserDataContainer(); - udc->addUserObject(templateRefs); - udc->addUserObject(mergeGroup); // for ICO ref counting + group->getOrCreateUserDataContainer()->addUserObject(templateRefs); return group; } @@ -492,4 +495,10 @@ namespace MWRender OpenThreads::ScopedLock lock(mDisabledMutex); mDisabled.clear(); } + + void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const + { + stats->setAttribute(frameNumber, "Object Chunk", mCache->getCacheSize()); + } + } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 56f393c7b..9fb3aa754 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -36,6 +36,8 @@ namespace MWRender void enableObject(const ESM::RefNum & refnum, bool enabled); void clear(); + void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; + private: Resource::SceneManager* mSceneManager; float mMergeFactor; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index f4a605bca..29b312670 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -720,8 +720,13 @@ namespace Resource osgUtil::IncrementalCompileOperation::CompileSets& sets = mIncrementalCompileOperation->getToCompile(); for(osgUtil::IncrementalCompileOperation::CompileSets::iterator it = sets.begin(); it != sets.end();) { - if ((*it)->_subgraphToCompile->referenceCount() <= 2) + int refcount = (*it)->_subgraphToCompile->referenceCount(); + if ((*it)->_subgraphToCompile->asDrawable()) refcount -= 1; // ref by CompileList. + if (refcount <= 2) // ref by ObjectCache + ref by _subgraphToCompile. + { + // no other ref = not needed anymore. it = sets.erase(it); + } else ++it; } diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 0dd52ffb6..2dda2c2df 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -292,6 +292,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) "Nif", "Keyframe", "", + "Object Chunk", "Terrain Chunk", "Terrain Texture", "Land", From 38c21163eab45050096ed7067ddabddd27a3a444 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 4 May 2020 13:37:00 +0000 Subject: [PATCH 186/227] + meshsizecache for reduce i&o stalling Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 21 +++++++++++++++++++-- apps/openmw/mwrender/objectpaging.hpp | 4 ++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 28644cecf..412123309 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -374,6 +374,17 @@ namespace MWRender continue; } + float d = (viewPoint - pos).length(); + { + OpenThreads::ScopedLock lock(mSizeCacheMutex); + SizeCache::iterator found = mSizeCache.find(pair.first); + if (found != mSizeCache.end()) + { + if (found->second < d*mMinSize) + continue; + } + } + std::string id = Misc::StringUtils::lowerCase(ref.mRefID); if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player @@ -389,9 +400,15 @@ namespace MWRender */ osg::ref_ptr cnode = mSceneManager->getTemplate(model, compile); - float d = (viewPoint - pos).length(); - if (cnode->getBound().radius() * ref.mScale < d*mMinSize) + float radius = cnode->getBound().radius() * ref.mScale; + if (radius < d*mMinSize) + { + OpenThreads::ScopedLock lock(mSizeCacheMutex); + { + mSizeCache[pair.first] = radius; + } continue; + } auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 9fb3aa754..71a0db996 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -45,6 +45,10 @@ namespace MWRender OpenThreads::Mutex mDisabledMutex; std::set mDisabled; + + OpenThreads::Mutex mSizeCacheMutex; + typedef std::map SizeCache; + SizeCache mSizeCache; }; } From 0b4226f3e27488d4f6dd3293871ffb4b27eba94a Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 5 May 2020 13:37:00 +0000 Subject: [PATCH 187/227] ico effieciency Signed-off-by: Bret Curtis --- apps/openmw/mwgui/loadingscreen.cpp | 32 ++++++++++++++------ apps/openmw/mwgui/loadingscreen.hpp | 10 ++++--- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwrender/objectpaging.cpp | 36 +++++++++++++++++------ apps/openmw/mwrender/renderingmanager.cpp | 1 - 5 files changed, 57 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index dcfe723f7..436e9c2bc 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/statemanager.hpp" @@ -29,9 +30,9 @@ namespace MWGui { - LoadingScreen::LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer) + LoadingScreen::LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer) : WindowBase("openmw_loading_screen.layout") - , mVFS(vfs) + , mResourceSystem(resourceSystem) , mViewer(viewer) , mTargetFrameRate(120.0) , mLastWallpaperChangeTime(0.0) @@ -64,9 +65,9 @@ namespace MWGui void LoadingScreen::findSplashScreens() { - const std::map& index = mVFS->getIndex(); + const std::map& index = mResourceSystem->getVFS()->getIndex(); std::string pattern = "Splash/"; - mVFS->normalizeFilename(pattern); + mResourceSystem->getVFS()->normalizeFilename(pattern); /* priority given to the left */ const std::array supported_extensions {{".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}}; @@ -171,6 +172,11 @@ namespace MWGui // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); + if (const osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) { + mOldIcoMin = ico->getMinimumTimeAvailableForGLCompileAndDeletePerFrame(); + mOldIcoMax = ico->getMaximumNumOfObjectsToCompilePerFrame(); + } + mVisible = visible; mLoadingBox->setVisible(mVisible); setVisible(true); @@ -215,6 +221,12 @@ namespace MWGui //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl; setVisible(false); + if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) + { + ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(mOldIcoMin); + ico->setMaximumNumOfObjectsToCompilePerFrame(mOldIcoMax); + } + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper); } @@ -336,7 +348,13 @@ namespace MWGui MWBase::Environment::get().getInputManager()->update(0, true, true); - //osg::Timer timer; + mResourceSystem->reportStats(mViewer->getFrameStamp()->getFrameNumber(), mViewer->getViewerStats()); + if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) + { + ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(1.f/getTargetFrameRate()); + ico->setMaximumNumOfObjectsToCompilePerFrame(1000); + } + // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() @@ -344,10 +362,6 @@ namespace MWGui mViewer->updateTraversal(); mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); - //std::cout << "frame took " << timer.time_m() << std::endl; - - //if (mViewer->getIncrementalCompileOperation()) - //std::cout << "num to compile " << mViewer->getIncrementalCompileOperation()->getToCompile().size() << std::endl; mLastRenderTime = mTimer.time_m(); } diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index c054f3bbd..1be1b3a0c 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -20,9 +20,9 @@ namespace osg class Texture2D; } -namespace VFS +namespace Resource { - class Manager; + class ResourceSystem; } namespace MWGui @@ -32,7 +32,7 @@ namespace MWGui class LoadingScreen : public WindowBase, public Loading::Listener { public: - LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer); + LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer); virtual ~LoadingScreen(); /// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details @@ -53,7 +53,7 @@ namespace MWGui void setupCopyFramebufferToTextureCallback(); - const VFS::Manager* mVFS; + Resource::ResourceSystem* mResourceSystem; osg::ref_ptr mViewer; double mTargetFrameRate; @@ -70,6 +70,8 @@ namespace MWGui size_t mProgress; bool mShowWallpaper; + float mOldIcoMin = 0.f; + unsigned int mOldIcoMax = 0; MyGUI::Widget* mLoadingBox; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ab7c3334c..e1f7a4fbf 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -230,7 +230,7 @@ namespace MWGui mKeyboardNavigation->setEnabled(keyboardNav); Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav); - mLoadingScreen = new LoadingScreen(mResourceSystem->getVFS(), mViewer); + mLoadingScreen = new LoadingScreen(mResourceSystem, mViewer); mWindows.push_back(mLoadingScreen); //set up the hardware cursor manager diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 412123309..0d547184c 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -353,6 +353,7 @@ namespace MWRender { std::vector mInstances; AnalyzeVisitor::Result mAnalyzeResult; + bool mNeedCompile = false; }; typedef std::map, InstanceList> NodeMap; NodeMap nodes; @@ -398,7 +399,7 @@ namespace MWRender if (useAnim) model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); */ - osg::ref_ptr cnode = mSceneManager->getTemplate(model, compile); + osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); float radius = cnode->getBound().radius() * ref.mScale; if (radius < d*mMinSize) @@ -415,6 +416,7 @@ namespace MWRender { const_cast(cnode.get())->accept(analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); + emplaced.first->second.mNeedCompile = compile && cnode->referenceCount() <= 3; } emplaced.first->second.mInstances.push_back(&ref); } @@ -422,19 +424,29 @@ namespace MWRender osg::ref_ptr group = new osg::Group; osg::ref_ptr mergeGroup = new osg::Group; osg::ref_ptr templateRefs = new TemplateRef; + osgUtil::StateToCompile stateToCompile(0, nullptr); for (const auto& pair : nodes) { const osg::Node* cnode = pair.first; - // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache - templateRefs->mObjects.push_back(cnode); - const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult; float mergeCost = analyzeResult.mNumVerts * size; float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; bool merge = mergeBenefit > mergeCost; + // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache + templateRefs->mObjects.push_back(cnode); + + if (pair.second.mNeedCompile) + { + int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES; + if (!merge) + mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + stateToCompile._mode = mode; + const_cast(cnode)->accept(stateToCompile); + } + for (auto cref : pair.second.mInstances) { const ESM::CellRef& ref = *cref; @@ -479,15 +491,21 @@ namespace MWRender group->addChild(mergeGroup); - auto ico = mSceneManager->getIncrementalCompileOperation(); - if (compile && ico) + if (compile) { - auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(mergeGroup); - ico->add(compileSet); - compileSet->_subgraphToCompile = group; // for ref counting in SceneManager::updateCache + stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + mergeGroup->accept(stateToCompile); } } + auto ico = mSceneManager->getIncrementalCompileOperation(); + if (!stateToCompile.empty() && ico) + { + auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(group); + compileSet->buildCompileMap(ico->getContextSet(), stateToCompile); + ico->add(compileSet, false); + } + group->getBound(); group->setNodeMask(Mask_Static); group->getOrCreateUserDataContainer()->addUserObject(templateRefs); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 41cfc56a4..a545e0674 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -260,7 +260,6 @@ namespace MWRender { mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); - mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100); } mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation()); From 8a624e5a71aa39b81e3f69586eacd336363ff198 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 5 May 2020 13:37:00 +0000 Subject: [PATCH 188/227] minsize based on mergedecision solves partial culling Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 50 +++++++++++++++++++-------- apps/openmw/mwrender/objectpaging.hpp | 2 ++ files/settings-default.cfg | 6 ++++ 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 0d547184c..5438347a1 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -285,6 +285,8 @@ namespace MWRender { mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); + mMinSizeMergeFactor = Settings::Manager::getFloat("object paging min size merge factor", "Terrain"); + mMinSizeCostMultiplier = Settings::Manager::getFloat("object paging min size cost multiplier", "Terrain"); } osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile) @@ -358,7 +360,9 @@ namespace MWRender typedef std::map, InstanceList> NodeMap; NodeMap nodes; AnalyzeVisitor analyzeVisitor; - + float minSize = mMinSize; + if (mMinSizeMergeFactor) + minSize *= mMinSizeMergeFactor; for (const auto& pair : refs) { const ESM::CellRef& ref = pair.second; @@ -381,7 +385,7 @@ namespace MWRender SizeCache::iterator found = mSizeCache.find(pair.first); if (found != mSizeCache.end()) { - if (found->second < d*mMinSize) + if (found->second < d*minSize) continue; } } @@ -402,7 +406,7 @@ namespace MWRender osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); float radius = cnode->getBound().radius() * ref.mScale; - if (radius < d*mMinSize) + if (radius < d*minSize) { OpenThreads::ScopedLock lock(mSizeCacheMutex); { @@ -435,23 +439,26 @@ namespace MWRender float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; bool merge = mergeBenefit > mergeCost; - // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache - templateRefs->mObjects.push_back(cnode); - - if (pair.second.mNeedCompile) - { - int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES; - if (!merge) - mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; - stateToCompile._mode = mode; - const_cast(cnode)->accept(stateToCompile); - } + float minSizeMerged = mMinSize; + float factor2 = mergeBenefit > 0 ? std::min(1.f, mergeCost * mMinSizeCostMultiplier / mergeBenefit) : 1; + float minSizeMergeFactor2 = (1-factor2) * mMinSizeMergeFactor + factor2; + if (minSizeMergeFactor2 > 0) + minSizeMerged *= minSizeMergeFactor2; + unsigned int numinstances = 0; for (auto cref : pair.second.mInstances) { const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); + if (minSizeMerged != minSize) + { + float d = (viewPoint - pos).length(); + float radius = cnode->getBound().radius() * cref->mScale; + if (radius < d*minSizeMerged) + continue; + } + osg::Matrixf matrix; matrix.preMultTranslate(pos - worldCenter); matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * @@ -474,6 +481,21 @@ namespace MWRender mergeGroup->addChild(trans); else group->addChild(trans); + ++numinstances; + } + if (numinstances > 0) + { + // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache + templateRefs->mObjects.push_back(cnode); + + if (pair.second.mNeedCompile) + { + int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES; + if (!merge) + mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + stateToCompile._mode = mode; + const_cast(cnode)->accept(stateToCompile); + } } } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 71a0db996..c85fe3705 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -42,6 +42,8 @@ namespace MWRender Resource::SceneManager* mSceneManager; float mMergeFactor; float mMinSize; + float mMinSizeMergeFactor; + float mMinSizeCostMultiplier; OpenThreads::Mutex mDisabledMutex; std::set mDisabled; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index ec5e87016..bb21df73e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -115,6 +115,12 @@ object paging merge factor = 1500 # Cull objects smaller than this size divided by distance object paging min size = 0.01 +# Adjusts 'min size' based on merging decision. Allows inexpensive objects to be rendered from a greater distance. +object paging min size merge factor = 0.6 + +# Controls how inexpensive an object needs to be to utilize 'min size merge factor'. +object paging min size cost multiplier = 4 + [Fog] # If true, use extended fog parameters for distant terrain not controlled by From 00e56ae8624e8b75102945efb6a2f13695f78d4a Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 5 May 2020 13:37:00 +0000 Subject: [PATCH 189/227] batch debug colours Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 29 +++++++++++++++++++++++++++ apps/openmw/mwrender/objectpaging.hpp | 1 + files/settings-default.cfg | 5 ++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 5438347a1..dc84ccdd2 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include "apps/openmw/mwworld/esmstore.hpp" #include "apps/openmw/mwbase/environment.hpp" @@ -279,10 +281,31 @@ namespace MWRender StateSetCounter mGlobalStateSetCounter; }; + class DebugVisitor : public osg::NodeVisitor + { + public: + DebugVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {} + virtual void apply(osg::Drawable& node) + { + osg::ref_ptr m (new osg::Material); + osg::Vec4f color(Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), 0.f); + color.normalize(); + m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f)); + m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f)); + m->setColorMode(osg::Material::OFF); + m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(color)); + osg::ref_ptr stateset = node.getStateSet() ? osg::clone(node.getStateSet(), osg::CopyOp::SHALLOW_COPY) : new osg::StateSet; + stateset->setAttribute(m); + stateset->addUniform(new osg::Uniform("colorMode", 0)); + node.setStateSet(stateset); + } + }; + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) { + mDebugBatches = Settings::Manager::getBool("object paging debug batches", "Terrain"); mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); mMinSizeMergeFactor = Settings::Manager::getFloat("object paging min size merge factor", "Terrain"); @@ -518,6 +541,12 @@ namespace MWRender stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; mergeGroup->accept(stateToCompile); } + + if (mDebugBatches) + { + DebugVisitor dv; + mergeGroup->accept(dv); + } } auto ico = mSceneManager->getIncrementalCompileOperation(); diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index c85fe3705..57121ae76 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -40,6 +40,7 @@ namespace MWRender private: Resource::SceneManager* mSceneManager; + bool mDebugBatches; float mMergeFactor; float mMinSize; float mMinSizeMergeFactor; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index bb21df73e..4a8eb889e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -109,7 +109,7 @@ max composite geometry size = 4.0 # Load far objects on terrain object paging = true -# Affects the likelyhood of objects being merged. A higher value means merging is more likely and improves FPS at the cost of memory. +# Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory. object paging merge factor = 1500 # Cull objects smaller than this size divided by distance @@ -121,6 +121,9 @@ object paging min size merge factor = 0.6 # Controls how inexpensive an object needs to be to utilize 'min size merge factor'. object paging min size cost multiplier = 4 +# Assign a random color to merged batches. +object paging debug batches = false + [Fog] # If true, use extended fog parameters for distant terrain not controlled by From 77b92aee9cd4b6282f5b3600b075994becc9f968 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Wed, 6 May 2020 13:37:00 +0000 Subject: [PATCH 190/227] fix shadowsglitch by bounds overflow Signed-off-by: Bret Curtis --- components/terrain/quadtreenode.cpp | 7 ------- components/terrain/quadtreenode.hpp | 2 -- components/terrain/quadtreeworld.cpp | 2 ++ files/settings-default.cfg | 4 ++-- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index a28554be9..ddb2c611b 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -171,8 +171,6 @@ void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) { mBoundingBox = boundingBox; mValidBounds = boundingBox.valid(); - dirtyBound(); - getBound(); } const osg::BoundingBox &QuadTreeNode::getBoundingBox() const @@ -180,11 +178,6 @@ const osg::BoundingBox &QuadTreeNode::getBoundingBox() const return mBoundingBox; } -osg::BoundingSphere QuadTreeNode::computeBound() const -{ - return osg::BoundingSphere(mBoundingBox); -} - float QuadTreeNode::getSize() const { return mSize; diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 4adbc6025..0d4cf7807 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -91,8 +91,6 @@ namespace Terrain const osg::BoundingBox& getBoundingBox() const; bool hasValidBounds() const { return mValidBounds; } - virtual osg::BoundingSphere computeBound() const; - /// size in cell coordinates float getSize() const; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 69291f6d8..d13a3989d 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -140,6 +140,8 @@ public: addChildren(mRootNode); mRootNode->initNeighbours(); + float cellWorldSize = mStorage->getCellWorldSize(); + mRootNode->setInitialBound(osg::BoundingSphere(osg::BoundingBox(osg::Vec3(mMinX*cellWorldSize, mMinY*cellWorldSize, 0), osg::Vec3(mMaxX*cellWorldSize, mMaxY*cellWorldSize, 0)))); } void addChildren(QuadTreeNode* parent) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 4a8eb889e..f250b68aa 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -826,8 +826,8 @@ enable debug hud = false # Enable the debug overlay to see where each shadow map affects. enable debug overlay = false -# Attempt to better use the shadow map by making them cover a smaller area. May have a minor to major performance impact. -compute tight scene bounds = true +# Attempt to better use the shadow map by making them cover a smaller area. May have a major performance impact. +compute tight scene bounds = false # How large to make the shadow map(s). Higher values increase GPU load, but can produce better-looking results. Power-of-two values may turn out to be faster on some GPU/driver combinations. shadow map resolution = 1024 From b7b31926a844d0608915826c5d9c4c9421704be2 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Wed, 6 May 2020 13:37:00 +0000 Subject: [PATCH 191/227] fix map glitch + cleanup Signed-off-by: Bret Curtis --- apps/openmw/mwrender/localmap.cpp | 8 +------ components/terrain/quadtreenode.cpp | 22 ----------------- components/terrain/quadtreenode.hpp | 3 --- components/terrain/quadtreeworld.cpp | 36 +++------------------------- components/terrain/quadtreeworld.hpp | 2 +- 5 files changed, 5 insertions(+), 66 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 416a753eb..12401f45e 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -168,11 +168,10 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax) { osg::ref_ptr camera (new osg::Camera); - camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10); camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); camera->setViewMatrixAsLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector); - camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); + camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f)); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -360,11 +359,6 @@ void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell) osg::ref_ptr camera = createOrthographicCamera(x*mMapWorldSize + mMapWorldSize/2.f, y*mMapWorldSize + mMapWorldSize/2.f, mMapWorldSize, mMapWorldSize, osg::Vec3d(0,1,0), zmin, zmax); - camera->getOrCreateUserDataContainer()->addDescription("NoTerrainLod"); - std::ostringstream stream; - stream << x << " " << y; - camera->getOrCreateUserDataContainer()->addDescription(stream.str()); - setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY()); MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())]; diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index ddb2c611b..1f5a3e4b3 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -128,28 +128,6 @@ void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodC } } -void QuadTreeNode::traverseTo(ViewData* vd, float size, const osg::Vec2f& center) -{ - if (!hasValidBounds()) - return; - - if (getCenter().x() + getSize()/2.f <= center.x() - size/2.f - || getCenter().x() - getSize()/2.f >= center.x() + size/2.f - || getCenter().y() + getSize()/2.f <= center.y() - size/2.f - || getCenter().y() - getSize()/2.f >= center.y() + size/2.f) - return; - - bool stopTraversal = (getSize() == size); - - if (stopTraversal) - vd->add(this); - else - { - for (unsigned int i=0; itraverseTo(vd, size, center); - } -} - void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector& intersector) { if (!hasValidBounds()) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 0d4cf7807..74abea362 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -100,9 +100,6 @@ namespace Terrain /// Traverse nodes according to LOD selection. void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist); - /// Traverse to a specific node and add only that node. - void traverseTo(ViewData* vd, float size, const osg::Vec2f& center); - /// Adds all leaf nodes which intersect the line from start to end void intersect(ViewData* vd, TerrainLineIntersector& intersector); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index d13a3989d..40d2c823b 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -70,7 +70,7 @@ public: float halfSize = node->getSize()/2; const osg::Vec2f& center = node->getCenter(); osg::Vec4i nodeBounds (static_cast(center.x() - halfSize), static_cast(center.y() - halfSize), static_cast(center.x() + halfSize), static_cast(center.y() + halfSize)); - bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) <= std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) <= std::min(nodeBounds.w(), mActiveGrid.w())); + bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) < std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) < std::min(nodeBounds.w(), mActiveGrid.w())); // to prevent making chunks who will cross the activegrid border if (intersects) return false; @@ -430,21 +430,8 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) if (isCullVisitor) { osgUtil::CullVisitor* cv = static_cast(&nv); - - osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer(); - if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod") - { - std::istringstream stream(udc->getDescriptions()[1]); - int x,y; - stream >> x; - stream >> y; - mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5,y+0.5)); - } - else - { - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); - mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); - } + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); + mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); } else { @@ -517,23 +504,6 @@ void QuadTreeWorld::enable(bool enabled) mRootNode->setNodeMask(enabled ? ~0 : 0); } -void QuadTreeWorld::cacheCell(View *view, int x, int y) -{ - ensureQuadTreeBuilt(); - osg::Vec4i grid (x,y,x+1,y+1); - ViewData* vd = static_cast(view); - vd->setActiveGrid(grid); - mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5f,y+0.5f)); - - const float cellWorldSize = mStorage->getCellWorldSize(); - - for (unsigned int i=0; igetNumEntries(); ++i) - { - ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); - } -} - View* QuadTreeWorld::createView() { return new ViewData; diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 201054852..15ef0634a 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -31,7 +31,7 @@ namespace Terrain virtual void setViewDistance(float distance) { mViewDistance = distance; } - void cacheCell(View *view, int x, int y); + void cacheCell(View *view, int x, int y) {} /// @note Not thread safe. virtual void loadCell(int x, int y); /// @note Not thread safe. From 4dccabeb83231557a353b22588043e32366ec062 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Wed, 6 May 2020 13:37:00 +0000 Subject: [PATCH 192/227] fix analyzation not taking instancecount in account + settings calibration Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 7 +++++++ files/settings-default.cfg | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index dc84ccdd2..d30bd7a63 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -264,6 +264,11 @@ namespace MWRender mCurrentStateSet = nullptr; return result; } + void addInstance(const Result& result) + { + for (auto pair : result.mStateSetCounter) + mGlobalStateSetCounter[pair.first] += pair.second; + } float getMergeBenefit(const Result& result) { if (result.mStateSetCounter.empty()) return 1; @@ -445,6 +450,8 @@ namespace MWRender emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); emplaced.first->second.mNeedCompile = compile && cnode->referenceCount() <= 3; } + else + analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult); emplaced.first->second.mInstances.push_back(&ref); } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index f250b68aa..b8c151e56 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -110,16 +110,16 @@ max composite geometry size = 4.0 object paging = true # Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory. -object paging merge factor = 1500 +object paging merge factor = 250 # Cull objects smaller than this size divided by distance object paging min size = 0.01 # Adjusts 'min size' based on merging decision. Allows inexpensive objects to be rendered from a greater distance. -object paging min size merge factor = 0.6 +object paging min size merge factor = 0.3 # Controls how inexpensive an object needs to be to utilize 'min size merge factor'. -object paging min size cost multiplier = 4 +object paging min size cost multiplier = 25 # Assign a random color to merged batches. object paging debug batches = false From da92ad329b23c91f4baa7d36b00e7b59a374d25b Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Thu, 7 May 2020 13:37:00 +0000 Subject: [PATCH 193/227] move renderbin Signed-off-by: Bret Curtis --- components/terrain/chunkmanager.cpp | 9 ++++++++- components/terrain/chunkmanager.hpp | 2 ++ components/terrain/material.cpp | 25 +++++++++++++------------ components/terrain/terraindrawable.cpp | 6 ++++++ components/terrain/world.cpp | 7 ------- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 5f80b9d36..3c3bd0f9d 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -31,7 +32,11 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T , mCompositeMapLevel(1.f) , mMaxCompGeometrySize(1.f) { - + mMultiPassRoot = new osg::StateSet; + mMultiPassRoot->setRenderingHint(osg::StateSet::OPAQUE_BIN); + osg::ref_ptr material (new osg::Material); + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + mMultiPassRoot->setAttributeAndModes(material, osg::StateAttribute::ON); } osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) @@ -196,6 +201,8 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->createClusterCullingCallback(); + geometry->setStateSet(mMultiPassRoot); + if (useCompositeMap) { osg::ref_ptr compositeMap = new CompositeMap; diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 87770fafb..11e5769de 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -65,6 +65,8 @@ namespace Terrain CompositeMapRenderer* mCompositeMapRenderer; BufferCache mBufferCache; + osg::ref_ptr mMultiPassRoot; + unsigned int mNodeMask; unsigned int mCompositeMapSize; diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index fd385e793..e662f4439 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -183,17 +183,20 @@ namespace Terrain osg::ref_ptr stateset (new osg::StateSet); - stateset->setMode(GL_BLEND, osg::StateAttribute::ON); - - if (!firstLayer) + if (!blendmaps.empty()) { - stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON); - stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON); - } - else - { - stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON); - stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); + stateset->setRenderBinDetails(passIndex++, "RenderBin"); + if (!firstLayer) + { + stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON); + } + else + { + stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); + } } int texunit = 0; @@ -268,8 +271,6 @@ namespace Terrain } - stateset->setRenderBinDetails(passIndex++, "RenderBin"); - passes.push_back(stateset); } return passes; diff --git a/components/terrain/terraindrawable.cpp b/components/terrain/terraindrawable.cpp index 9593687cf..0d82be4ff 100644 --- a/components/terrain/terraindrawable.cpp +++ b/components/terrain/terraindrawable.cpp @@ -102,6 +102,10 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) bool pushedLight = mLightListCallback && mLightListCallback->pushLightState(this, cv); + osg::StateSet* stateset = getStateSet(); + if (stateset) + cv->pushStateSet(stateset); + for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it) { cv->pushStateSet(*it); @@ -109,6 +113,8 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) cv->popStateSet(); } + if (stateset) + cv->popStateSet(); if (pushedLight) cv->popStateSet(); } diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index fcacb442a..5b4807b38 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -1,7 +1,6 @@ #include "world.hpp" #include -#include #include #include @@ -23,12 +22,6 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); - mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); - - osg::ref_ptr material (new osg::Material); - material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); - mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); - mTerrainRoot->setName("Terrain Root"); osg::ref_ptr compositeCam = new osg::Camera; From ffbed7ee38dc7fe7afb1a85a98bef241f83d69a5 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Thu, 7 May 2020 13:37:00 +0000 Subject: [PATCH 194/227] loadingscreen Signed-off-by: Bret Curtis --- apps/openmw/mwworld/cellpreloader.cpp | 52 +++++++++++++++++++++++---- apps/openmw/mwworld/cellpreloader.hpp | 3 ++ apps/openmw/mwworld/scene.cpp | 28 +++++++++++++++ apps/openmw/mwworld/scene.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 3 +- components/terrain/quadtreeworld.cpp | 8 +++-- components/terrain/quadtreeworld.hpp | 2 +- components/terrain/world.hpp | 2 +- 8 files changed, 88 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 60b9a3220..8f61673b3 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -174,6 +174,8 @@ namespace MWWorld public: TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) : mAbort(false) + , mProgress(views.size()) + , mProgressRange(0) , mTerrainViews(views) , mWorld(world) , mPreloadPositions(preloadPositions) @@ -191,7 +193,7 @@ namespace MWWorld for (unsigned int i=0; ireset(); - mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort); + mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort, mProgress[i], mProgressRange); } } @@ -200,8 +202,13 @@ namespace MWWorld mAbort = true; } + int getProgress() const { return !mProgress.empty() ? mProgress[0].load() : 0; } + int getProgressRange() const { return !mProgress.empty() && mProgress[0].load() ? mProgressRange : 0; } + private: std::atomic mAbort; + std::vector> mProgress; + int mProgressRange; std::vector > mTerrainViews; Terrain::World* mWorld; std::vector mPreloadPositions; @@ -328,9 +335,6 @@ namespace MWWorld } mPreloadCells.erase(found); - - if (cell->isExterior() && mTerrainPreloadItem && mTerrainPreloadItem->isDone()) - mTerrainPreloadItem->storeViews(0.0); } } @@ -415,6 +419,38 @@ namespace MWWorld mUnrefQueue = unrefQueue; } + bool CellPreloader::getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp) + { + if (!mTerrainPreloadItem) + return false; + else if (mTerrainPreloadItem->isDone()) + { + mTerrainPreloadItem->storeViews(timestamp); + mTerrainPreloadItem = nullptr; + return false; + } + else + { + progress = mTerrainPreloadItem->getProgress(); + progressRange = mTerrainPreloadItem->getProgressRange(); + return !progress || progress < progressRange; + } + } + + void CellPreloader::abortTerrainPreloadExcept(const osg::Vec3f& exceptPos) + { + if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) + { + const float resetThreshold = ESM::Land::REAL_SIZE; + for (auto pos : mTerrainPreloadPositions) + if ((pos.first-exceptPos).length2() < resetThreshold*resetThreshold) + return; + mTerrainPreloadItem->abort(); + mTerrainPreloadItem->waitTillDone(); + mTerrainPreloadItem = nullptr; + } + } + void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) @@ -436,8 +472,12 @@ namespace MWWorld } mTerrainPreloadPositions = positions; - mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); - mWorkQueue->addWorkItem(mTerrainPreloadItem); + + if (!positions.empty()) + { + mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); + mWorkQueue->addWorkItem(mTerrainPreloadItem); + } } } diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index ef2817019..386fee293 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -72,6 +72,9 @@ namespace MWWorld typedef std::pair PositionCellGrid; void setTerrainPreloadPositions(const std::vector& positions); + bool getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp); + void abortTerrainPreloadExcept(const osg::Vec3f& exceptPos); + private: Resource::ResourceSystem* mResourceSystem; Resource::BulletShapeManager* mBulletShapeManager; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 1dac18eaa..f8c16ca37 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -849,15 +849,42 @@ namespace MWWorld if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); + preloadTerrain(position.asVec3()); + changeCellGrid(x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); + checkTerrainLoaded(); + if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } + void Scene::checkTerrainLoaded() + { + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); + int progress = 0, initialProgress = -1, progressRange = 0; + while (mPreloader->getTerrainPreloadInProgress(progress, progressRange, mRendering.getReferenceTime())) + { + if (initialProgress == -1) + { + loadingListener->setLabel("#{sLoadingMessage4}"); + initialProgress = progress; + } + if (progress) + { + loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); + loadingListener->setProgress(progress-initialProgress); + } + else + loadingListener->setProgress(0); + OpenThreads::Thread::microSleep(5000); + } + } + CellStore* Scene::getCurrentCell () { return mCurrentCell; @@ -1102,6 +1129,7 @@ namespace MWWorld void Scene::preloadTerrain(const osg::Vec3f &pos) { + mPreloader->abortTerrainPreloadExcept(pos); std::vector vec; vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); mPreloader->setTerrainPreloadPositions(vec); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 9b8403c38..349dce423 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -101,6 +101,8 @@ namespace MWWorld void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + void checkTerrainLoaded(); + osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0817a4226..5f4f9d1cd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -243,7 +243,6 @@ namespace MWWorld if (bypass && !mStartCell.empty()) { ESM::Position pos; - if (findExteriorPosition (mStartCell, pos)) { changeToExteriorCell (pos, true); @@ -384,9 +383,9 @@ namespace MWWorld mPlayer->readRecord(reader, type); if (getPlayerPtr().isInCell()) { - mWorldScene->preloadCell(getPlayerPtr().getCell(), true); if (getPlayerPtr().getCell()->isExterior()) mWorldScene->preloadTerrain(getPlayerPtr().getRefData().getPosition().asVec3()); + mWorldScene->preloadCell(getPlayerPtr().getCell(), true); } break; default: diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 40d2c823b..cdb9e6191 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -509,7 +509,7 @@ View* QuadTreeWorld::createView() return new ViewData; } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort, std::atomic &progress, int& progressTotal) { ensureQuadTreeBuilt(); @@ -519,12 +519,16 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, grid); mRootNode->traverseNodes(vd, viewPoint, &lodCallback, mViewDistance); - const float cellWorldSize = mStorage->getCellWorldSize(); + if (!progressTotal) + for (unsigned int i=0; igetNumEntries(); ++i) + progressTotal += vd->getEntry(i).mNode->getSize(); + const float cellWorldSize = mStorage->getCellWorldSize(); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); + progress += entry.mNode->getSize(); } vd->markUnchanged(); } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 15ef0634a..97fcd004d 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -38,7 +38,7 @@ namespace Terrain virtual void unloadCell(int x, int y); View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort); + void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); void storeView(const View* view, double referenceTime); void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index e55932169..dba79994a 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -147,7 +147,7 @@ namespace Terrain /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. - virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort) {} + virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange) {} /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// @note Not thread safe. From c1ebd9474bdafb7c252428c83927802eac09b269 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 8 May 2020 13:37:00 +0000 Subject: [PATCH 195/227] stop navmesh updates when ai off Signed-off-by: Bret Curtis --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 8 ++++++++ components/detournavigator/navigator.hpp | 5 +++++ components/detournavigator/navigatorimpl.cpp | 8 ++++++++ components/detournavigator/navigatorimpl.hpp | 3 +++ components/detournavigator/navigatorstub.hpp | 2 ++ 5 files changed, 26 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5f5e7d13b..6b175239e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include #include "../mwworld/esmstore.hpp" @@ -900,6 +902,12 @@ namespace MWMechanics bool MechanicsManager::toggleAI() { mAI = !mAI; + + MWBase::World* world = MWBase::Environment::get().getWorld(); + world->getNavigator()->setUpdatesEnabled(mAI); + if (mAI) + world->getNavigator()->update(world->getPlayerPtr().getRefData().getPosition().asVec3()); + return mAI; } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index cfdf92232..fa2faf383 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -155,6 +155,11 @@ namespace DetourNavigator */ virtual void update(const osg::Vec3f& playerPosition) = 0; + /** + * @brief disable navigator updates + */ + virtual void setUpdatesEnabled(bool enabled) = 0; + /** * @brief wait locks thread until all tiles are updated from last update call. */ diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index d7889629e..c47cf9766 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -12,6 +12,7 @@ namespace DetourNavigator NavigatorImpl::NavigatorImpl(const Settings& settings) : mSettings(settings) , mNavMeshManager(mSettings) + , mUpdatesEnabled(true) { } @@ -138,11 +139,18 @@ namespace DetourNavigator void NavigatorImpl::update(const osg::Vec3f& playerPosition) { + if (!mUpdatesEnabled) + return; removeUnusedNavMeshes(); for (const auto& v : mAgents) mNavMeshManager.update(playerPosition, v.first); } + void NavigatorImpl::setUpdatesEnabled(bool enabled) + { + mUpdatesEnabled = enabled; + } + void NavigatorImpl::wait() { mNavMeshManager.wait(); diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 66a4d8bb3..f99aab05a 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -46,6 +46,8 @@ namespace DetourNavigator void update(const osg::Vec3f& playerPosition) override; + void setUpdatesEnabled(bool enabled) override; + void wait() override; SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const override; @@ -61,6 +63,7 @@ namespace DetourNavigator private: Settings mSettings; NavMeshManager mNavMeshManager; + bool mUpdatesEnabled; std::map mAgents; std::unordered_map mAvoidIds; std::unordered_map mWaterIds; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index 9c379338f..9279e77e3 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -66,6 +66,8 @@ namespace DetourNavigator void update(const osg::Vec3f& /*playerPosition*/) override {} + void setUpdatesEnabled(bool enabled) override {} + void wait() override {} SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override From 17637c65759fd93008672dc1704dd0dd05783fea Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 8 May 2020 13:37:00 +0000 Subject: [PATCH 196/227] pagerebuild on disable Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 47 ++++++++++++++++++++--- apps/openmw/mwrender/objectpaging.hpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 6 +-- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 10 ++++- components/resource/objectcache.hpp | 4 +- components/terrain/quadtreeworld.cpp | 7 ++++ components/terrain/quadtreeworld.hpp | 1 + components/terrain/texturemanager.cpp | 2 +- components/terrain/viewdata.cpp | 20 ++++++++++ components/terrain/viewdata.hpp | 2 + components/terrain/world.hpp | 2 + 12 files changed, 91 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index d30bd7a63..eb71f0993 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -576,17 +576,52 @@ namespace MWRender return Mask_Static; } - void ObjectPaging::enableObject(const ESM::RefNum & refnum, bool enabled) + struct ClearCacheFunctor { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (enabled) mDisabled.erase(refnum); - else mDisabled.insert(refnum); + void operator()(MWRender::ChunkId id, osg::Object* obj) + { + if (intersects(id, mPosition)) + mToClear.insert(id); + } + bool intersects(ChunkId id, osg::Vec3f pos) + { + pos /= ESM::Land::REAL_SIZE; + osg::Vec2f center = std::get<0>(id); + float halfSize = std::get<1>(id)/2; + return pos.x() >= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize; + } + osg::Vec3f mPosition; + std::set mToClear; + }; + + bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) + { + if (!typeFilter(type, false)) + return false; + + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (enabled && !mDisabled.erase(refnum)) return false; + if (!enabled && !mDisabled.insert(refnum).second) return false; + } + + ClearCacheFunctor ccf; + ccf.mPosition = pos; + mCache->call(ccf); + for (auto chunk : ccf.mToClear) + mCache->removeFromObjectCache(chunk); + return true; } void ObjectPaging::clear() { - OpenThreads::ScopedLock lock(mDisabledMutex); - mDisabled.clear(); + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (mDisabled.empty()) + return; + mDisabled.clear(); + } + mCache->clear(); } void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 57121ae76..93423f21e 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -33,7 +33,9 @@ namespace MWRender virtual unsigned int getNodeMask() override; - void enableObject(const ESM::RefNum & refnum, bool enabled); + /// @return true if something changed + bool enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled); + void clear(); void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a545e0674..9d435a612 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1481,9 +1481,9 @@ namespace MWRender { mTerrain->setActiveGrid(grid); } - void RenderingManager::pagingEnableObject(const ESM::RefNum & refnum, bool enabled) + void RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) { - if (mObjectPaging) - mObjectPaging->enableObject(refnum, enabled); + if (mObjectPaging && mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getRefData().getPosition().asVec3(), enabled)) + mTerrain->clearCachedViews(ptr.getRefData().getPosition().asVec3()); } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 6bf122232..36df09202 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -241,7 +241,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); - void pagingEnableObject(const ESM::RefNum & refnum, bool enabled); + void pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); private: void updateProjectionMatrix(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5f4f9d1cd..fdb54fb75 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -815,7 +815,10 @@ namespace MWWorld mWorldScene->addObjectToScene (reference); if (reference.getCellRef().getRefNum().hasContentFile()) - mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), true); + { + int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); + mRendering->pagingEnableObject(type, reference, true); + } } } @@ -853,7 +856,10 @@ namespace MWWorld reference.getRefData().disable(); if (reference.getCellRef().getRefNum().hasContentFile()) - mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), false); + { + int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); + mRendering->pagingEnableObject(type, reference, false); + } if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->removeObjectFromScene (reference); diff --git a/components/resource/objectcache.hpp b/components/resource/objectcache.hpp index cfd41f19c..24d7d04a9 100644 --- a/components/resource/objectcache.hpp +++ b/components/resource/objectcache.hpp @@ -161,13 +161,13 @@ class GenericObjectCache : public osg::Referenced } } - /** call operator()(osg::Object*) for each object in the cache. */ + /** call operator()(KeyType, osg::Object*) for each object in the cache. */ template void call(Functor& f) { OpenThreads::ScopedLock lock(_objectCacheMutex); for (typename ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it) - f(it->second.first.get()); + f(it->first, it->second.first.get()); } /** Get the number of objects in the cache. */ diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index cdb9e6191..92bd09bd0 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -574,4 +574,11 @@ void QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m) mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask()); } +void QuadTreeWorld::clearCachedViews(const osg::Vec3f &pos) +{ +//mViewDataMap->clear(); + osg::Vec3 pos_ = pos / mStorage->getCellWorldSize(); + mViewDataMap->clearCachedViews(pos_); +} + } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 97fcd004d..7c01f5c27 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -40,6 +40,7 @@ namespace Terrain View* createView(); void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); void storeView(const View* view, double referenceTime); + void clearCachedViews(const osg::Vec3f& pos) override; void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/texturemanager.cpp b/components/terrain/texturemanager.cpp index b901fa159..c28b13f1d 100644 --- a/components/terrain/texturemanager.cpp +++ b/components/terrain/texturemanager.cpp @@ -25,7 +25,7 @@ struct UpdateTextureFilteringFunctor } Resource::SceneManager* mSceneManager; - void operator()(osg::Object* obj) + void operator()(std::string, osg::Object* obj) { mSceneManager->applyFilterSettings(static_cast(obj)); } diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 6399425a4..e5e856cad 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -1,5 +1,7 @@ #include "viewdata.hpp" +#include "quadtreenode.hpp" + namespace Terrain { @@ -99,6 +101,18 @@ bool ViewData::contains(QuadTreeNode *node) return false; } +bool intersects(const osg::Vec2f& center, float halfSize, osg::Vec3f pos) +{ + return (pos.x() >= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize); +} + +void ViewData::clearCache(const osg::Vec3f &cellPos) +{ + for (Entry& entry : mEntries) + if (entry.mNode && intersects(entry.mNode->getCenter(), entry.mNode->getSize()/2.f, cellPos)) + entry.mRenderingNode = nullptr; +} + ViewData::Entry::Entry() : mNode(nullptr) , mLodFlags(0) @@ -188,6 +202,12 @@ void ViewDataMap::clearUnusedViews(double referenceTime) } } +void ViewDataMap::clearCachedViews(const osg::Vec3f &cellPos) +{ + for (auto pair : mViews) + pair.second->clearCache(cellPos); +} + void ViewDataMap::clear() { mViews.clear(); diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 87b45f57b..f21f56ae1 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -24,6 +24,7 @@ namespace Terrain void reset(); void clear(); + void clearCache(const osg::Vec3f &cellPos); bool contains(QuadTreeNode* node); @@ -84,6 +85,7 @@ namespace Terrain ViewData* createOrReuseView(); void clearUnusedViews(double referenceTime); + void clearCachedViews(const osg::Vec3f &cellPos); void clear(); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index dba79994a..4ad4e5f0a 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -153,6 +153,8 @@ namespace Terrain /// @note Not thread safe. virtual void storeView(const View* view, double referenceTime) {} + virtual void clearCachedViews(const osg::Vec3f& pos) {} + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} virtual void setViewDistance(float distance) {} From b4af2ac6725430b9547006c87ee0daed2be73b93 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 8 May 2020 13:37:00 +0000 Subject: [PATCH 197/227] avoid blocking on pagerebuild Signed-off-by: Bret Curtis --- apps/openmw/mwrender/renderingmanager.cpp | 10 +- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/cellpreloader.cpp | 44 ++++++-- apps/openmw/mwworld/cellpreloader.hpp | 1 + apps/openmw/mwworld/scene.cpp | 5 + apps/openmw/mwworld/scene.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 6 +- components/terrain/quadtreenode.hpp | 1 - components/terrain/quadtreeworld.cpp | 20 ++-- components/terrain/quadtreeworld.hpp | 4 +- components/terrain/viewdata.cpp | 117 ++++++++++++---------- components/terrain/viewdata.hpp | 24 +++-- components/terrain/world.hpp | 4 +- 13 files changed, 150 insertions(+), 89 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9d435a612..569f40483 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1481,9 +1481,15 @@ namespace MWRender { mTerrain->setActiveGrid(grid); } - void RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) + bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) { + if (!ptr.isInCell() || !ptr.getCell()->isExterior()) + return false; if (mObjectPaging && mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getRefData().getPosition().asVec3(), enabled)) - mTerrain->clearCachedViews(ptr.getRefData().getPosition().asVec3()); + { + mTerrain->rebuildViews(); + return true; + } + return false; } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 36df09202..b45e3c8dc 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -241,7 +241,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); - void pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); + bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); private: void updateProjectionMatrix(); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 8f61673b3..4280e1e68 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -182,10 +182,12 @@ namespace MWWorld { } - void storeViews(double referenceTime) + bool storeViews(double referenceTime) { for (unsigned int i=0; istoreView(mTerrainViews[i], referenceTime); + if (!mWorld->storeView(mTerrainViews[i], referenceTime)) + return false; + return true; } virtual void doWork() @@ -244,6 +246,7 @@ namespace MWWorld , mMaxCacheSize(0) , mPreloadInstances(true) , mLastResourceCacheUpdate(0.0) + , mStoreViewsFailCount(0) { } @@ -379,7 +382,17 @@ namespace MWWorld if (mTerrainPreloadItem && mTerrainPreloadItem->isDone()) { - mTerrainPreloadItem->storeViews(timestamp); + if (!mTerrainPreloadItem->storeViews(timestamp)) + { + if (++mStoreViewsFailCount > 100) + { + OSG_ALWAYS << "paging views are rebuilt every frame, please check for faulty enable/disable scripts." << std::endl; + mStoreViewsFailCount = 0; + } + setTerrainPreloadPositions(std::vector()); + } + else + mStoreViewsFailCount = 0; mTerrainPreloadItem = nullptr; } } @@ -451,11 +464,31 @@ namespace MWWorld } } + bool contains(const std::vector& container, const std::vector& contained) + { + for (auto pos : contained) + { + bool found = false; + for (auto pos2 : container) + { + if ((pos.first-pos2.first).length2() < 1 && pos.second == pos2.second) + { + found = true; + break; + } + } + if (!found) return false; + } + return true; + } + void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { - if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) + if (positions.empty()) + mTerrainPreloadPositions.clear(); + else if (contains(mTerrainPreloadPositions, positions)) return; - else if (positions == mTerrainPreloadPositions) + if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) return; else { @@ -472,7 +505,6 @@ namespace MWWorld } mTerrainPreloadPositions = positions; - if (!positions.empty()) { mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 386fee293..c17078735 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -88,6 +88,7 @@ namespace MWWorld bool mPreloadInstances; double mLastResourceCacheUpdate; + int mStoreViewsFailCount; struct PreloadEntry { diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index f8c16ca37..36f877bee 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1135,6 +1135,11 @@ namespace MWWorld mPreloader->setTerrainPreloadPositions(vec); } + void Scene::reloadTerrain() + { + mPreloader->setTerrainPreloadPositions(std::vector()); + } + struct ListFastTravelDestinationsVisitor { ListFastTravelDestinationsVisitor(float preloadDist, const osg::Vec3f& playerPos) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 349dce423..5727f1391 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -115,6 +115,7 @@ namespace MWWorld void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); void preloadTerrain(const osg::Vec3f& pos); + void reloadTerrain(); void unloadCell (CellStoreCollection::iterator iter, bool test = false); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fdb54fb75..8392a827c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -817,7 +817,8 @@ namespace MWWorld if (reference.getCellRef().getRefNum().hasContentFile()) { int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); - mRendering->pagingEnableObject(type, reference, true); + if (mRendering->pagingEnableObject(type, reference, true)) + mWorldScene->reloadTerrain(); } } } @@ -858,7 +859,8 @@ namespace MWWorld if (reference.getCellRef().getRefNum().hasContentFile()) { int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); - mRendering->pagingEnableObject(type, reference, false); + if (mRendering->pagingEnableObject(type, reference, false)) + mWorldScene->reloadTerrain(); } if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 74abea362..147ca8c8a 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -53,7 +53,6 @@ namespace Terrain virtual bool isSufficientDetail(QuadTreeNode *node, float dist) = 0; }; - class ViewDataMap; class ViewData; class QuadTreeNode : public osg::Group diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 92bd09bd0..c7544dbe2 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -253,7 +253,6 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour QuadTreeWorld::~QuadTreeWorld() { - mViewDataMap->clear(); } /// get the level of vertex detail to render this node at, expressed relative to the native resolution of the data set. @@ -279,7 +278,7 @@ unsigned int getVertexLod(QuadTreeNode* node, int vertexLodMod) } /// get the flags to use for stitching in the index buffer so that chunks of different LOD connect seamlessly -unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewData* vd) +unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, const ViewData* vd) { unsigned int lodFlags = 0; for (unsigned int i=0; i<4; ++i) @@ -506,7 +505,7 @@ void QuadTreeWorld::enable(bool enabled) View* QuadTreeWorld::createView() { - return new ViewData; + return mViewDataMap->createIndependentView(); } void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort, std::atomic &progress, int& progressTotal) @@ -533,14 +532,9 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: vd->markUnchanged(); } -void QuadTreeWorld::storeView(const View* view, double referenceTime) +bool QuadTreeWorld::storeView(const View* view, double referenceTime) { - osg::ref_ptr dummy = new osg::DummyObject; - const ViewData* vd = static_cast(view); - bool needsUpdate = false; - ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), vd->getActiveGrid(), needsUpdate); - stored->copyFrom(*vd); - stored->setLastUsageTimeStamp(referenceTime); + return mViewDataMap->storeView(static_cast(view), referenceTime); } void QuadTreeWorld::reportStats(unsigned int frameNumber, osg::Stats *stats) @@ -574,11 +568,9 @@ void QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m) mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask()); } -void QuadTreeWorld::clearCachedViews(const osg::Vec3f &pos) +void QuadTreeWorld::rebuildViews() { -//mViewDataMap->clear(); - osg::Vec3 pos_ = pos / mStorage->getCellWorldSize(); - mViewDataMap->clearCachedViews(pos_); + mViewDataMap->rebuildViews(); } } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 7c01f5c27..0cd2526de 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -39,8 +39,8 @@ namespace Terrain View* createView(); void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); - void storeView(const View* view, double referenceTime); - void clearCachedViews(const osg::Vec3f& pos) override; + bool storeView(const View* view, double referenceTime); + void rebuildViews() override; void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index e5e856cad..8a8769000 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -10,6 +10,7 @@ ViewData::ViewData() , mLastUsageTimeStamp(0.0) , mChanged(false) , mHasViewPoint(false) + , mWorldUpdateRevision(0) { } @@ -27,6 +28,7 @@ void ViewData::copyFrom(const ViewData& other) mHasViewPoint = other.mHasViewPoint; mViewPoint = other.mViewPoint; mActiveGrid = other.mActiveGrid; + mWorldUpdateRevision = other.mWorldUpdateRevision; } void ViewData::add(QuadTreeNode *node) @@ -93,7 +95,12 @@ void ViewData::clear() mHasViewPoint = false; } -bool ViewData::contains(QuadTreeNode *node) +bool ViewData::suitableToUse(const osg::Vec4i &activeGrid) const +{ + return hasViewPoint() && activeGrid == mActiveGrid && getNumEntries(); +} + +bool ViewData::contains(QuadTreeNode *node) const { for (unsigned int i=0; i= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize); -} - -void ViewData::clearCache(const osg::Vec3f &cellPos) -{ - for (Entry& entry : mEntries) - if (entry.mNode && intersects(entry.mNode->getCenter(), entry.mNode->getSize()/2.f, cellPos)) - entry.mRenderingNode = nullptr; -} - ViewData::Entry::Entry() : mNode(nullptr) , mLodFlags(0) @@ -133,86 +128,106 @@ bool ViewData::Entry::set(QuadTreeNode *node) } } -bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist, const osg::Vec4i& activeGrid) -{ - return vd->hasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist && vd->getActiveGrid() == activeGrid; -} - ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate) { - Map::const_iterator found = mViews.find(viewer); + ViewerMap::const_iterator found = mViewers.find(viewer); ViewData* vd = nullptr; - if (found == mViews.end()) + if (found == mViewers.end()) { vd = createOrReuseView(); - mViews[viewer] = vd; + mViewers[viewer] = vd; } else vd = found->second; + needsUpdate = false; - if (!suitable(vd, viewPoint, mReuseDistance, activeGrid)) + if (!(vd->suitableToUse(activeGrid) && (vd->getViewPoint()-viewPoint).length2() < mReuseDistance*mReuseDistance && vd->getWorldUpdateRevision() >= mWorldUpdateRevision)) { - for (Map::const_iterator other = mViews.begin(); other != mViews.end(); ++other) + float shortestDist = std::numeric_limits::max(); + const ViewData* mostSuitableView = nullptr; + for (const ViewData& other : mViewVector) { - if (suitable(other->second, viewPoint, mReuseDistance, activeGrid) && other->second->getNumEntries()) + if (other.suitableToUse(activeGrid) && other.getWorldUpdateRevision() >= mWorldUpdateRevision) { - vd->copyFrom(*other->second); - needsUpdate = false; - return vd; + float dist = (viewPoint-other.getViewPoint()).length2(); + if (dist < shortestDist) + { + shortestDist = dist; + mostSuitableView = &other; + } } } + if (mostSuitableView && mostSuitableView != vd) + { + vd->copyFrom(*mostSuitableView); + return vd; + } + } + if (!vd->suitableToUse(activeGrid)) + { vd->setViewPoint(viewPoint); vd->setActiveGrid(activeGrid); needsUpdate = true; } - else - needsUpdate = false; - return vd; } +bool ViewDataMap::storeView(const ViewData* view, double referenceTime) +{ + if (view->getWorldUpdateRevision() < mWorldUpdateRevision) + return false; + ViewData* store = createOrReuseView(); + store->copyFrom(*view); + store->setLastUsageTimeStamp(referenceTime); + return true; +} + ViewData *ViewDataMap::createOrReuseView() { + ViewData* vd = nullptr; if (mUnusedViews.size()) { - ViewData* vd = mUnusedViews.front(); + vd = mUnusedViews.front(); mUnusedViews.pop_front(); - return vd; } else { mViewVector.push_back(ViewData()); - return &mViewVector.back(); + vd = &mViewVector.back(); } + vd->setWorldUpdateRevision(mWorldUpdateRevision); + return vd; +} + +ViewData *ViewDataMap::createIndependentView() const +{ + ViewData* vd = new ViewData; + vd->setWorldUpdateRevision(mWorldUpdateRevision); + return vd; } void ViewDataMap::clearUnusedViews(double referenceTime) { - for (Map::iterator it = mViews.begin(); it != mViews.end(); ) + for (ViewerMap::iterator it = mViewers.begin(); it != mViewers.end(); ) { - ViewData* vd = it->second; - if (vd->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) - { - vd->clear(); - mUnusedViews.push_back(vd); - mViews.erase(it++); - } + if (it->second->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) + mViewers.erase(it++); else ++it; } + for (ViewData& vd : mViewVector) + { + if (vd.getLastUsageTimeStamp() + mExpiryDelay < referenceTime) + { + vd.clear(); + mUnusedViews.push_back(&vd); + } + } } -void ViewDataMap::clearCachedViews(const osg::Vec3f &cellPos) -{ - for (auto pair : mViews) - pair.second->clearCache(cellPos); -} - -void ViewDataMap::clear() +void ViewDataMap::rebuildViews() { - mViews.clear(); - mUnusedViews.clear(); - mViewVector.clear(); + ++mWorldUpdateRevision; } } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index f21f56ae1..ba4fba3b2 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -23,10 +23,11 @@ namespace Terrain void reset(); + bool suitableToUse(const osg::Vec4i& activeGrid) const; + void clear(); - void clearCache(const osg::Vec3f &cellPos); - bool contains(QuadTreeNode* node); + bool contains(QuadTreeNode* node) const; void copyFrom(const ViewData& other); @@ -61,6 +62,9 @@ namespace Terrain void setActiveGrid(const osg::Vec4i &grid) { if (grid != mActiveGrid) {mActiveGrid = grid;mEntries.clear();mNumEntries=0;} } const osg::Vec4i &getActiveGrid() const { return mActiveGrid;} + unsigned int getWorldUpdateRevision() const { return mWorldUpdateRevision; } + void setWorldUpdateRevision(int updateRevision) { mWorldUpdateRevision = updateRevision; } + private: std::vector mEntries; unsigned int mNumEntries; @@ -69,35 +73,39 @@ namespace Terrain osg::Vec3f mViewPoint; bool mHasViewPoint; osg::Vec4i mActiveGrid; + unsigned int mWorldUpdateRevision; }; class ViewDataMap : public osg::Referenced { public: ViewDataMap() - : mReuseDistance(300) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused. + : mReuseDistance(150) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused. // this value also serves as a threshold for when a newly loaded LOD gets unloaded again so that if you hover around an LOD transition point the LODs won't keep loading and unloading all the time. , mExpiryDelay(1.f) + , mWorldUpdateRevision(0) {} ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate); ViewData* createOrReuseView(); + ViewData* createIndependentView() const; void clearUnusedViews(double referenceTime); - void clearCachedViews(const osg::Vec3f &cellPos); - - void clear(); + void rebuildViews(); + bool storeView(const ViewData* view, double referenceTime); private: std::list mViewVector; - typedef std::map, ViewData*> Map; - Map mViews; + typedef std::map, ViewData*> ViewerMap; + ViewerMap mViewers; float mReuseDistance; float mExpiryDelay; // time in seconds for unused view to be removed + unsigned int mWorldUpdateRevision; + std::deque mUnusedViews; }; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 4ad4e5f0a..9f08454c8 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -151,9 +151,9 @@ namespace Terrain /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// @note Not thread safe. - virtual void storeView(const View* view, double referenceTime) {} + virtual bool storeView(const View* view, double referenceTime) {return true;} - virtual void clearCachedViews(const osg::Vec3f& pos) {} + virtual void rebuildViews() {} virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} From c7fda6d28004fa9be927c0f48d4cede21773d991 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sun, 10 May 2020 13:37:00 +0000 Subject: [PATCH 198/227] activegrid paging = 2xfps Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 86 +++++++++++++++++++---- apps/openmw/mwrender/objectpaging.hpp | 9 ++- apps/openmw/mwrender/renderingmanager.cpp | 5 ++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellpreloader.cpp | 4 +- apps/openmw/mwworld/cellpreloader.hpp | 2 +- apps/openmw/mwworld/scene.cpp | 48 ++++++++----- apps/openmw/mwworld/scene.hpp | 2 + components/terrain/quadtreenode.cpp | 18 ++--- components/terrain/quadtreenode.hpp | 10 ++- components/terrain/quadtreeworld.cpp | 35 +++++---- components/terrain/quadtreeworld.hpp | 1 - files/settings-default.cfg | 5 +- 13 files changed, 158 insertions(+), 68 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index eb71f0993..9b3165d87 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -32,16 +33,19 @@ namespace MWRender { - bool typeFilter(int type, bool far) + bool typeFilter(int type, bool far, bool activeGrid) { switch (type) { case ESM::REC_STAT: - case ESM::REC_ACTI: - case ESM::REC_DOOR: - return true; + return true; + + case ESM::REC_ACTI: // TODO enable when intersectionvisitor supported + case ESM::REC_DOOR: + return !activeGrid; case ESM::REC_CONT: - return far ? false : true; + return far ? false : !activeGrid; + default: return false; } @@ -64,17 +68,19 @@ namespace MWRender } } - osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) + osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) { - if (!far)return nullptr; - ChunkId id = std::make_tuple(center, size); + if (activeGrid && !mActiveGrid) + return nullptr; + + ChunkId id = std::make_tuple(center, size, activeGrid); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); if (obj) return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, viewPoint, compile); + osg::ref_ptr node = createChunk(size, center, activeGrid, viewPoint, compile); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -231,6 +237,15 @@ namespace MWRender std::vector> mObjects; }; + class RefnumSet : public osg::Object + { + public: + RefnumSet(){} + RefnumSet(const RefnumSet& copy, const osg::CopyOp&) : mRefnums(copy.mRefnums) {} + META_Object(MWRender, RefnumSet) + std::set mRefnums; + }; + class AnalyzeVisitor : public osg::NodeVisitor { public: @@ -310,6 +325,7 @@ namespace MWRender : GenericResourceManager(nullptr) , mSceneManager(sceneManager) { + mActiveGrid = Settings::Manager::getBool("object paging active grid", "Terrain"); mDebugBatches = Settings::Manager::getBool("object paging debug batches", "Terrain"); mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); @@ -317,7 +333,7 @@ namespace MWRender mMinSizeCostMultiplier = Settings::Manager::getFloat("object paging min size cost multiplier", "Terrain"); } - osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile) + osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) { osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f)); @@ -349,7 +365,7 @@ namespace MWRender { if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2)) continue; + if (!typeFilter(type,size>=2,activeGrid)) continue; if (deleted) { refs.erase(ref.mRefNum); continue; } refs[ref.mRefNum] = ref; } @@ -365,7 +381,7 @@ namespace MWRender bool deleted = it->second; if (deleted) { refs.erase(ref.mRefNum); continue; } int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2)) continue; + if (!typeFilter(type,size>=2,activeGrid)) continue; refs[ref.mRefNum] = ref; } } @@ -387,6 +403,7 @@ namespace MWRender }; typedef std::map, InstanceList> NodeMap; NodeMap nodes; + osg::ref_ptr refnumSet = activeGrid ? new RefnumSet : nullptr; AnalyzeVisitor analyzeVisitor; float minSize = mMinSize; if (mMinSizeMergeFactor) @@ -443,6 +460,9 @@ namespace MWRender continue; } + if (activeGrid && cnode->getNumChildrenRequiringUpdateTraversal() > 0) + continue; + auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { @@ -453,6 +473,9 @@ namespace MWRender else analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult); emplaced.first->second.mInstances.push_back(&ref); + + if (activeGrid) + refnumSet->mRefnums.insert(pair.first); } osg::ref_ptr group = new osg::Group; @@ -566,7 +589,13 @@ namespace MWRender group->getBound(); group->setNodeMask(Mask_Static); - group->getOrCreateUserDataContainer()->addUserObject(templateRefs); + osg::UserDataContainer* udc = group->getOrCreateUserDataContainer(); + if (activeGrid) + { + udc->addUserObject(refnumSet); + group->addCullCallback(new SceneUtil::LightListCallback); + } + udc->addUserObject(templateRefs); return group; } @@ -596,7 +625,7 @@ namespace MWRender bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) { - if (!typeFilter(type, false)) + if (!typeFilter(type, false, false)) return false; { @@ -624,6 +653,35 @@ namespace MWRender mCache->clear(); } + struct GetRefnumsFunctor + { + GetRefnumsFunctor(std::set& output) : mOutput(output) {} + void operator()(MWRender::ChunkId chunkId, osg::Object* obj) + { + if (!std::get<2>(chunkId)) return; + const osg::Vec2f& center = std::get<0>(chunkId); + bool activeGrid = (center.x() > mActiveGrid.x() || center.y() > mActiveGrid.y() || center.x() < mActiveGrid.z() || center.y() < mActiveGrid.w()); + if (!activeGrid) return; + + osg::UserDataContainer* udc = obj->getUserDataContainer(); + if (udc && udc->getNumUserObjects()) + { + RefnumSet* refnums = dynamic_cast(udc->getUserObject(0)); + if (!refnums) return; + mOutput.insert(refnums->mRefnums.begin(), refnums->mRefnums.end()); + } + } + osg::Vec4i mActiveGrid; + std::set& mOutput; + }; + + void ObjectPaging::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) + { + GetRefnumsFunctor grf(out); + grf.mActiveGrid = activeGrid; + mCache->call(grf); + } + void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const { stats->setAttribute(frameNumber, "Object Chunk", mCache->getCacheSize()); diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 93423f21e..79591dcc4 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -19,7 +19,7 @@ namespace MWWorld namespace MWRender { - typedef std::tuple ChunkId; // Center, Size + typedef std::tuple ChunkId; // Center, Size, ActiveGrid class ObjectPaging : public Resource::GenericResourceManager, public Terrain::QuadTreeWorld::ChunkManager { @@ -27,9 +27,9 @@ namespace MWRender ObjectPaging(Resource::SceneManager* sceneManager); ~ObjectPaging() = default; - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) override; + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override; - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile); virtual unsigned int getNodeMask() override; @@ -40,8 +40,11 @@ namespace MWRender void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; + void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); + private: Resource::SceneManager* mSceneManager; + bool mActiveGrid; bool mDebugBatches; float mMergeFactor; float mMinSize; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 569f40483..5523a6936 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1492,4 +1492,9 @@ namespace MWRender } return false; } + void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) + { + if (mObjectPaging) + mObjectPaging->getPagedRefnums(activeGrid, out); + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b45e3c8dc..3082866c4 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -242,6 +242,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); + void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); private: void updateProjectionMatrix(); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 4280e1e68..eb48424c5 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -450,13 +450,13 @@ namespace MWWorld } } - void CellPreloader::abortTerrainPreloadExcept(const osg::Vec3f& exceptPos) + void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid& exceptPos) { if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) { const float resetThreshold = ESM::Land::REAL_SIZE; for (auto pos : mTerrainPreloadPositions) - if ((pos.first-exceptPos).length2() < resetThreshold*resetThreshold) + if ((pos.first-exceptPos.first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos.second) return; mTerrainPreloadItem->abort(); mTerrainPreloadItem->waitTillDone(); diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index c17078735..98e173f17 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -73,7 +73,7 @@ namespace MWWorld void setTerrainPreloadPositions(const std::vector& positions); bool getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp); - void abortTerrainPreloadExcept(const osg::Vec3f& exceptPos); + void abortTerrainPreloadExcept(const PositionCellGrid &exceptPos); private: Resource::ResourceSystem* mResourceSystem; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 36f877bee..bd9a92dae 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -87,7 +88,7 @@ namespace } void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, - MWRender::RenderingManager& rendering) + MWRender::RenderingManager& rendering, std::set& pagedRefs) { if (ptr.getRefData().getBaseNode() || physics.getActor(ptr)) { @@ -104,7 +105,11 @@ namespace if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - ptr.getClass().insertObjectRendering(ptr, model, rendering); + const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); + if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end()) + ptr.getClass().insertObjectRendering(ptr, model, rendering); + else + ptr.getRefData().setBaseNode(new SceneUtil::PositionAttitudeTransform); // FIXME remove this when physics code is fixed not to depend on basenode setNodeRotation(ptr, rendering, RotationOrder::direct); ptr.getClass().insertObject (ptr, model, physics); @@ -498,18 +503,14 @@ namespace MWWorld osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter); if (newCell != mCurrentGridCenter) + { + preloadTerrain(pos); changeCellGrid(newCell.x(), newCell.y()); + } } void Scene::changeCellGrid (int playerCellX, int playerCellY, bool changeEvent) { - Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - Loading::ScopedLoad load(loadingListener); - - int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); - std::string loadingExteriorText = "#{sLoadingMessage3}"; - loadingListener->setLabel(loadingExteriorText, false, messagesCount > 0); - CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) { @@ -526,6 +527,14 @@ namespace MWWorld unloadCell (active++); } + mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); + osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); + mRendering.setActiveGrid(newGrid); + + mPagedRefs.clear(); + checkTerrainLoaded(); + mRendering.getPagedRefnums(newGrid, mPagedRefs); + std::size_t refsToLoad = 0; std::vector> cellsPositionsToLoad; // get the number of refs to load @@ -554,6 +563,11 @@ namespace MWWorld } } + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); + int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); + std::string loadingExteriorText = "#{sLoadingMessage3}"; + loadingListener->setLabel(loadingExteriorText, false, messagesCount > 0); loadingListener->setProgressRange(refsToLoad); const auto getDistanceToPlayerCell = [&] (const std::pair& cellPosition) @@ -601,9 +615,6 @@ namespace MWWorld CellStore* current = MWBase::Environment::get().getWorld()->getExterior(playerCellX, playerCellY); MWBase::Environment::get().getWindowManager()->changeCell(current); - mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); - mRendering.setActiveGrid(gridCenterToBounds(mCurrentGridCenter)); - if (changeEvent) mCellChanged = true; } @@ -820,6 +831,7 @@ namespace MWWorld loadingListener->setProgressRange(cell->count()); // Load cell. + mPagedRefs.clear(); loadCell (cell, loadingListener, changeEvent); changePlayerCell(cell, position, adjustPlayerPos); @@ -850,14 +862,12 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); preloadTerrain(position.asVec3()); - + checkTerrainLoaded(); changeCellGrid(x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); - checkTerrainLoaded(); - if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } @@ -899,7 +909,7 @@ namespace MWWorld { InsertVisitor insertVisitor (cell, *loadingListener, test); cell.forEach (insertVisitor); - insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering); }); + insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs); }); insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); }); // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order @@ -911,7 +921,7 @@ namespace MWWorld { try { - addObject(ptr, *mPhysics, mRendering); + addObject(ptr, *mPhysics, mRendering, mPagedRefs); addObject(ptr, *mPhysics, mNavigator); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); @@ -943,6 +953,8 @@ namespace MWWorld mRendering.removeObject (ptr); if (ptr.getClass().isActor()) mRendering.removeWaterRippleEmitter(ptr); + ptr.getRefData().setBaseNode(nullptr); + mPagedRefs.erase(ptr.getCellRef().getRefNum()); } bool Scene::isCellActive(const CellStore &cell) @@ -1129,9 +1141,9 @@ namespace MWWorld void Scene::preloadTerrain(const osg::Vec3f &pos) { - mPreloader->abortTerrainPreloadExcept(pos); std::vector vec; vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); + mPreloader->abortTerrainPreloadExcept(vec[0]); mPreloader->setTerrainPreloadPositions(vec); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 5727f1391..b7aeafbfe 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -88,6 +88,8 @@ namespace MWWorld osg::Vec3f mLastPlayerPos; + std::set mPagedRefs; + void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false); osg::Vec2i mCurrentGridCenter; diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 1f5a3e4b3..8f47986df 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -108,24 +108,20 @@ void QuadTreeNode::initNeighbours() getChild(i)->initNeighbours(); } -void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist) +void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback) { if (!hasValidBounds()) return; - - float dist = distance(viewPoint); - if (dist > maxDist) + LodCallback::ReturnValue lodResult = lodCallback->isSufficientDetail(this, distance(viewPoint)); + if (lodResult == LodCallback::StopTraversal) return; - - bool stopTraversal = (lodCallback->isSufficientDetail(this, dist)) || !getNumChildren(); - - if (stopTraversal) - vd->add(this); - else + else if (lodResult == LodCallback::Deeper && getNumChildren()) { for (unsigned int i=0; itraverseNodes(vd, viewPoint, lodCallback, maxDist); + getChild(i)->traverseNodes(vd, viewPoint, lodCallback); } + else + vd->add(this); } void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector& intersector) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 147ca8c8a..183b5e07a 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -50,7 +50,13 @@ namespace Terrain public: virtual ~LodCallback() {} - virtual bool isSufficientDetail(QuadTreeNode *node, float dist) = 0; + enum ReturnValue + { + Deeper, + StopTraversal, + StopTraversalAndUse + }; + virtual ReturnValue isSufficientDetail(QuadTreeNode *node, float dist) = 0; }; class ViewData; @@ -97,7 +103,7 @@ namespace Terrain const osg::Vec2f& getCenter() const; /// Traverse nodes according to LOD selection. - void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist); + void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback); /// Adds all leaf nodes which intersect the line from start to end void intersect(ViewData* vd, TerrainLineIntersector& intersector); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index c7544dbe2..78d8835b5 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -53,34 +53,40 @@ namespace Terrain class DefaultLodCallback : public LodCallback { public: - DefaultLodCallback(float factor, float minSize, const osg::Vec4i& grid) + DefaultLodCallback(float factor, float minSize, float viewDistance, const osg::Vec4i& grid) : mFactor(factor) , mMinSize(minSize) + , mViewDistance(viewDistance) , mActiveGrid(grid) { } - virtual bool isSufficientDetail(QuadTreeNode* node, float dist) + virtual ReturnValue isSufficientDetail(QuadTreeNode* node, float dist) { - int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); - int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); - + const osg::Vec2f& center = node->getCenter(); + bool activeGrid = (center.x() > mActiveGrid.x() && center.y() > mActiveGrid.y() && center.x() < mActiveGrid.z() && center.y() < mActiveGrid.w()); + if (dist > mViewDistance && !activeGrid) // for Scene<->ObjectPaging sync the activegrid must remain loaded + return StopTraversal; if (node->getSize()>1) { float halfSize = node->getSize()/2; - const osg::Vec2f& center = node->getCenter(); osg::Vec4i nodeBounds (static_cast(center.x() - halfSize), static_cast(center.y() - halfSize), static_cast(center.x() + halfSize), static_cast(center.y() + halfSize)); bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) < std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) < std::min(nodeBounds.w(), mActiveGrid.w())); // to prevent making chunks who will cross the activegrid border if (intersects) - return false; + return Deeper; } - return nativeLodLevel <= lodLevel; + + int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); + int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); + + return nativeLodLevel <= lodLevel ? StopTraversalAndUse : Deeper; } private: float mFactor; float mMinSize; + float mViewDistance; osg::Vec4i mActiveGrid; }; @@ -330,11 +336,11 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, f pat->setPosition(osg::Vec3f(entry.mNode->getCenter().x()*cellWorldSize, entry.mNode->getCenter().y()*cellWorldSize, 0.f)); const osg::Vec2f& center = entry.mNode->getCenter(); - bool far = (center.x() <= gridbounds.x() || center.y() <= gridbounds.y() || center.x() >= gridbounds.z() || center.y() >= gridbounds.w()); + bool activeGrid = (center.x() > gridbounds.x() && center.y() > gridbounds.y() && center.x() < gridbounds.z() && center.y() < gridbounds.w()); for (QuadTreeWorld::ChunkManager* m : chunkManagers) { - osg::ref_ptr n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint(), compile); + osg::ref_ptr n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, activeGrid, vd->getViewPoint(), compile); if (n) pat->addChild(n); } entry.mRenderingNode = pat; @@ -428,9 +434,8 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) vd->reset(); if (isCullVisitor) { - osgUtil::CullVisitor* cv = static_cast(&nv); - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); - mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, mActiveGrid); + mRootNode->traverseNodes(vd, nv.getViewPoint(), &lodCallback); } else { @@ -515,8 +520,8 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); vd->setActiveGrid(grid); - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, grid); - mRootNode->traverseNodes(vd, viewPoint, &lodCallback, mViewDistance); + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, grid); + mRootNode->traverseNodes(vd, viewPoint, &lodCallback); if (!progressTotal) for (unsigned int i=0; igetNumEntries(); ++i) diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 0cd2526de..7b395bff6 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -15,7 +15,6 @@ namespace Terrain { class RootNode; class ViewDataMap; - class LodCallback; /// @brief Terrain implementation that loads cells into a Quad Tree, with geometry LOD and texture LOD. class QuadTreeWorld : public TerrainGrid // note: derived from TerrainGrid is only to render default cells (see loadCell) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index b8c151e56..686d65adc 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -106,9 +106,12 @@ composite map resolution = 512 # Controls the maximum size of composite geometry, should be >= 1.0. With low values there will be many small chunks, with high values - lesser count of bigger chunks. max composite geometry size = 4.0 -# Load far objects on terrain +# Use object paging for non active cells object paging = true +# Use object paging for active cells grid +object paging active grid = true + # Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory. object paging merge factor = 250 From 340d626589c661724d3c03b12117d3bd0a1825bf Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 199/227] static moving support Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 45 +++++++++++-- apps/openmw/mwrender/objectpaging.hpp | 6 +- apps/openmw/mwrender/renderingmanager.cpp | 13 +++- apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellstore.cpp | 6 ++ apps/openmw/mwworld/scene.cpp | 82 +++++++++++++---------- apps/openmw/mwworld/scene.hpp | 3 + apps/openmw/mwworld/worldimp.cpp | 29 +++++--- 8 files changed, 132 insertions(+), 53 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 9b3165d87..4c8706caf 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -389,8 +389,9 @@ namespace MWRender { OpenThreads::ScopedLock lock(mDisabledMutex); - for (auto disabled : mDisabled) - refs.erase(disabled); + if (activeGrid) + for (auto ref : mBlacklist) + refs.erase(ref); } osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); @@ -424,7 +425,9 @@ namespace MWRender continue; } + float d = (viewPoint - pos).length(); + if (!activeGrid) { OpenThreads::ScopedLock lock(mSizeCacheMutex); SizeCache::iterator found = mSizeCache.find(pair.first); @@ -450,6 +453,15 @@ namespace MWRender */ osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); + if (activeGrid) + refnumSet->mRefnums.insert(pair.first); + + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (mDisabled.count(pair.first)) + continue; + } + float radius = cnode->getBound().radius() * ref.mScale; if (radius < d*minSize) { @@ -473,9 +485,6 @@ namespace MWRender else analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult); emplaced.first->second.mInstances.push_back(&ref); - - if (activeGrid) - refnumSet->mRefnums.insert(pair.first); } osg::ref_ptr group = new osg::Group; @@ -614,6 +623,7 @@ namespace MWRender } bool intersects(ChunkId id, osg::Vec3f pos) { + if (mActiveGridOnly && !std::get<2>(id)) return false; pos /= ESM::Land::REAL_SIZE; osg::Vec2f center = std::get<0>(id); float halfSize = std::get<1>(id)/2; @@ -621,6 +631,7 @@ namespace MWRender } osg::Vec3f mPosition; std::set mToClear; + bool mActiveGridOnly = false; }; bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) @@ -642,13 +653,33 @@ namespace MWRender return true; } + bool ObjectPaging::blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos) + { + if (!typeFilter(type, false, true)) + return false; + + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (!mBlacklist.insert(refnum).second) return false; + } + + ClearCacheFunctor ccf; + ccf.mPosition = pos; + ccf.mActiveGridOnly = true; + mCache->call(ccf); + if (ccf.mToClear.empty()) return false; + for (auto chunk : ccf.mToClear) + mCache->removeFromObjectCache(chunk); + return true; + } + + void ObjectPaging::clear() { { OpenThreads::ScopedLock lock(mDisabledMutex); - if (mDisabled.empty()) - return; mDisabled.clear(); + mBlacklist.clear(); } mCache->clear(); } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 79591dcc4..4419cd86a 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -33,9 +33,12 @@ namespace MWRender virtual unsigned int getNodeMask() override; - /// @return true if something changed + /// @return true if view needs rebuild bool enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled); + /// @return true if view needs rebuild + bool blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos); + void clear(); void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; @@ -53,6 +56,7 @@ namespace MWRender OpenThreads::Mutex mDisabledMutex; std::set mDisabled; + std::set mBlacklist; OpenThreads::Mutex mSizeCacheMutex; typedef std::map SizeCache; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5523a6936..590c59302 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1483,15 +1483,24 @@ namespace MWRender } bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) { - if (!ptr.isInCell() || !ptr.getCell()->isExterior()) + if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging) return false; - if (mObjectPaging && mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getRefData().getPosition().asVec3(), enabled)) + if (mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getCellRef().getPosition().asVec3(), enabled)) { mTerrain->rebuildViews(); return true; } return false; } + void RenderingManager::pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr) + { + if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging) + return; + const ESM::RefNum & refnum = ptr.getCellRef().getRefNum(); + if (!refnum.hasContentFile()) return; + if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3())) + mTerrain->rebuildViews(); + } void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) { if (mObjectPaging) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 3082866c4..c46c2f343 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -242,6 +242,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); + void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr); void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); private: diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 6b737f202..36f568a5f 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -141,7 +141,13 @@ namespace if (iter->mRef.getRefNum()==state.mRef.mRefNum && *iter->mRef.getRefIdPtr() == state.mRef.mRefID) { // overwrite existing reference + float oldscale = iter->mRef.getScale(); iter->load (state); + const ESM::Position & oldpos = iter->mRef.getPosition(); + const ESM::Position & newpos = iter->mData.getPosition(); + const MWWorld::Ptr ptr(&*iter, cellstore); + if ((oldscale != iter->mRef.getScale() || oldpos.asVec3() != newpos.asVec3() || oldpos.rot[0] != newpos.rot[0] || oldpos.rot[1] != newpos.rot[1] || oldpos.rot[2] != newpos.rot[2]) && !ptr.getClass().isActor()) + MWBase::Environment::get().getWorld()->moveObject(ptr, newpos.pos[0], newpos.pos[1], newpos.pos[2]); if (!iter->mData.isEnabled()) { iter->mData.enable(); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index bd9a92dae..a46986616 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -87,6 +87,19 @@ namespace ); } + std::string getModel(const MWWorld::Ptr &ptr, const VFS::Manager *vfs) + { + bool useAnim = ptr.getClass().useAnim(); + std::string model = ptr.getClass().getModel(ptr); + if (useAnim) + model = Misc::ResourceHelpers::correctActorModelPath(model, vfs); + + const std::string &id = ptr.getCellRef().getRefId(); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player + return model; + } + void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering, std::set& pagedRefs) { @@ -97,13 +110,7 @@ namespace } bool useAnim = ptr.getClass().useAnim(); - std::string model = ptr.getClass().getModel(ptr); - if (useAnim) - model = Misc::ResourceHelpers::correctActorModelPath(model, rendering.getResourceSystem()->getVFS()); - - std::string id = ptr.getCellRef().getRefId(); - if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") - model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player + std::string model = getModel(ptr, rendering.getResourceSystem()->getVFS()); const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end()) @@ -188,27 +195,6 @@ namespace } } - void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, - MWRender::RenderingManager& rendering, RotationOrder order) - { - setNodeRotation(ptr, rendering, order); - physics.updateRotation(ptr); - } - - void updateObjectScale(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, - MWRender::RenderingManager& rendering) - { - if (ptr.getRefData().getBaseNode() != nullptr) - { - float scale = ptr.getCellRef().getScale(); - osg::Vec3f scaleVec (scale, scale, scale); - ptr.getClass().adjustScale(ptr, scaleVec, true); - rendering.scaleObject(ptr, scaleVec); - - physics.updateScale(ptr); - } - } - struct InsertVisitor { MWWorld::CellStore& mCell; @@ -281,23 +267,49 @@ namespace namespace MWWorld { - void Scene::updateObjectRotation(const Ptr& ptr, RotationOrder order) + void Scene::removeFromPagedRefs(const Ptr &ptr) + { + const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); + if (refnum.hasContentFile() && mPagedRefs.erase(refnum)) + { + if (!ptr.getRefData().getBaseNode()) return; + ptr.getClass().insertObjectRendering(ptr, getModel(ptr, mRendering.getResourceSystem()->getVFS()), mRendering); + setNodeRotation(ptr, mRendering, RotationOrder::direct); + reloadTerrain(); + } + } + + void Scene::updateObjectPosition(const Ptr &ptr, const osg::Vec3f &pos, bool movePhysics) + { + mRendering.moveObject(ptr, pos); + if (movePhysics) + { + mPhysics->updatePosition(ptr); + } + } + + void Scene::updateObjectRotation(const Ptr &ptr, RotationOrder order) { - ::updateObjectRotation(ptr, *mPhysics, mRendering, order); + setNodeRotation(ptr, mRendering, order); + mPhysics->updateRotation(ptr); } void Scene::updateObjectScale(const Ptr &ptr) { - ::updateObjectScale(ptr, *mPhysics, mRendering); + float scale = ptr.getCellRef().getScale(); + osg::Vec3f scaleVec (scale, scale, scale); + ptr.getClass().adjustScale(ptr, scaleVec, true); + mRendering.scaleObject(ptr, scaleVec); + mPhysics->updateScale(ptr); } void Scene::update (float duration, bool paused) { - mPreloadTimer += duration; - if (mPreloadTimer > 0.1f) + mPreloadTimer -= duration; + if (mPreloadTimer <= 0.f) { preloadCells(0.1f); - mPreloadTimer = 0.f; + mPreloadTimer = 0.1f; } mRendering.update (duration, paused); @@ -954,7 +966,6 @@ namespace MWWorld if (ptr.getClass().isActor()) mRendering.removeWaterRippleEmitter(ptr); ptr.getRefData().setBaseNode(nullptr); - mPagedRefs.erase(ptr.getCellRef().getRefNum()); } bool Scene::isCellActive(const CellStore &cell) @@ -1149,6 +1160,7 @@ namespace MWWorld void Scene::reloadTerrain() { + mPreloadTimer = 0; mPreloader->setTerrainPreloadPositions(std::vector()); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index b7aeafbfe..2fbbccf68 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -155,8 +155,11 @@ namespace MWWorld void removeObjectFromScene (const Ptr& ptr); ///< Remove an object from the scene, but not from the world model. + void removeFromPagedRefs(const Ptr &ptr); + void updateObjectRotation(const Ptr& ptr, RotationOrder order); void updateObjectScale(const Ptr& ptr); + void updateObjectPosition(const Ptr &ptr, const osg::Vec3f &pos, bool movePhysics); bool isCellActive(const CellStore &cell); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8392a827c..2975240e7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1204,20 +1204,22 @@ namespace MWWorld } if (haveToMove && newPtr.getRefData().getBaseNode()) { - mRendering->moveObject(newPtr, vec); + mWorldScene->updateObjectPosition(newPtr, vec, movePhysics); if (movePhysics) { - mPhysics->updatePosition(newPtr); - mPhysics->updatePtr(ptr, newPtr); - - if (const auto object = mPhysics->getObject(newPtr)) + if (const auto object = mPhysics->getObject(ptr)) updateNavigatorObject(object); } } + if (isPlayer) - { mWorldScene->playerMoved(vec); + else + { + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(newPtr); } + return newPtr; } @@ -1246,9 +1248,15 @@ namespace MWWorld if (mPhysics->getActor(ptr)) mNavigator->removeAgent(getPathfindingHalfExtents(ptr)); - ptr.getCellRef().setScale(scale); + if (scale != ptr.getCellRef().getScale()) + { + ptr.getCellRef().setScale(scale); + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(ptr); + } - mWorldScene->updateObjectScale(ptr); + if(ptr.getRefData().getBaseNode() != 0) + mWorldScene->updateObjectScale(ptr); if (mPhysics->getActor(ptr)) mNavigator->addAgent(getPathfindingHalfExtents(ptr)); @@ -1292,6 +1300,9 @@ namespace MWWorld ptr.getRefData().setPosition(pos); + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(ptr); + if(ptr.getRefData().getBaseNode() != 0) { const auto order = flags & MWBase::RotationFlag_inverseOrder @@ -3565,6 +3576,8 @@ namespace MWWorld std::string World::exportSceneGraph(const Ptr &ptr) { std::string file = mUserDataPath + "/openmw.osgt"; + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(ptr); mRendering->exportSceneGraph(ptr, file, "Ascii"); return file; } From 65cd2c77aa40f1e184b6231d8273651bbe43e7dd Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 200/227] static intersections Signed-off-by: Bret Curtis --- components/terrain/quadtreenode.cpp | 17 ------------- components/terrain/quadtreenode.hpp | 30 ---------------------- components/terrain/quadtreeworld.cpp | 37 +++------------------------- 3 files changed, 4 insertions(+), 80 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 8f47986df..7baea45c8 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -124,23 +124,6 @@ void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodC vd->add(this); } -void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector& intersector) -{ - if (!hasValidBounds()) - return; - - if (!intersector.intersectAndClip(getBoundingBox())) - return; - - if (getNumChildren() == 0) - vd->add(this); - else - { - for (unsigned int i=0; iintersect(vd, intersector); - } -} - void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) { mBoundingBox = boundingBox; diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 183b5e07a..a309d91fc 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -2,39 +2,12 @@ #define OPENMW_COMPONENTS_TERRAIN_QUADTREENODE_H #include -#include #include "defs.hpp" namespace Terrain { - class TerrainLineIntersector : public osgUtil::LineSegmentIntersector - { - public: - TerrainLineIntersector(osgUtil::LineSegmentIntersector* intersector, osg::Matrix& matrix) : - osgUtil::LineSegmentIntersector(intersector->getStart() * matrix, intersector->getEnd() * matrix) - { - setPrecisionHint(intersector->getPrecisionHint()); - _intersectionLimit = intersector->getIntersectionLimit(); - _parent = intersector; - } - - TerrainLineIntersector(osgUtil::LineSegmentIntersector* intersector) : - osgUtil::LineSegmentIntersector(intersector->getStart(), intersector->getEnd()) - { - setPrecisionHint(intersector->getPrecisionHint()); - _intersectionLimit = intersector->getIntersectionLimit(); - _parent = intersector; - } - - bool intersectAndClip(const osg::BoundingBox& bbInput) - { - osg::Vec3d s(_start), e(_end); - return osgUtil::LineSegmentIntersector::intersectAndClip(s, e, bbInput); - } - }; - enum ChildDirection { NW = 0, @@ -105,9 +78,6 @@ namespace Terrain /// Traverse nodes according to LOD selection. void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback); - /// Adds all leaf nodes which intersect the line from start to end - void intersect(ViewData* vd, TerrainLineIntersector& intersector); - private: QuadTreeNode* mParent; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 78d8835b5..e09433061 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -418,44 +418,15 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) return; } + osg::Object * viewer = isCullVisitor ? static_cast(&nv)->getCurrentCamera() : nullptr; bool needsUpdate = true; - ViewData* vd = nullptr; - if (isCullVisitor) - vd = mViewDataMap->getViewData(static_cast(&nv)->getCurrentCamera(), nv.getViewPoint(), mActiveGrid, needsUpdate); - else - { - static ViewData sIntersectionViewData; - vd = &sIntersectionViewData; - vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. - } + ViewData *vd = mViewDataMap->getViewData(viewer, nv.getViewPoint(), mActiveGrid, needsUpdate); if (needsUpdate) { vd->reset(); - if (isCullVisitor) - { - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, mActiveGrid); - mRootNode->traverseNodes(vd, nv.getViewPoint(), &lodCallback); - } - else - { - osgUtil::IntersectionVisitor* iv = static_cast(&nv); - osgUtil::LineSegmentIntersector* lineIntersector = dynamic_cast(iv->getIntersector()); - if (!lineIntersector) - throw std::runtime_error("Cannot update QuadTreeWorld: node visitor is not LineSegmentIntersector"); - - if (lineIntersector->getCoordinateFrame() == osgUtil::Intersector::CoordinateFrame::MODEL && iv->getModelMatrix() == 0) - { - TerrainLineIntersector terrainIntersector(lineIntersector); - mRootNode->intersect(vd, terrainIntersector); - } - else - { - osg::Matrix matrix(lineIntersector->getTransformation(*iv, lineIntersector->getCoordinateFrame())); - TerrainLineIntersector terrainIntersector(lineIntersector, matrix); - mRootNode->intersect(vd, terrainIntersector); - } - } + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, mActiveGrid); + mRootNode->traverseNodes(vd, nv.getViewPoint(), &lodCallback); } const float cellWorldSize = mStorage->getCellWorldSize(); From 9f0398c0214b5a52aa13ee21117572d99d4a831d Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 201/227] intersection by refnum tag + enable paging for acti,door,cont Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 75 ++++++++++++++++++----- apps/openmw/mwrender/objectpaging.hpp | 10 +++ apps/openmw/mwrender/renderingmanager.cpp | 16 +++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellstore.cpp | 40 ++---------- apps/openmw/mwworld/worldimp.cpp | 8 +++ components/sceneutil/optimizer.cpp | 4 ++ 7 files changed, 104 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 4c8706caf..365e8ec91 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -33,18 +35,16 @@ namespace MWRender { - bool typeFilter(int type, bool far, bool activeGrid) + bool typeFilter(int type, bool far) { switch (type) { case ESM::REC_STAT: - return true; - - case ESM::REC_ACTI: // TODO enable when intersectionvisitor supported + case ESM::REC_ACTI: case ESM::REC_DOOR: - return !activeGrid; + return true; case ESM::REC_CONT: - return far ? false : !activeGrid; + return !far; default: return false; @@ -321,6 +321,21 @@ namespace MWRender } }; + class AddRefnumMarkerVisitor : public osg::NodeVisitor + { + public: + AddRefnumMarkerVisitor(const ESM::RefNum &refnum) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mRefnum(refnum) {} + ESM::RefNum mRefnum; + virtual void apply(osg::Geometry &node) + { + osg::ref_ptr marker (new RefnumMarker); + marker->mRefnum = mRefnum; + if (osg::Array* array = node.getVertexArray()) + marker->mNumVertices = array->getNumElements(); + node.getOrCreateUserDataContainer()->addUserObject(marker); + } + }; + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) @@ -365,7 +380,7 @@ namespace MWRender { if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2,activeGrid)) continue; + if (!typeFilter(type,size>=2)) continue; if (deleted) { refs.erase(ref.mRefNum); continue; } refs[ref.mRefNum] = ref; } @@ -381,7 +396,7 @@ namespace MWRender bool deleted = it->second; if (deleted) { refs.erase(ref.mRefNum); continue; } int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2,activeGrid)) continue; + if (!typeFilter(type,size>=2)) continue; refs[ref.mRefNum] = ref; } } @@ -446,15 +461,32 @@ namespace MWRender std::string model = getModel(type, id, store); if (model.empty()) continue; model = "meshes/" + model; -/* + bool useAnim = type != ESM::REC_STAT; if (useAnim) + { model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); -*/ + if (activeGrid) + { + std::string kfname = Misc::StringUtils::lowerCase(model); + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) + { + kfname.replace(kfname.size()-4, 4, ".kf"); + if (mSceneManager->getVFS()->exists(kfname)) + continue; + } + } + } + osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); if (activeGrid) - refnumSet->mRefnums.insert(pair.first); + { + if (cnode->getNumChildrenRequiringUpdateTraversal() > 0 || SceneUtil::hasUserDescription(cnode, Constants::NightDayLabel) || SceneUtil::hasUserDescription(cnode, Constants::HerbalismLabel)) + continue; + else + refnumSet->mRefnums.insert(pair.first); + } { OpenThreads::ScopedLock lock(mDisabledMutex); @@ -472,9 +504,6 @@ namespace MWRender continue; } - if (activeGrid && cnode->getNumChildrenRequiringUpdateTraversal() > 0) - continue; - auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { @@ -537,6 +566,20 @@ namespace MWRender osg::ref_ptr node = osg::clone(cnode, co); node->setUserDataContainer(nullptr); + if (activeGrid) + { + if (merge) + { + AddRefnumMarkerVisitor visitor(ref.mRefNum); + node->accept(visitor); + } + else + { + osg::ref_ptr marker = new RefnumMarker; marker->mRefnum = ref.mRefNum; + node->getOrCreateUserDataContainer()->addUserObject(marker); + } + } + trans->addChild(node); if (merge) @@ -636,7 +679,7 @@ namespace MWRender bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) { - if (!typeFilter(type, false, false)) + if (!typeFilter(type, false)) return false; { @@ -655,7 +698,7 @@ namespace MWRender bool ObjectPaging::blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos) { - if (!typeFilter(type, false, true)) + if (!typeFilter(type, false)) return false; { diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 4419cd86a..03d9ccfad 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -63,6 +63,16 @@ namespace MWRender SizeCache mSizeCache; }; + class RefnumMarker : public osg::Object + { + public: + RefnumMarker() : mNumVertices(0) {} + RefnumMarker(const RefnumMarker ©, osg::CopyOp co) : mRefnum(copy.mRefnum), mNumVertices(copy.mNumVertices) {} + META_Object(MWRender, RefnumMarker) + + ESM::RefNum mRefnum; + unsigned int mNumVertices; + }; } #endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 590c59302..bb7528a20 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1016,6 +1016,7 @@ namespace MWRender { RenderingManager::RayResult result; result.mHit = false; + result.mHitRefnum.mContentFile = -1; result.mRatio = 0; if (intersector->containsIntersections()) { @@ -1027,6 +1028,7 @@ namespace MWRender result.mRatio = intersection.ratio; PtrHolder* ptrHolder = nullptr; + std::vector refnumMarkers; for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) { osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer(); @@ -1036,11 +1038,25 @@ namespace MWRender { if (PtrHolder* p = dynamic_cast(userDataContainer->getUserObject(i))) ptrHolder = p; + if (RefnumMarker* r = dynamic_cast(userDataContainer->getUserObject(i))) + refnumMarkers.push_back(r); } } if (ptrHolder) result.mHitObject = ptrHolder->mPtr; + + unsigned int vertexCounter = 0; + for (unsigned int i=0; imNumVertices || (intersectionIndex >= vertexCounter && intersectionIndex < vertexCounter + refnumMarkers[i]->mNumVertices)) + { + result.mHitRefnum = refnumMarkers[i]->mRefnum; + break; + } + vertexCounter += refnumMarkers[i]->mNumVertices; + } } return result; diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index c46c2f343..28376d1d6 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -157,6 +157,7 @@ namespace MWRender osg::Vec3f mHitNormalWorld; osg::Vec3f mHitPointWorld; MWWorld::Ptr mHitObject; + ESM::RefNum mHitRefnum; float mRatio; }; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 36f568a5f..2cda83e17 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -165,28 +165,6 @@ namespace ref.load (state); collection.mList.push_back (ref); } - - struct SearchByRefNumVisitor - { - MWWorld::LiveCellRefBase* mFound; - ESM::RefNum mRefNumToFind; - - SearchByRefNumVisitor(const ESM::RefNum& toFind) - : mFound(nullptr) - , mRefNumToFind(toFind) - { - } - - bool operator()(const MWWorld::Ptr& ptr) - { - if (ptr.getCellRef().getRefNum() == mRefNumToFind) - { - mFound = ptr.getBase(); - return false; - } - return true; - } - }; } namespace MWWorld @@ -263,9 +241,7 @@ namespace MWWorld throw std::runtime_error("moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)"); // Ensure that the object actually exists in the cell - SearchByRefNumVisitor searchVisitor(object.getCellRef().getRefNum()); - forEach(searchVisitor); - if (!searchVisitor.mFound) + if (searchViaRefNum(object.getCellRef().getRefNum()).isEmpty()) throw std::runtime_error("moveTo: object is not in this cell"); @@ -942,26 +918,22 @@ namespace MWWorld movedTo.load(reader); // Search for the reference. It might no longer exist if its content file was removed. - SearchByRefNumVisitor visitor(refnum); - forEachInternal(visitor); - - if (!visitor.mFound) + Ptr movedRef = searchViaRefNum(refnum); + if (movedRef.isEmpty()) { Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)"; continue; } - MWWorld::LiveCellRefBase* movedRef = visitor.mFound; - CellStore* otherCell = callback->getCellStore(movedTo); if (otherCell == nullptr) { - Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() + Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef.getCellRef().getRefId() << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location."; // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. // Restore original coordinates: - movedRef->mData.setPosition(movedRef->mRef.getPosition()); + movedRef.getRefData().setPosition(movedRef.getCellRef().getPosition()); continue; } @@ -972,7 +944,7 @@ namespace MWWorld continue; } - moveTo(MWWorld::Ptr(movedRef, this), otherCell); + moveTo(movedRef, otherCell); } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2975240e7..5f1c8aaa2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1965,6 +1965,14 @@ namespace MWWorld rayToObject = mRendering->castCameraToViewportRay(0.5f, 0.5f, maxDistance, ignorePlayer); facedObject = rayToObject.mHitObject; + if (facedObject.isEmpty() && rayToObject.mHitRefnum.hasContentFile()) + { + for (CellStore* cellstore : mWorldScene->getActiveCells()) + { + facedObject = cellstore->searchViaRefNum(rayToObject.mHitRefnum); + if (!facedObject.isEmpty()) break; + } + } if (rayToObject.mHit) mDistanceToFacedObject = (rayToObject.mRatio * maxDistance) - camDist; else diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 985fd9ee2..f0da9e7e2 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -1824,6 +1824,10 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom lhs.dirtyBound(); lhs.dirtyDisplayList(); + if (osg::UserDataContainer* rhsUserData = rhs.getUserDataContainer()) + for (unsigned int i=0; igetNumUserObjects(); ++i) + lhs.getOrCreateUserDataContainer()->addUserObject(rhsUserData->getUserObject(i)); + return true; } From 89ec6cfea2d9aadd5996cff755ee457aaf6a2437 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 202/227] tooltips labels Signed-off-by: Bret Curtis --- apps/openmw/mwphysics/physicssystem.cpp | 9 +++++++++ apps/openmw/mwphysics/physicssystem.hpp | 4 ++++ apps/openmw/mwrender/renderingmanager.cpp | 12 +++--------- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 11 +++++++++-- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 7cc0f7770..bbb2ae8d3 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -397,6 +397,15 @@ namespace MWPhysics return osg::Vec3f(); } + osg::BoundingBox PhysicsSystem::getBoundingBox(const MWWorld::ConstPtr &object) const + { + const Object * physobject = getObject(object); + if (!physobject) return osg::BoundingBox(); + btVector3 min, max; + physobject->getCollisionObject()->getCollisionShape()->getAabb(physobject->getCollisionObject()->getWorldTransform(), min, max); + return osg::BoundingBox(Misc::Convert::toOsg(min), Misc::Convert::toOsg(max)); + } + osg::Vec3f PhysicsSystem::getCollisionObjectPosition(const MWWorld::ConstPtr &actor) const { const Actor* physactor = getActor(actor); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 8b09722af..32d460b1d 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include "../mwworld/ptr.hpp" @@ -144,6 +145,9 @@ namespace MWPhysics /// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. osg::Vec3f getCollisionObjectPosition(const MWWorld::ConstPtr& actor) const; + /// Get bounding box in world space of the given object. + osg::BoundingBox getBoundingBox(const MWWorld::ConstPtr &object) const; + /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will /// be overwritten. Valid until the next call to applyQueuedMovement. void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index bb7528a20..30e63e56e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -977,20 +977,14 @@ namespace MWRender renderCameraToImage(rttCamera.get(),image,w,h); } - osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr) + osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox &worldbb) { - if (!ptr.getRefData().getBaseNode()) - return osg::Vec4f(); - - osg::ComputeBoundsVisitor computeBoundsVisitor; - computeBoundsVisitor.setTraversalMask(~(Mask_ParticleSystem|Mask_Effect)); - ptr.getRefData().getBaseNode()->accept(computeBoundsVisitor); - + if (!worldbb.valid()) return osg::Vec4f(); osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix(); float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f; for (int i=0; i<8; ++i) { - osg::Vec3f corner = computeBoundsVisitor.getBoundingBox().corner(i); + osg::Vec3f corner = worldbb.corner(i); corner = corner * viewProj; float x = (corner.x() + 1.f) * 0.5f; diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 28376d1d6..b18d2caf1 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -168,7 +168,7 @@ namespace MWRender RayResult castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors=false); /// Get the bounding box of the given object in screen coordinates as (minX, minY, maxX, maxY), with (0,0) being the top left corner. - osg::Vec4f getScreenBounds(const MWWorld::Ptr& ptr); + osg::Vec4f getScreenBounds(const osg::BoundingBox &worldbb); void setSkyEnabled(bool enabled); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5f1c8aaa2..12521f059 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1934,8 +1934,15 @@ namespace MWWorld // retrieve object dimensions so we know where to place the floating label if (!object.isEmpty ()) { - osg::Vec4f screenBounds = mRendering->getScreenBounds(object); - + osg::BoundingBox bb = mPhysics->getBoundingBox(object); + if (!bb.valid() && object.getRefData().getBaseNode()) + { + osg::ComputeBoundsVisitor computeBoundsVisitor; + computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect)); + object.getRefData().getBaseNode()->accept(computeBoundsVisitor); + bb = computeBoundsVisitor.getBoundingBox(); + } + osg::Vec4f screenBounds = mRendering->getScreenBounds(bb); MWBase::Environment::get().getWindowManager()->setFocusObjectScreenCoords( screenBounds.x(), screenBounds.y(), screenBounds.z(), screenBounds.w()); } From 6fa12a6eb854476c98a9546658d4ce6f837e930c Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 203/227] preload tweak Signed-off-by: Bret Curtis --- apps/openmw/mwworld/cellpreloader.cpp | 51 ++++++++++----------------- apps/openmw/mwworld/scene.cpp | 12 ++----- apps/openmw/mwworld/scene.hpp | 1 - 3 files changed, 22 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index eb48424c5..872a29e89 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -65,23 +66,7 @@ namespace MWWorld mTerrainView = mTerrain->createView(); ListModelsVisitor visitor (mMeshes); - if (cell->getState() == MWWorld::CellStore::State_Loaded) - { - cell->forEach(visitor); - } - else - { - const std::vector& objectIds = cell->getPreloadedIds(); - - // could possibly build the model list in the worker thread if we manage to make the Store thread safe - for (const std::string& id : objectIds) - { - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id); - std::string model = ref.getPtr().getClass().getModel(ref.getPtr()); - if (!model.empty()) - mMeshes.push_back(model); - } - } + cell->forEach(visitor); } virtual void abort() @@ -97,7 +82,7 @@ namespace MWWorld try { mTerrain->cacheCell(mTerrainView.get(), mX, mY); - mPreloadedObjects.push_back(mLandManager->getLand(mX, mY)); + mPreloadedObjects.insert(mLandManager->getLand(mX, mY)); } catch(std::exception& e) { @@ -113,17 +98,7 @@ namespace MWWorld { mesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS()); - if (mPreloadInstances) - { - mPreloadedObjects.push_back(mSceneManager->cacheInstance(mesh)); - mPreloadedObjects.push_back(mBulletShapeManager->cacheInstance(mesh)); - } - else - { - mPreloadedObjects.push_back(mSceneManager->getTemplate(mesh)); - mPreloadedObjects.push_back(mBulletShapeManager->getShape(mesh)); - } - + bool animated = false; size_t slashpos = mesh.find_last_of("/\\"); if (slashpos != std::string::npos && slashpos != mesh.size()-1) { @@ -134,11 +109,23 @@ namespace MWWorld if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) { kfname.replace(kfname.size()-4, 4, ".kf"); - mPreloadedObjects.push_back(mKeyframeManager->get(kfname)); + if (mSceneManager->getVFS()->exists(kfname)) + { + mPreloadedObjects.insert(mKeyframeManager->get(kfname)); + animated = true; + } } - } } + if (mPreloadInstances && animated) + mPreloadedObjects.insert(mSceneManager->cacheInstance(mesh)); + else + mPreloadedObjects.insert(mSceneManager->getTemplate(mesh)); + if (mPreloadInstances) + mPreloadedObjects.insert(mBulletShapeManager->cacheInstance(mesh)); + else + mPreloadedObjects.insert(mBulletShapeManager->getShape(mesh)); + } catch (std::exception& e) { @@ -166,7 +153,7 @@ namespace MWWorld osg::ref_ptr mTerrainView; // keep a ref to the loaded objects to make sure it stays loaded as long as this cell is in the preloaded state - std::vector > mPreloadedObjects; + std::set > mPreloadedObjects; }; class TerrainPreloadItem : public SceneUtil::WorkItem diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index a46986616..d2b31a855 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -305,12 +305,7 @@ namespace MWWorld void Scene::update (float duration, bool paused) { - mPreloadTimer -= duration; - if (mPreloadTimer <= 0.f) - { - preloadCells(0.1f); - mPreloadTimer = 0.1f; - } + preloadCells(duration); mRendering.update (duration, paused); @@ -760,13 +755,12 @@ namespace MWWorld MWBase::Environment::get().getWorld()->adjustSky(); - mLastPlayerPos = pos.asVec3(); + mLastPlayerPos = player.getRefData().getPosition().asVec3(); } Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics, DetourNavigator::Navigator& navigator) : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNavigator(navigator) - , mPreloadTimer(0.f) , mHalfGridSize(Settings::Manager::getInt("exterior cell load distance", "Cells")) , mCellLoadingThreshold(1024.f) , mPreloadDistance(Settings::Manager::getInt("preload distance", "Cells")) @@ -1025,6 +1019,7 @@ namespace MWWorld void Scene::preloadCells(float dt) { + if (dt<=1e-06) return; std::vector exteriorPositions; const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); @@ -1160,7 +1155,6 @@ namespace MWWorld void Scene::reloadTerrain() { - mPreloadTimer = 0; mPreloader->setTerrainPreloadPositions(std::vector()); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 2fbbccf68..227bb7b31 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -75,7 +75,6 @@ namespace MWWorld MWRender::RenderingManager& mRendering; DetourNavigator::Navigator& mNavigator; std::unique_ptr mPreloader; - float mPreloadTimer; int mHalfGridSize; float mCellLoadingThreshold; float mPreloadDistance; From b27b76e32582601412da380eef273619a62cfe4b Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 204/227] avoid pagerebuild when reloading a same save Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 41 ++++++++++----- apps/openmw/mwrender/objectpaging.hpp | 20 +++++-- apps/openmw/mwrender/renderingmanager.cpp | 9 ++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellpreloader.cpp | 32 +++++++----- apps/openmw/mwworld/cellpreloader.hpp | 4 +- apps/openmw/mwworld/scene.cpp | 64 +++++++++++------------ apps/openmw/mwworld/scene.hpp | 6 +-- 8 files changed, 110 insertions(+), 67 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 365e8ec91..c3a90da80 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -339,6 +339,7 @@ namespace MWRender ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) + , mRefTrackerLocked(false) { mActiveGrid = Settings::Manager::getBool("object paging active grid", "Terrain"); mDebugBatches = Settings::Manager::getBool("object paging debug batches", "Terrain"); @@ -403,9 +404,9 @@ namespace MWRender } { - OpenThreads::ScopedLock lock(mDisabledMutex); + OpenThreads::ScopedLock lock(mRefTrackerMutex); if (activeGrid) - for (auto ref : mBlacklist) + for (auto ref : getRefTracker().mBlacklist) refs.erase(ref); } @@ -489,8 +490,8 @@ namespace MWRender } { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (mDisabled.count(pair.first)) + OpenThreads::ScopedLock lock(mRefTrackerMutex); + if (getRefTracker().mDisabled.count(pair.first)) continue; } @@ -683,14 +684,16 @@ namespace MWRender return false; { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (enabled && !mDisabled.erase(refnum)) return false; - if (!enabled && !mDisabled.insert(refnum).second) return false; + OpenThreads::ScopedLock lock(mRefTrackerMutex); + if (enabled && !getWritableRefTracker().mDisabled.erase(refnum)) return false; + if (!enabled && !getWritableRefTracker().mDisabled.insert(refnum).second) return false; + if (mRefTrackerLocked) return false; } ClearCacheFunctor ccf; ccf.mPosition = pos; mCache->call(ccf); + if (ccf.mToClear.empty()) return false; for (auto chunk : ccf.mToClear) mCache->removeFromObjectCache(chunk); return true; @@ -702,8 +705,9 @@ namespace MWRender return false; { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (!mBlacklist.insert(refnum).second) return false; + OpenThreads::ScopedLock lock(mRefTrackerMutex); + if (!getWritableRefTracker().mBlacklist.insert(refnum).second) return false; + if (mRefTrackerLocked) return false; } ClearCacheFunctor ccf; @@ -719,12 +723,25 @@ namespace MWRender void ObjectPaging::clear() { + OpenThreads::ScopedLock lock(mRefTrackerMutex); + mRefTrackerNew.mDisabled.clear(); + mRefTrackerNew.mBlacklist.clear(); + mRefTrackerLocked = true; + } + + bool ObjectPaging::unlockCache() + { + if (!mRefTrackerLocked) return false; { - OpenThreads::ScopedLock lock(mDisabledMutex); - mDisabled.clear(); - mBlacklist.clear(); + OpenThreads::ScopedLock lock(mRefTrackerMutex); + mRefTrackerLocked = false; + if (mRefTracker == mRefTrackerNew) + return false; + else + mRefTracker = mRefTrackerNew; } mCache->clear(); + return true; } struct GetRefnumsFunctor diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 03d9ccfad..c79dd6e06 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -41,6 +41,10 @@ namespace MWRender void clear(); + /// Must be called after clear() before rendering starts. + /// @return true if view needs rebuild + bool unlockCache(); + void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); @@ -54,9 +58,19 @@ namespace MWRender float mMinSizeMergeFactor; float mMinSizeCostMultiplier; - OpenThreads::Mutex mDisabledMutex; - std::set mDisabled; - std::set mBlacklist; + OpenThreads::Mutex mRefTrackerMutex; + struct RefTracker + { + std::set mDisabled; + std::set mBlacklist; + bool operator==(const RefTracker&other) { return mDisabled == other.mDisabled && mBlacklist == other.mBlacklist; } + }; + RefTracker mRefTracker; + RefTracker mRefTrackerNew; + bool mRefTrackerLocked; + + const RefTracker& getRefTracker() const { return mRefTracker; } + RefTracker& getWritableRefTracker() { return mRefTrackerLocked ? mRefTrackerNew : mRefTracker; } OpenThreads::Mutex mSizeCacheMutex; typedef std::map SizeCache; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 30e63e56e..99f3e25ac 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1511,6 +1511,15 @@ namespace MWRender if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3())) mTerrain->rebuildViews(); } + bool RenderingManager::pagingUnlockCache() + { + if (mObjectPaging && mObjectPaging->unlockCache()) + { + mTerrain->rebuildViews(); + return true; + } + return false; + } void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) { if (mObjectPaging) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b18d2caf1..db4788970 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -244,6 +244,7 @@ namespace MWRender bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr); + bool pagingUnlockCache(); void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); private: diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 872a29e89..bee6a957d 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -419,36 +419,44 @@ namespace MWWorld mUnrefQueue = unrefQueue; } - bool CellPreloader::getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp) + bool CellPreloader::syncTerrainLoad(const std::vector &positions, int& progress, int& progressRange, double timestamp) { if (!mTerrainPreloadItem) - return false; + return true; else if (mTerrainPreloadItem->isDone()) { - mTerrainPreloadItem->storeViews(timestamp); - mTerrainPreloadItem = nullptr; - return false; + if (mTerrainPreloadItem->storeViews(timestamp)) + { + mTerrainPreloadItem = nullptr; + return true; + } + else + { + setTerrainPreloadPositions(std::vector()); + setTerrainPreloadPositions(positions); + return false; + } } else { progress = mTerrainPreloadItem->getProgress(); progressRange = mTerrainPreloadItem->getProgressRange(); - return !progress || progress < progressRange; + return false; } } - void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid& exceptPos) + void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid *exceptPos) { + const float resetThreshold = ESM::Land::REAL_SIZE; + for (auto pos : mTerrainPreloadPositions) + if (exceptPos && (pos.first-exceptPos->first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos->second) + return; if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) { - const float resetThreshold = ESM::Land::REAL_SIZE; - for (auto pos : mTerrainPreloadPositions) - if ((pos.first-exceptPos.first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos.second) - return; mTerrainPreloadItem->abort(); mTerrainPreloadItem->waitTillDone(); - mTerrainPreloadItem = nullptr; } + setTerrainPreloadPositions(std::vector()); } bool contains(const std::vector& container, const std::vector& contained) diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 98e173f17..e719f2e60 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -72,8 +72,8 @@ namespace MWWorld typedef std::pair PositionCellGrid; void setTerrainPreloadPositions(const std::vector& positions); - bool getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp); - void abortTerrainPreloadExcept(const PositionCellGrid &exceptPos); + bool syncTerrainLoad(const std::vector &positions, int& progress, int& progressRange, double timestamp); + void abortTerrainPreloadExcept(const PositionCellGrid *exceptPos); private: Resource::ResourceSystem* mResourceSystem; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index d2b31a855..53ddaf08c 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -510,13 +510,10 @@ namespace MWWorld osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter); if (newCell != mCurrentGridCenter) - { - preloadTerrain(pos); - changeCellGrid(newCell.x(), newCell.y()); - } + changeCellGrid(pos, newCell.x(), newCell.y()); } - void Scene::changeCellGrid (int playerCellX, int playerCellY, bool changeEvent) + void Scene::changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent) { CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) @@ -538,8 +535,8 @@ namespace MWWorld osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); mRendering.setActiveGrid(newGrid); + preloadTerrain(pos, true); mPagedRefs.clear(); - checkTerrainLoaded(); mRendering.getPagedRefnums(newGrid, mPagedRefs); std::size_t refsToLoad = 0; @@ -867,9 +864,7 @@ namespace MWWorld if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); - preloadTerrain(position.asVec3()); - checkTerrainLoaded(); - changeCellGrid(x, y, changeEvent); + changeCellGrid(position.asVec3(), x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); @@ -878,29 +873,6 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } - void Scene::checkTerrainLoaded() - { - Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - Loading::ScopedLoad load(loadingListener); - int progress = 0, initialProgress = -1, progressRange = 0; - while (mPreloader->getTerrainPreloadInProgress(progress, progressRange, mRendering.getReferenceTime())) - { - if (initialProgress == -1) - { - loadingListener->setLabel("#{sLoadingMessage4}"); - initialProgress = progress; - } - if (progress) - { - loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); - loadingListener->setProgress(progress-initialProgress); - } - else - loadingListener->setProgress(0); - OpenThreads::Thread::microSleep(5000); - } - } - CellStore* Scene::getCurrentCell () { return mCurrentCell; @@ -1145,12 +1117,36 @@ namespace MWWorld mPreloader->preload(cell, mRendering.getReferenceTime()); } - void Scene::preloadTerrain(const osg::Vec3f &pos) + void Scene::preloadTerrain(const osg::Vec3f &pos, bool sync) { std::vector vec; vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); - mPreloader->abortTerrainPreloadExcept(vec[0]); + if (sync && mRendering.pagingUnlockCache()) + mPreloader->abortTerrainPreloadExcept(nullptr); + else + mPreloader->abortTerrainPreloadExcept(&vec[0]); mPreloader->setTerrainPreloadPositions(vec); + if (!sync) return; + + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); + int progress = 0, initialProgress = -1, progressRange = 0; + while (!mPreloader->syncTerrainLoad(vec, progress, progressRange, mRendering.getReferenceTime())) + { + if (initialProgress == -1) + { + loadingListener->setLabel("#{sLoadingMessage4}"); + initialProgress = progress; + } + if (progress) + { + loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); + loadingListener->setProgress(progress-initialProgress); + } + else + loadingListener->setProgress(0); + OpenThreads::Thread::microSleep(5000); + } } void Scene::reloadTerrain() diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 227bb7b31..b9a39779d 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -93,7 +93,7 @@ namespace MWWorld osg::Vec2i mCurrentGridCenter; // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center - void changeCellGrid (int playerCellX, int playerCellY, bool changeEvent = true); + void changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent = true); typedef std::pair PositionCellGrid; @@ -102,8 +102,6 @@ namespace MWWorld void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); - void checkTerrainLoaded(); - osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; @@ -115,7 +113,7 @@ namespace MWWorld ~Scene(); void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); - void preloadTerrain(const osg::Vec3f& pos); + void preloadTerrain(const osg::Vec3f& pos, bool sync=false); void reloadTerrain(); void unloadCell (CellStoreCollection::iterator iter, bool test = false); From 4238fbccdf450c89454bfdb76fc7b1a7c1b19945 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 205/227] view fix Signed-off-by: Bret Curtis --- apps/openmw/mwrender/renderingmanager.cpp | 1 + apps/openmw/mwworld/scene.cpp | 3 +-- components/terrain/viewdata.cpp | 20 ++++++++++++-------- components/terrain/viewdata.hpp | 1 + 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 99f3e25ac..d21482894 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1063,6 +1063,7 @@ namespace MWRender mIntersectionVisitor = new osgUtil::IntersectionVisitor; mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber()); + mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp()); mIntersectionVisitor->setIntersector(intersector); int mask = ~0; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 53ddaf08c..b311107f4 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -305,11 +305,10 @@ namespace MWWorld void Scene::update (float duration, bool paused) { + mPreloader->updateCache(mRendering.getReferenceTime()); preloadCells(duration); mRendering.update (duration, paused); - - mPreloader->updateCache(mRendering.getReferenceTime()); } void Scene::unloadCell (CellStoreCollection::iterator iter, bool test) diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 8a8769000..c24252b7d 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -145,15 +145,15 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo { float shortestDist = std::numeric_limits::max(); const ViewData* mostSuitableView = nullptr; - for (const ViewData& other : mViewVector) + for (const ViewData* other : mUsedViews) { - if (other.suitableToUse(activeGrid) && other.getWorldUpdateRevision() >= mWorldUpdateRevision) + if (other->suitableToUse(activeGrid) && other->getWorldUpdateRevision() >= mWorldUpdateRevision) { - float dist = (viewPoint-other.getViewPoint()).length2(); + float dist = (viewPoint-other->getViewPoint()).length2(); if (dist < shortestDist) { shortestDist = dist; - mostSuitableView = &other; + mostSuitableView = other; } } } @@ -195,6 +195,7 @@ ViewData *ViewDataMap::createOrReuseView() mViewVector.push_back(ViewData()); vd = &mViewVector.back(); } + mUsedViews.push_back(vd); vd->setWorldUpdateRevision(mWorldUpdateRevision); return vd; } @@ -215,13 +216,16 @@ void ViewDataMap::clearUnusedViews(double referenceTime) else ++it; } - for (ViewData& vd : mViewVector) + for (std::deque::iterator it = mUsedViews.begin(); it != mUsedViews.end(); ) { - if (vd.getLastUsageTimeStamp() + mExpiryDelay < referenceTime) + if ((*it)->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) { - vd.clear(); - mUnusedViews.push_back(&vd); + (*it)->clear(); + mUnusedViews.push_back(*it); + it = mUsedViews.erase(it); } + else + ++it; } } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index ba4fba3b2..400d9fbbe 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -106,6 +106,7 @@ namespace Terrain unsigned int mWorldUpdateRevision; + std::deque mUsedViews; std::deque mUnusedViews; }; From f12879a04c4db10e8ee8f80f217c9b9e8d11c121 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 206/227] allow statesetupdater as cullcallback = faster + works in paging Signed-off-by: Bret Curtis --- apps/openmw/mwrender/animation.cpp | 22 +++++----------------- apps/openmw/mwrender/animation.hpp | 3 --- apps/openmw/mwrender/npcanimation.cpp | 8 +++----- apps/openmw/mwrender/npcanimation.hpp | 2 +- apps/openmw/mwrender/objectpaging.cpp | 11 +++++++++++ components/nifosg/controller.cpp | 16 ++++++++-------- components/nifosg/controller.hpp | 8 +++++--- components/nifosg/nifloader.cpp | 18 +++++++++++++----- components/sceneutil/statesetupdater.cpp | 22 +++++++++++++++------- components/sceneutil/statesetupdater.hpp | 2 +- 10 files changed, 62 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8f8e8c233..48c58f247 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -513,6 +513,9 @@ namespace MWRender if (mShadowUniform) stateset->addUniform(mShadowUniform); + stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); + // FIXME: overriding diffuse/ambient/emissive colors osg::Material* material = new osg::Material; material->setColorMode(osg::Material::OFF); @@ -1741,31 +1744,16 @@ namespace MWRender if (mTransparencyUpdater == nullptr) { mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform()); - mObjectRoot->addUpdateCallback(mTransparencyUpdater); + mObjectRoot->addCullCallback(mTransparencyUpdater); } else mTransparencyUpdater->setAlpha(alpha); } else { - mObjectRoot->removeUpdateCallback(mTransparencyUpdater); + mObjectRoot->removeCullCallback(mTransparencyUpdater); mTransparencyUpdater = nullptr; - mObjectRoot->setStateSet(nullptr); - } - - setRenderBin(); - } - - void Animation::setRenderBin() - { - if (mAlpha != 1.f) - { - osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet(); - stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); } - else if (osg::StateSet* stateset = mObjectRoot->getStateSet()) - stateset->setRenderBinToInherit(); } void Animation::setLightEffect(float effect) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 2890e7be4..c53cf98a9 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -336,9 +336,6 @@ protected: */ virtual void addControllers(); - /// Set the render bin for this animation's object root. May be customized by subclasses. - virtual void setRenderBin(); - public: Animation(const MWWorld::Ptr &ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ec825ca2f..6e7669976 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -435,12 +435,10 @@ void NpcAnimation::setRenderBin() osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin); prototypeAdded = true; } - - osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet(); - stateset->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); + mObjectRoot->getOrCreateStateSet()->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); } - else - Animation::setRenderBin(); + else if (osg::StateSet* stateset = mObjectRoot->getStateSet()) + stateset->setRenderBinToInherit(); } void NpcAnimation::rebuild() diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index e102f5097..7f8d5434b 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -88,7 +88,7 @@ private: void addPartGroup(int group, int priority, const std::vector &parts, bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr); - virtual void setRenderBin(); + void setRenderBin(); osg::ref_ptr mFirstPersonNeckController; diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index c3a90da80..b138a6412 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -162,6 +162,17 @@ namespace MWRender { if (callback->className() == std::string("BillboardCallback")) handleBillboard(cloned); + else + { + if (node->getCullCallback()->getNestedCallback()) + { + osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); + clonedCallback->setNestedCallback(nullptr); + cloned->addCullCallback(clonedCallback); + } + else + cloned->addCullCallback(const_cast(callback)); + } callback = callback->getNestedCallback(); } } diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index a088ead4c..b5fd374e3 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -352,8 +352,9 @@ void RollController::operator() (osg::Node* node, osg::NodeVisitor* nv) } } -AlphaController::AlphaController(const Nif::NiFloatData *data) +AlphaController::AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial) : mData(data->mKeyList, 1.f) + , mBaseMaterial(baseMaterial) { } @@ -365,14 +366,13 @@ AlphaController::AlphaController() AlphaController::AlphaController(const AlphaController ©, const osg::CopyOp ©op) : StateSetUpdater(copy, copyop), Controller(copy) , mData(copy.mData) + , mBaseMaterial(copy.mBaseMaterial) { } void AlphaController::setDefaults(osg::StateSet *stateset) { - // need to create a deep copy of StateAttributes we will modify - osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); - stateset->setAttribute(osg::clone(mat, osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); } void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) @@ -387,9 +387,10 @@ void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) } } -MaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color) +MaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial) : mData(data->mKeyList, osg::Vec3f(1,1,1)) , mTargetColor(color) + , mBaseMaterial(baseMaterial) { } @@ -401,14 +402,13 @@ MaterialColorController::MaterialColorController(const MaterialColorController & : StateSetUpdater(copy, copyop), Controller(copy) , mData(copy.mData) , mTargetColor(copy.mTargetColor) + , mBaseMaterial(copy.mBaseMaterial) { } void MaterialColorController::setDefaults(osg::StateSet *stateset) { - // need to create a deep copy of StateAttributes we will modify - osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); - stateset->setAttribute(osg::clone(mat, osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); } void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 3f66013a2..c81f97a71 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -24,6 +24,7 @@ namespace osg { class Node; class StateSet; + class Material; } namespace osgParticle @@ -268,9 +269,9 @@ namespace NifOsg { private: FloatInterpolator mData; - + osg::ref_ptr mBaseMaterial; public: - AlphaController(const Nif::NiFloatData *data); + AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial); AlphaController(); AlphaController(const AlphaController& copy, const osg::CopyOp& copyop); @@ -291,7 +292,7 @@ namespace NifOsg Specular = 2, Emissive = 3 }; - MaterialColorController(const Nif::NiPosData *data, TargetColor color); + MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial); MaterialColorController(); MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop); @@ -304,6 +305,7 @@ namespace NifOsg private: Vec3Interpolator mData; TargetColor mTargetColor = Ambient; + osg::ref_ptr mBaseMaterial; }; class FlipController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index bff414707..88e89b400 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -639,7 +639,15 @@ namespace NifOsg handleParticleSystem(nifNode, node, composite, animflags, rootNode); if (composite->getNumControllers() > 0) - node->addUpdateCallback(composite); + { + osg::Callback *cb = composite; + if (composite->getNumControllers() == 1) + cb = composite->getController(0); + if (animflags & Nif::NiNode::AnimFlag_AutoPlay) + node->addCullCallback(cb); + else + node->addUpdateCallback(cb); // have to remain as UpdateCallback so AssignControllerSourcesVisitor can find it. + } bool isAnimated = false; handleNodeControllers(nifNode, node, animflags, isAnimated); @@ -778,7 +786,7 @@ namespace NifOsg } } - void handleMaterialControllers(const Nif::Property *materialProperty, SceneUtil::CompositeStateSetUpdater* composite, int animflags) + void handleMaterialControllers(const Nif::Property *materialProperty, SceneUtil::CompositeStateSetUpdater* composite, int animflags, const osg::Material* baseMaterial) { for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { @@ -789,7 +797,7 @@ namespace NifOsg const Nif::NiAlphaController* alphactrl = static_cast(ctrl.getPtr()); if (alphactrl->data.empty()) continue; - osg::ref_ptr osgctrl(new AlphaController(alphactrl->data.getPtr())); + osg::ref_ptr osgctrl(new AlphaController(alphactrl->data.getPtr(), baseMaterial)); setupController(alphactrl, osgctrl, animflags); composite->addController(osgctrl); } @@ -799,7 +807,7 @@ namespace NifOsg if (matctrl->data.empty()) continue; auto targetColor = static_cast(matctrl->targetColor); - osg::ref_ptr osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor)); + osg::ref_ptr osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor, baseMaterial)); setupController(matctrl, osgctrl, animflags); composite->addController(osgctrl); } @@ -1767,7 +1775,7 @@ namespace NifOsg if (!matprop->controller.empty()) { hasMatCtrl = true; - handleMaterialControllers(matprop, composite, animflags); + handleMaterialControllers(matprop, composite, animflags, mat); } break; diff --git a/components/sceneutil/statesetupdater.cpp b/components/sceneutil/statesetupdater.cpp index 121cdaca6..08418f331 100644 --- a/components/sceneutil/statesetupdater.cpp +++ b/components/sceneutil/statesetupdater.cpp @@ -2,30 +2,38 @@ #include #include +#include namespace SceneUtil { void StateSetUpdater::operator()(osg::Node* node, osg::NodeVisitor* nv) { + bool isCullVisitor = nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR; if (!mStateSets[0]) { - // first time setup - osg::StateSet* src = node->getOrCreateStateSet(); - for (int i=0; i<2; ++i) // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first - // This can be done conveniently in user implementations of the setDefaults() method + for (int i=0; i<2; ++i) { - mStateSets[i] = new osg::StateSet(*src, osg::CopyOp::SHALLOW_COPY); + if (!isCullVisitor) + mStateSets[i] = new osg::StateSet(*node->getOrCreateStateSet(), osg::CopyOp::SHALLOW_COPY); // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first in setDefaults + else + mStateSets[i] = new osg::StateSet; setDefaults(mStateSets[i]); } } osg::StateSet* stateset = mStateSets[nv->getTraversalNumber()%2]; - node->setStateSet(stateset); - apply(stateset, nv); + if (!isCullVisitor) + node->setStateSet(stateset); + else + static_cast(nv)->pushStateSet(stateset); + traverse(node, nv); + + if (isCullVisitor) + static_cast(nv)->popStateSet(); } void StateSetUpdater::reset() diff --git a/components/sceneutil/statesetupdater.hpp b/components/sceneutil/statesetupdater.hpp index 51398844c..d12316fb2 100644 --- a/components/sceneutil/statesetupdater.hpp +++ b/components/sceneutil/statesetupdater.hpp @@ -13,7 +13,7 @@ namespace SceneUtil /// traversals run in parallel can yield up to 200% framerates. /// @par Race conditions are prevented using a "double buffering" scheme - we have two StateSets that take turns, /// one StateSet we can write to, the second one is currently in use by the draw traversal of the last frame. - /// @par Must be set as UpdateCallback on a Node. + /// @par Must be set as UpdateCallback or CullCallback on a Node. If set as a CullCallback, the StateSetUpdater operates on an empty StateSet, otherwise it operates on a clone of the node's existing StateSet. /// @note Do not add the same StateSetUpdater to multiple nodes. /// @note Do not add multiple StateSetControllers on the same Node as they will conflict - instead use the CompositeStateSetUpdater. class StateSetUpdater : public osg::NodeCallback From 66c9469a804d772d56f60e0329b46d48d69432e4 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 207/227] fix Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index b138a6412..8cff46fa8 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -444,15 +444,12 @@ namespace MWRender if (size < 1.f) { osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE; - cellPos.x() = std::max(cellPos.x(), std::floor(minBound.x())); - cellPos.x() = std::min(cellPos.x(), std::ceil(maxBound.x())); - cellPos.y() = std::max(cellPos.y(), std::floor(minBound.y())); - cellPos.y() = std::min(cellPos.y(), std::ceil(maxBound.y())); - if (cellPos.x() < minBound.x() || cellPos.x() > maxBound.x() || cellPos.y() < minBound.y() || cellPos.y() > maxBound.y()) + if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y())) + continue; + if ((maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y())) continue; } - float d = (viewPoint - pos).length(); if (!activeGrid) { @@ -619,7 +616,7 @@ namespace MWRender if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()) + if ((relativeViewPoint - mergeGroup->getBound().center()).length() > mergeGroup->getBound().radius()*2) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); @@ -630,17 +627,16 @@ namespace MWRender group->addChild(mergeGroup); - if (compile) - { - stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; - mergeGroup->accept(stateToCompile); - } - if (mDebugBatches) { DebugVisitor dv; mergeGroup->accept(dv); } + if (compile) + { + stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + mergeGroup->accept(stateToCompile); + } } auto ico = mSceneManager->getIncrementalCompileOperation(); From 4e2efb3cdb6d4ab3cbf4b120bfc6d3a70062de1a Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 15 May 2020 13:37:00 +0000 Subject: [PATCH 208/227] avoid sqrt Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 8cff46fa8..dbe99b0e1 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -450,14 +450,14 @@ namespace MWRender continue; } - float d = (viewPoint - pos).length(); + float dSqr = (viewPoint - pos).length2(); if (!activeGrid) { OpenThreads::ScopedLock lock(mSizeCacheMutex); SizeCache::iterator found = mSizeCache.find(pair.first); if (found != mSizeCache.end()) { - if (found->second < d*minSize) + if (found->second < dSqr*minSize*minSize) continue; } } @@ -503,12 +503,12 @@ namespace MWRender continue; } - float radius = cnode->getBound().radius() * ref.mScale; - if (radius < d*minSize) + float radius2 = cnode->getBound().radius2() * ref.mScale*ref.mScale; + if (radius2 < dSqr*minSize*minSize) { OpenThreads::ScopedLock lock(mSizeCacheMutex); { - mSizeCache[pair.first] = radius; + mSizeCache[pair.first] = radius2; } continue; } @@ -551,13 +551,8 @@ namespace MWRender const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); - if (minSizeMerged != minSize) - { - float d = (viewPoint - pos).length(); - float radius = cnode->getBound().radius() * cref->mScale; - if (radius < d*minSizeMerged) - continue; - } + if (minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged) + continue; osg::Matrixf matrix; matrix.preMultTranslate(pos - worldCenter); @@ -616,7 +611,7 @@ namespace MWRender if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - mergeGroup->getBound().center()).length() > mergeGroup->getBound().radius()*2) + if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()*2*2) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); From daa2761c2d01120d2fad597b25668a2563cb712f Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sat, 16 May 2020 13:37:00 +0000 Subject: [PATCH 209/227] alphablending & billboardfix Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 68 ++++++++++------------ components/sceneutil/mwshadowtechnique.cpp | 5 +- components/sceneutil/optimizer.cpp | 7 +++ 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index dbe99b0e1..d65fffde8 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -95,21 +95,15 @@ namespace MWRender } virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Node* node,unsigned int option) const { - return true; + return (node->getDataVariance() != osg::Object::DYNAMIC); } }; class CopyOp : public osg::CopyOp { public: - CopyOp(bool deep) : mSqrDistance(0.f) { - unsigned int flags = osg::CopyOp::DEEP_COPY_NODES; - if (deep) - flags |= osg::CopyOp::DEEP_COPY_DRAWABLES; - setCopyFlags(flags); - } - - float mSqrDistance; + bool mOptimizeBillboards = true; + float mSqrDistance = 0.f; osg::Vec3f mViewVector; mutable std::vector mNodePath; @@ -157,23 +151,27 @@ namespace MWRender } void handleCallbacks(const osg::Node* node, osg::Node *cloned) const { - const osg::Callback* callback = node->getCullCallback(); - while (callback) + for (const osg::Callback* callback = node->getCullCallback(); callback != nullptr; callback = callback->getNestedCallback()) { if (callback->className() == std::string("BillboardCallback")) - handleBillboard(cloned); - else { - if (node->getCullCallback()->getNestedCallback()) + if (mOptimizeBillboards) { - osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); - clonedCallback->setNestedCallback(nullptr); - cloned->addCullCallback(clonedCallback); + handleBillboard(cloned); + continue; } else - cloned->addCullCallback(const_cast(callback)); + cloned->setDataVariance(osg::Object::DYNAMIC); } - callback = callback->getNestedCallback(); + + if (node->getCullCallback()->getNestedCallback()) + { + osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); + clonedCallback->setNestedCallback(nullptr); + cloned->addCullCallback(clonedCallback); + } + else + cloned->addCullCallback(const_cast(callback)); } } void handleBillboard(osg::Node* node) const @@ -414,11 +412,11 @@ namespace MWRender } } + if (activeGrid) { OpenThreads::ScopedLock lock(mRefTrackerMutex); - if (activeGrid) - for (auto ref : getRefTracker().mBlacklist) - refs.erase(ref); + for (auto ref : getRefTracker().mBlacklist) + refs.erase(ref); } osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); @@ -444,9 +442,8 @@ namespace MWRender if (size < 1.f) { osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE; - if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y())) - continue; - if ((maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y())) + if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y()) + || (maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y())) continue; } @@ -455,11 +452,8 @@ namespace MWRender { OpenThreads::ScopedLock lock(mSizeCacheMutex); SizeCache::iterator found = mSizeCache.find(pair.first); - if (found != mSizeCache.end()) - { - if (found->second < dSqr*minSize*minSize) - continue; - } + if (found != mSizeCache.end() && found->second < dSqr*minSize*minSize) + continue; } std::string id = Misc::StringUtils::lowerCase(ref.mRefID); @@ -504,12 +498,10 @@ namespace MWRender } float radius2 = cnode->getBound().radius2() * ref.mScale*ref.mScale; - if (radius2 < dSqr*minSize*minSize) + if (radius2 < dSqr*minSize*minSize && !activeGrid) { OpenThreads::ScopedLock lock(mSizeCacheMutex); - { - mSizeCache[pair.first] = radius2; - } + mSizeCache[pair.first] = radius2; continue; } @@ -551,7 +543,7 @@ namespace MWRender const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); - if (minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged) + if (!activeGrid && minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged) continue; osg::Matrixf matrix; @@ -563,7 +555,9 @@ namespace MWRender osg::ref_ptr trans = new osg::MatrixTransform(matrix); trans->setDataVariance(osg::Object::STATIC); - CopyOp co = CopyOp(merge); + CopyOp co; + co.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES); + co.mOptimizeBillboards = (size > 1/4.f); co.mNodePath.push_back(trans); co.mSqrDistance = (viewPoint - pos).length2(); co.mViewVector = (viewPoint - worldCenter); @@ -611,7 +605,7 @@ namespace MWRender if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()*2*2) + if (size > 1/8.f) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index cb3a1b278..98771bbb4 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -1580,7 +1581,9 @@ void MWShadowTechnique::createShaders() _shadowCastingStateSet->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON); _shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false)); _shadowCastingStateSet->addUniform(_shadowMapAlphaTestDisableUniform); - + osg::ref_ptr depth = new osg::Depth; + depth->setWriteMask(true); + _shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON); _shadowCastingStateSet->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "RenderBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS); diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index f0da9e7e2..71cb8a2e5 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1543,6 +1544,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) geom->setUseVertexBufferObjects(true); geom->setUseDisplayList(false); } + if (_alphaBlendingActive && _mergeAlphaBlending && !geom->getStateSet()) + { + osg::Depth* d = new osg::Depth; + d->setWriteMask(0); + geom->getOrCreateStateSet()->setAttribute(d); + } } } From 26ab1763893e19b5f4d0b9eded3b86956829fa2f Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sat, 16 May 2020 13:37:00 +0000 Subject: [PATCH 210/227] profiling Signed-off-by: Bret Curtis --- apps/openmw/mwrender/animation.cpp | 2 +- apps/openmw/mwrender/objectpaging.cpp | 80 ++++++++++++++------------ apps/openmw/mwworld/store.cpp | 18 ++---- components/esm/cellref.cpp | 16 ------ components/esm/cellref.hpp | 16 +++++- components/nifosg/controller.cpp | 4 +- components/nifosg/particle.cpp | 2 +- components/resource/scenemanager.cpp | 2 +- components/sceneutil/clone.cpp | 19 ++---- components/sceneutil/clone.hpp | 2 - components/sceneutil/morphgeometry.cpp | 2 +- components/sceneutil/optimizer.cpp | 4 +- components/sceneutil/riggeometry.cpp | 6 +- 13 files changed, 78 insertions(+), 95 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 48c58f247..594713a1c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1372,7 +1372,7 @@ namespace MWRender osg::Group* sheathParent = findVisitor.mFoundNode; if (sheathParent) { - osg::Node* copy = osg::clone(nodePair.first, osg::CopyOp::DEEP_COPY_NODES); + osg::Node* copy = static_cast(nodePair.first->clone(osg::CopyOp::DEEP_COPY_NODES)); sheathParent->addChild(copy); } } diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index d65fffde8..71268a52c 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -107,8 +107,23 @@ namespace MWRender osg::Vec3f mViewVector; mutable std::vector mNodePath; + void copy(const osg::Node* toCopy, osg::Group* attachTo) + { + const osg::Group* groupToCopy = toCopy->asGroup(); + if (toCopy->getStateSet() || toCopy->asTransform() || !groupToCopy) + attachTo->addChild(operator()(toCopy)); + else + { + for (unsigned int i=0; igetNumChildren(); ++i) + attachTo->addChild(operator()(groupToCopy->getChild(i))); + } + } + virtual osg::Node* operator() (const osg::Node* node) const { + if (const osg::Drawable* d = node->asDrawable()) + return operator()(d); + if (dynamic_cast(node)) return nullptr; if (dynamic_cast(node)) @@ -133,12 +148,9 @@ namespace MWRender return n; } - if (const osg::Drawable* d = node->asDrawable()) - return operator()(d); - mNodePath.push_back(node); - osg::Node* cloned = osg::clone(node, *this); + osg::Node* cloned = static_cast(node->clone(*this)); cloned->setDataVariance(osg::Object::STATIC); cloned->setUserDataContainer(nullptr); cloned->setName(""); @@ -222,14 +234,14 @@ namespace MWRender if (getCopyFlags() & DEEP_COPY_DRAWABLES) { - osg::Drawable* d = osg::clone(drawable, *this); + osg::Drawable* d = static_cast(drawable->clone(*this)); d->setDataVariance(osg::Object::STATIC); d->setUserDataContainer(nullptr); d->setName(""); return d; } else - return osg::CopyOp::operator()(drawable); + return const_cast(drawable); } virtual osg::Callback* operator() (const osg::Callback* callback) const { @@ -388,8 +400,9 @@ namespace MWRender bool deleted = false; while(cell->getNextRef(esm[index], ref, deleted)) { + Misc::StringUtils::lowerCaseInPlace(ref.mRefID); if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; - int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + int type = store.findStatic(ref.mRefID); if (!typeFilter(type,size>=2)) continue; if (deleted) { refs.erase(ref.mRefNum); continue; } refs[ref.mRefNum] = ref; @@ -403,9 +416,10 @@ namespace MWRender for (ESM::CellRefTracker::const_iterator it = cell->mLeasedRefs.begin(); it != cell->mLeasedRefs.end(); ++it) { ESM::CellRef ref = it->first; + Misc::StringUtils::lowerCaseInPlace(ref.mRefID); bool deleted = it->second; if (deleted) { refs.erase(ref.mRefNum); continue; } - int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + int type = store.findStatic(ref.mRefID); if (!typeFilter(type,size>=2)) continue; refs[ref.mRefNum] = ref; } @@ -456,28 +470,23 @@ namespace MWRender continue; } - std::string id = Misc::StringUtils::lowerCase(ref.mRefID); - if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + if (ref.mRefID == "prisonmarker" || ref.mRefID == "divinemarker" || ref.mRefID == "templemarker" || ref.mRefID == "northmarker") continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - int type = store.findStatic(id); - std::string model = getModel(type, id, store); + int type = store.findStatic(ref.mRefID); + std::string model = getModel(type, ref.mRefID, store); if (model.empty()) continue; model = "meshes/" + model; - bool useAnim = type != ESM::REC_STAT; - if (useAnim) + if (activeGrid && type != ESM::REC_STAT) { model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); - if (activeGrid) + std::string kfname = Misc::StringUtils::lowerCase(model); + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) { - std::string kfname = Misc::StringUtils::lowerCase(model); - if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) - { - kfname.replace(kfname.size()-4, 4, ".kf"); - if (mSceneManager->getVFS()->exists(kfname)) - continue; - } + kfname.replace(kfname.size()-4, 4, ".kf"); + if (mSceneManager->getVFS()->exists(kfname)) + continue; } } @@ -521,6 +530,7 @@ namespace MWRender osg::ref_ptr mergeGroup = new osg::Group; osg::ref_ptr templateRefs = new TemplateRef; osgUtil::StateToCompile stateToCompile(0, nullptr); + CopyOp copyop; for (const auto& pair : nodes) { const osg::Node* cnode = pair.first; @@ -555,35 +565,29 @@ namespace MWRender osg::ref_ptr trans = new osg::MatrixTransform(matrix); trans->setDataVariance(osg::Object::STATIC); - CopyOp co; - co.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES); - co.mOptimizeBillboards = (size > 1/4.f); - co.mNodePath.push_back(trans); - co.mSqrDistance = (viewPoint - pos).length2(); - co.mViewVector = (viewPoint - worldCenter); - osg::ref_ptr node = osg::clone(cnode, co); - node->setUserDataContainer(nullptr); + copyop.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES); + copyop.mOptimizeBillboards = (size > 1/4.f); + copyop.mNodePath.push_back(trans); + copyop.mSqrDistance = (viewPoint - pos).length2(); + copyop.mViewVector = (viewPoint - worldCenter); + copyop.copy(cnode, trans); if (activeGrid) { if (merge) { AddRefnumMarkerVisitor visitor(ref.mRefNum); - node->accept(visitor); + trans->accept(visitor); } else { osg::ref_ptr marker = new RefnumMarker; marker->mRefnum = ref.mRefNum; - node->getOrCreateUserDataContainer()->addUserObject(marker); + trans->getOrCreateUserDataContainer()->addUserObject(marker); } } - trans->addChild(node); - - if (merge) - mergeGroup->addChild(trans); - else - group->addChild(trans); + osg::Group* attachTo = merge ? mergeGroup : group; + attachTo->addChild(trans); ++numinstances; } if (numinstances > 0) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 93f11d4fd..4c4417587 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -139,15 +139,12 @@ namespace MWWorld std::string idLower = Misc::StringUtils::lowerCase(id); typename Dynamic::const_iterator dit = mDynamic.find(idLower); - if (dit != mDynamic.end()) { + if (dit != mDynamic.end()) return &dit->second; - } typename std::map::const_iterator it = mStatic.find(idLower); - - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) return &(it->second); - } return 0; } @@ -156,10 +153,8 @@ namespace MWWorld { std::string idLower = Misc::StringUtils::lowerCase(id); typename std::map::const_iterator it = mStatic.find(idLower); - - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) return &(it->second); - } return 0; } @@ -289,7 +284,7 @@ namespace MWWorld typename std::map::iterator it = mStatic.find(idLower); - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) { // delete from the static part of mShared typename std::vector::iterator sharedIter = mShared.begin(); typename std::vector::iterator end = sharedIter + mStatic.size(); @@ -566,7 +561,7 @@ namespace MWWorld std::map::const_iterator it = mInt.find(cell.mName); - if (it != mInt.end() && Misc::StringUtils::ciEqual(it->second.mName, id)) { + if (it != mInt.end()) { return &(it->second); } @@ -1129,9 +1124,8 @@ namespace MWWorld { auto it = mStatic.find(Misc::StringUtils::lowerCase(id)); - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) mStatic.erase(it); - } return true; } diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index ab6ba2754..4b9852d65 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -232,19 +232,3 @@ void ESM::CellRef::blank() mPos.rot[i] = 0; } } - -bool ESM::operator== (const RefNum& left, const RefNum& right) -{ - return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile; -} - -bool ESM::operator< (const RefNum& left, const RefNum& right) -{ - if (left.mIndexright.mIndex) - return false; - - return left.mContentFileright.mIndex) + return false; + return left.mContentFilesetAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(static_cast(mBaseMaterial->clone(osg::CopyOp::DEEP_COPY_ALL)), osg::StateAttribute::ON); } void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) @@ -408,7 +408,7 @@ MaterialColorController::MaterialColorController(const MaterialColorController & void MaterialColorController::setDefaults(osg::StateSet *stateset) { - stateset->setAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(static_cast(mBaseMaterial->clone(osg::CopyOp::DEEP_COPY_ALL)), osg::StateAttribute::ON); } void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 1b1e469bc..804a8f8ab 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -277,7 +277,7 @@ Emitter::Emitter(const Emitter ©, const osg::CopyOp ©op) , mPlacer(copy.mPlacer) , mShooter(copy.mShooter) // need a deep copy because the remainder is stored in the object - , mCounter(osg::clone(copy.mCounter.get(), osg::CopyOp::DEEP_COPY_ALL)) + , mCounter(static_cast(copy.mCounter->clone(osg::CopyOp::DEEP_COPY_ALL))) { } diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 29b312670..d06a07273 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -577,7 +577,7 @@ namespace Resource osg::ref_ptr SceneManager::createInstance(const osg::Node *base) { - osg::ref_ptr cloned = osg::clone(base, SceneUtil::CopyOp()); + osg::ref_ptr cloned = static_cast(base->clone(SceneUtil::CopyOp())); // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache cloned->getOrCreateUserDataContainer()->addUserObject(new TemplateRef(base)); diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp index 0df0f4a5b..c3261515d 100644 --- a/components/sceneutil/clone.cpp +++ b/components/sceneutil/clone.cpp @@ -22,20 +22,11 @@ namespace SceneUtil | osg::CopyOp::DEEP_COPY_USERDATA); } - osg::StateSet* CopyOp::operator ()(const osg::StateSet* stateset) const - { - if (!stateset) - return nullptr; - if (stateset->getDataVariance() == osg::StateSet::DYNAMIC) - return osg::clone(stateset, *this); - return const_cast(stateset); - } - osg::Object* CopyOp::operator ()(const osg::Object* node) const { // We should copy node transformations when we copy node - if (const NifOsg::NodeUserData* data = dynamic_cast(node)) - return osg::clone(data, *this); + if (dynamic_cast(node)) + return static_cast(node->clone(*this)); return osg::CopyOp::operator()(node); } @@ -60,7 +51,7 @@ namespace SceneUtil if (dynamic_cast(drawable) || dynamic_cast(drawable)) { - return osg::clone(drawable, *this); + return static_cast(drawable->clone(*this)); } return osg::CopyOp::operator()(drawable); @@ -68,7 +59,7 @@ namespace SceneUtil osgParticle::ParticleProcessor* CopyOp::operator() (const osgParticle::ParticleProcessor* processor) const { - osgParticle::ParticleProcessor* cloned = osg::clone(processor, osg::CopyOp::DEEP_COPY_CALLBACKS); + osgParticle::ParticleProcessor* cloned = static_cast(processor->clone(osg::CopyOp::DEEP_COPY_CALLBACKS)); for (const auto& oldPsNewPsPair : mOldPsToNewPs) { if (processor->getParticleSystem() == oldPsNewPsPair.first) @@ -84,7 +75,7 @@ namespace SceneUtil osgParticle::ParticleSystem* CopyOp::operator ()(const osgParticle::ParticleSystem* partsys) const { - osgParticle::ParticleSystem* cloned = osg::clone(partsys, *this); + osgParticle::ParticleSystem* cloned = static_cast(partsys->clone(*this)); for (const auto& processorPsPair : mProcessorToOldPs) { diff --git a/components/sceneutil/clone.hpp b/components/sceneutil/clone.hpp index 20788799f..cf6d79e68 100644 --- a/components/sceneutil/clone.hpp +++ b/components/sceneutil/clone.hpp @@ -17,7 +17,6 @@ namespace SceneUtil /// @par Defines the cloning behaviour we need: /// * Assigns updated ParticleSystem pointers on cloned emitters and programs. - /// * Creates deep copy of StateSets if they have a DYNAMIC data variance. /// * Deep copies RigGeometry and MorphGeometry so they can animate without affecting clones. /// @warning Do not use an object of this class for more than one copy operation. class CopyOp : public osg::CopyOp @@ -31,7 +30,6 @@ namespace SceneUtil virtual osg::Node* operator() (const osg::Node* node) const; virtual osg::Drawable* operator() (const osg::Drawable* drawable) const; - virtual osg::StateSet* operator() (const osg::StateSet* stateset) const; virtual osg::Object* operator ()(const osg::Object* node) const; private: diff --git a/components/sceneutil/morphgeometry.cpp b/components/sceneutil/morphgeometry.cpp index 01bb35c45..04fd6fb36 100644 --- a/components/sceneutil/morphgeometry.cpp +++ b/components/sceneutil/morphgeometry.cpp @@ -44,7 +44,7 @@ void MorphGeometry::setSourceGeometry(osg::ref_ptr sourceGeom) osg::ref_ptr vbo (new osg::VertexBufferObject); vbo->setUsage(GL_DYNAMIC_DRAW_ARB); - osg::ref_ptr vertexArray = osg::clone(from.getVertexArray(), osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr vertexArray = static_cast(from.getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL)); if (vertexArray) { vertexArray->setVertexBufferObject(vbo); diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 71cb8a2e5..e8a945114 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -600,7 +600,7 @@ bool needvbo(const osg::Geometry* geom) osg::Array* cloneArray(osg::Array* array, osg::VertexBufferObject*& vbo, const osg::Geometry* geom) { - array = osg::clone(array, osg::CopyOp::DEEP_COPY_ALL); + array = static_cast(array->clone(osg::CopyOp::DEEP_COPY_ALL)); if (!vbo && needvbo(geom)) vbo = new osg::VertexBufferObject; if (vbo) @@ -1135,7 +1135,7 @@ osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps, osg::ElementBufferObjec { if (ps->referenceCount() <= 1) return ps; - ps = osg::clone(ps, osg::CopyOp::DEEP_COPY_ALL); + ps = static_cast(ps->clone(osg::CopyOp::DEEP_COPY_ALL)); osg::DrawElements* drawElements = ps->getDrawElements(); if (!drawElements) return ps; diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 627353b99..b9201fdf6 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -77,7 +77,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) osg::ref_ptr vbo (new osg::VertexBufferObject); vbo->setUsage(GL_DYNAMIC_DRAW_ARB); - osg::ref_ptr vertexArray = osg::clone(from.getVertexArray(), osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr vertexArray = static_cast(from.getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL)); if (vertexArray) { vertexArray->setVertexBufferObject(vbo); @@ -86,7 +86,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) if (const osg::Array* normals = from.getNormalArray()) { - osg::ref_ptr normalArray = osg::clone(normals, osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr normalArray = static_cast(normals->clone(osg::CopyOp::DEEP_COPY_ALL)); if (normalArray) { normalArray->setVertexBufferObject(vbo); @@ -97,7 +97,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) if (const osg::Vec4Array* tangents = dynamic_cast(from.getTexCoordArray(7))) { mSourceTangents = tangents; - osg::ref_ptr tangentArray = osg::clone(tangents, osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr tangentArray = static_cast(tangents->clone(osg::CopyOp::DEEP_COPY_ALL)); tangentArray->setVertexBufferObject(vbo); to.setTexCoordArray(7, tangentArray, osg::Array::BIND_PER_VERTEX); } From 13c4e4b2a990516d0413615247baf7bd4e0006b7 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 12 Jun 2020 22:35:38 +0000 Subject: [PATCH 211/227] Fix ifs for ACTIVATE_MSVC --- CI/before_script.msvc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 90654559b..2ecea7ebc 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -957,7 +957,7 @@ fi echo #fi -if ! [ -z $ACTIVATE_MSVC ]; then +if [ -n "$ACTIVATE_MSVC" ]; then echo -n "- Activating MSVC in the current shell... " command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; } @@ -1007,7 +1007,7 @@ if [ -z $VERBOSE ]; then fi fi -if [ -n $ACTIVATE_MSVC ]; then +if [ -n "$ACTIVATE_MSVC" ]; then echo echo "Note: you must manually activate MSVC for the shell in which you want to do the build." echo From 0d2129ca135834d5eab7fefc7179e42f8363b891 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 12 Jun 2020 22:50:06 +0000 Subject: [PATCH 212/227] Add success message to Windows prebuild script --- CI/before_script.msvc.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 90654559b..deeadb9e8 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -1007,6 +1007,9 @@ if [ -z $VERBOSE ]; then fi fi +echo "Script completed successfully." +echo "You now have an OpenMW build system at $(unixPathAsWindows "$(pwd)")" + if [ -n $ACTIVATE_MSVC ]; then echo echo "Note: you must manually activate MSVC for the shell in which you want to do the build." From a35497de0cc18a9797584716d7fea53ea3c1dbb7 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 13 Jun 2020 00:39:57 +0200 Subject: [PATCH 213/227] Remove redundant runSpeed as always equal to walkSpeed --- apps/openmw/mwclass/creature.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 71c96b06b..470a28b7e 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -513,17 +513,12 @@ namespace MWClass const GMST& gmst = getGmst(); - float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() + const float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat()); const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); - bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); - - // The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp) - float runSpeed = walkSpeed; - float moveSpeed; if(getEncumbrance(ptr) > getCapacity(ptr)) @@ -542,15 +537,11 @@ namespace MWClass else if(world->isSwimming(ptr)) { float swimSpeed = walkSpeed; - if(running) - swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat(); moveSpeed = swimSpeed; } - else if(running) - moveSpeed = runSpeed; else moveSpeed = walkSpeed; if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) From 439588d10ec6c53c99ce942fc677b5f829a3902c Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 13 Jun 2020 02:09:10 +0200 Subject: [PATCH 214/227] Remove unused mOffMeshConnectionIds --- components/detournavigator/navigatorimpl.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 66a4d8bb3..3a81adb97 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -64,7 +64,6 @@ namespace DetourNavigator std::map mAgents; std::unordered_map mAvoidIds; std::unordered_map mWaterIds; - std::multimap mOffMeshConnectionIds; void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId); void updateWaterShapeId(const ObjectId id, const ObjectId waterId); From 374b85a00d2f5e1e40b7ca9bc84ac9804236ba77 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 13 Jun 2020 01:04:55 +0200 Subject: [PATCH 215/227] Add Class methods to get walk, run, swim speed --- apps/openmw/mwclass/creature.cpp | 43 +++++++++++++----- apps/openmw/mwclass/creature.hpp | 6 +++ apps/openmw/mwclass/npc.cpp | 76 +++++++++++++++++++++++--------- apps/openmw/mwclass/npc.hpp | 6 +++ apps/openmw/mwworld/class.cpp | 15 +++++++ apps/openmw/mwworld/class.hpp | 6 +++ 6 files changed, 119 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 470a28b7e..88defbaa0 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -507,15 +507,13 @@ namespace MWClass float Creature::getSpeed(const MWWorld::Ptr &ptr) const { - MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) return 0.f; const GMST& gmst = getGmst(); - const float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() - * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat()); - const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); @@ -535,15 +533,9 @@ namespace MWClass moveSpeed = flySpeed; } else if(world->isSwimming(ptr)) - { - float swimSpeed = walkSpeed; - swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); - swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * - gmst.fSwimRunAthleticsMult->mValue.getFloat(); - moveSpeed = swimSpeed; - } + moveSpeed = getSwimSpeed(ptr); else - moveSpeed = walkSpeed; + moveSpeed = getWalkSpeed(ptr); if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) moveSpeed *= 0.75f; @@ -880,4 +872,31 @@ namespace MWClass { MWMechanics::setBaseAISetting(id, setting, value); } + + float Creature::getWalkSpeed(const MWWorld::Ptr& ptr) const + { + const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + const GMST& gmst = getGmst(); + + return gmst.fMinWalkSpeedCreature->mValue.getFloat() + + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() + * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat()); + } + + float Creature::getRunSpeed(const MWWorld::Ptr& ptr) const + { + return getWalkSpeed(ptr); + } + + float Creature::getSwimSpeed(const MWWorld::Ptr& ptr) const + { + const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + const GMST& gmst = getGmst(); + const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects(); + + return getWalkSpeed(ptr) + * (1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude()) + * (gmst.fSwimRunBase->mValue.getFloat() + + 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat()); + } } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 3288c0d11..9071b9a33 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -131,6 +131,12 @@ namespace MWClass /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; + + float getWalkSpeed(const MWWorld::Ptr& ptr) const final; + + float getRunSpeed(const MWWorld::Ptr& ptr) const final; + + float getSwimSpeed(const MWWorld::Ptr& ptr) const final; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 3b9c46c8f..319d5f014 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -947,16 +947,6 @@ namespace MWClass bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr); running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr)); - float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* - (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat()); - walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance; - walkSpeed = std::max(0.0f, walkSpeed); - if(sneaking) - walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat(); - - float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * - gmst.fAthleticsRunBonus->mValue.getFloat() + gmst.fBaseRunMultiplier->mValue.getFloat()); - float moveSpeed; if(getEncumbrance(ptr) > getCapacity(ptr)) moveSpeed = 0.0f; @@ -971,19 +961,11 @@ namespace MWClass moveSpeed = flySpeed; } else if (swimming) - { - float swimSpeed = walkSpeed; - if(running) - swimSpeed = runSpeed; - swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); - swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics)* - gmst.fSwimRunAthleticsMult->mValue.getFloat(); - moveSpeed = swimSpeed; - } + moveSpeed = getSwimSpeed(ptr); else if (running && !sneaking) - moveSpeed = runSpeed; + moveSpeed = getRunSpeed(ptr); else - moveSpeed = walkSpeed; + moveSpeed = getWalkSpeed(ptr); if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) moveSpeed *= 0.75f; @@ -1448,4 +1430,56 @@ namespace MWClass { MWMechanics::setBaseAISetting(id, setting, value); } + + float Npc::getWalkSpeed(const MWWorld::Ptr& ptr) const + { + const GMST& gmst = getGmst(); + const NpcCustomData* npcdata = static_cast(ptr.getRefData().getCustomData()); + const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); + const bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr); + + float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + + 0.01f * npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + * (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat()); + walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance; + walkSpeed = std::max(0.0f, walkSpeed); + if(sneaking) + walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat(); + + return walkSpeed; + } + + float Npc::getRunSpeed(const MWWorld::Ptr& ptr) const + { + const GMST& gmst = getGmst(); + return getWalkSpeed(ptr) + * (0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fAthleticsRunBonus->mValue.getFloat() + + gmst.fBaseRunMultiplier->mValue.getFloat()); + } + + float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const + { + const GMST& gmst = getGmst(); + const MWBase::World* world = MWBase::Environment::get().getWorld(); + const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + const NpcCustomData* npcdata = static_cast(ptr.getRefData().getCustomData()); + const MWMechanics::MagicEffects& mageffects = npcdata->mNpcStats.getMagicEffects(); + const bool swimming = world->isSwimming(ptr); + const bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr); + const bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run) + && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr)); + + float swimSpeed; + + if (running) + swimSpeed = getRunSpeed(ptr); + else + swimSpeed = getWalkSpeed(ptr); + + swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); + swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + + 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat(); + + return swimSpeed; + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index ae4f32d13..d92293acb 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -166,6 +166,12 @@ namespace MWClass virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const; virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; + + float getWalkSpeed(const MWWorld::Ptr& ptr) const final; + + float getRunSpeed(const MWWorld::Ptr& ptr) const final; + + float getSwimSpeed(const MWWorld::Ptr& ptr) const final; }; } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index d7ee59ee2..b59532f2a 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -521,4 +521,19 @@ namespace MWWorld { throw std::runtime_error ("class does not have creature stats"); } + + float Class::getWalkSpeed(const Ptr& /*ptr*/) const + { + return 0; + } + + float Class::getRunSpeed(const Ptr& /*ptr*/) const + { + return 0; + } + + float Class::getSwimSpeed(const Ptr& /*ptr*/) const + { + return 0; + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index fd679c43f..f92dc0373 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -362,6 +362,12 @@ namespace MWWorld virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; + + virtual float getWalkSpeed(const Ptr& ptr) const; + + virtual float getRunSpeed(const Ptr& ptr) const; + + virtual float getSwimSpeed(const Ptr& ptr) const; }; } From b095ca6c867285e3cccb9c8675e21b5b6503d0ea Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 13 Jun 2020 01:56:04 +0200 Subject: [PATCH 216/227] Use actor speed to define area cost for pathfinding --- apps/openmw/mwmechanics/aipackage.cpp | 26 ++++++++++++++- apps/openmw/mwmechanics/aipackage.hpp | 3 ++ apps/openmw/mwmechanics/aiwander.cpp | 9 +++-- apps/openmw/mwmechanics/pathfinding.cpp | 19 ++++++----- apps/openmw/mwmechanics/pathfinding.hpp | 10 +++--- .../detournavigator/navigator.cpp | 33 ++++++++++--------- components/detournavigator/areatype.hpp | 8 +++++ components/detournavigator/findsmoothpath.hpp | 7 +++- components/detournavigator/navigator.hpp | 5 +-- 9 files changed, 84 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 0c06697dc..e322f57ce 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -114,7 +114,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& { const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor); mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()), - pathfindingHalfExtents, getNavigatorFlags(actor)); + pathfindingHalfExtents, getNavigatorFlags(actor), getAreaCosts(actor)); mRotateOnTheRunChecks = 3; // give priority to go directly on target if there is minimal opportunity @@ -402,3 +402,27 @@ DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld:: return result; } + +DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::Ptr& actor) const +{ + DetourNavigator::AreaCosts costs; + const DetourNavigator::Flags flags = getNavigatorFlags(actor); + const MWWorld::Class& actorClass = actor.getClass(); + + if (flags & DetourNavigator::Flag_swim) + costs.mWater = costs.mWater / actorClass.getSwimSpeed(actor); + + if (flags & DetourNavigator::Flag_walk) + { + float walkCost; + if (getTypeId() == TypeIdWander) + walkCost = 1.0 / actorClass.getWalkSpeed(actor); + else + walkCost = 1.0 / actorClass.getRunSpeed(actor); + costs.mDoor = costs.mDoor * walkCost; + costs.mPathgrid = costs.mPathgrid * walkCost; + costs.mGround = costs.mGround * walkCost; + } + + return costs; +} diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index c32fb93aa..477976616 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -4,6 +4,7 @@ #include #include +#include #include "pathfinding.hpp" #include "obstacle.hpp" @@ -167,6 +168,8 @@ namespace MWMechanics DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; + DetourNavigator::AreaCosts getAreaCosts(const MWWorld::Ptr& actor) const; + const TypeId mTypeId; const Options mOptions; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 015859c4b..11c50dc09 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -202,7 +202,7 @@ namespace MWMechanics { const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(), - getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor)); + getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor), getAreaCosts(actor)); } if (mPathFinder.isPathConstructed()) @@ -337,6 +337,7 @@ namespace MWMechanics const auto halfExtents = world->getPathfindingHalfExtents(actor); const auto navigator = world->getNavigator(); const auto navigatorFlags = getNavigatorFlags(actor); + const auto areaCosts = getAreaCosts(actor); do { // Determine a random location within radius of original position @@ -365,7 +366,8 @@ namespace MWMechanics if (isWaterCreature || isFlyingCreature) mPathFinder.buildStraightPath(mDestination); else - mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags); + mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags, + areaCosts); if (mPathFinder.isPathConstructed()) { @@ -496,7 +498,8 @@ namespace MWMechanics if (mUsePathgrid) { const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); - mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor)); + mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor), + getAreaCosts(actor)); } if (mObstacleCheck.isEvading()) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index d00f2615e..4d4b5be51 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -309,12 +309,13 @@ namespace MWMechanics } void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, - const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags) + const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, + const DetourNavigator::AreaCosts& areaCosts) { mPath.clear(); // If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path - if (!buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath))) + if (!buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, areaCosts, std::back_inserter(mPath))) mPath.push_back(endPoint); mConstructed = true; @@ -322,7 +323,7 @@ namespace MWMechanics void PathFinder::buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, - const DetourNavigator::Flags flags) + const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts) { mPath.clear(); mCell = cell; @@ -330,11 +331,11 @@ namespace MWMechanics bool hasNavMesh = false; if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor)) - hasNavMesh = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); + hasNavMesh = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, areaCosts, std::back_inserter(mPath)); if (hasNavMesh && mPath.empty()) buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, - flags | DetourNavigator::Flag_usePathgrid, std::back_inserter(mPath)); + flags | DetourNavigator::Flag_usePathgrid, areaCosts, std::back_inserter(mPath)); if (mPath.empty()) buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath)); @@ -347,12 +348,12 @@ namespace MWMechanics bool PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, - std::back_insert_iterator> out) + const DetourNavigator::AreaCosts& areaCosts, std::back_insert_iterator> out) { const auto world = MWBase::Environment::get().getWorld(); const auto stepSize = getPathStepSize(actor); const auto navigator = world->getNavigator(); - const auto status = navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out); + const auto status = navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, areaCosts, out); if (status == DetourNavigator::Status::NavMeshNotFound) return false; @@ -369,7 +370,7 @@ namespace MWMechanics } void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, - const DetourNavigator::Flags flags) + const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts) { if (mPath.empty()) return; @@ -383,7 +384,7 @@ namespace MWMechanics const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); std::deque prePath; auto prePathInserter = std::back_inserter(prePath); - const auto status = navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, + const auto status = navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, areaCosts, prePathInserter); if (status == DetourNavigator::Status::NavMeshNotFound) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index cb33471ca..5af822fee 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -90,14 +91,15 @@ namespace MWMechanics const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph); void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, - const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags); + const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, + const DetourNavigator::AreaCosts& areaCosts); void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, - const DetourNavigator::Flags flags); + const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts); void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, - const DetourNavigator::Flags flags); + const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts); /// Remove front point if exist and within tolerance void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance); @@ -203,7 +205,7 @@ namespace MWMechanics bool buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, - std::back_insert_iterator> out); + const DetourNavigator::AreaCosts& areaCosts, std::back_insert_iterator> out); }; } diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 276877508..5a92d5d28 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -37,6 +37,7 @@ namespace std::deque mPath; std::back_insert_iterator> mOut; float mStepSize; + AreaCosts mAreaCosts; DetourNavigatorNavigatorTest() : mPlayerPosition(0, 0, 0) @@ -80,7 +81,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty) { - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::NavMeshNotFound); EXPECT_EQ(mPath, std::deque()); } @@ -88,7 +89,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception) { mNavigator->addAgent(mAgentHalfExtents); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::StartPolygonNotFound); } @@ -97,7 +98,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents); mNavigator->removeAgent(mAgentHalfExtents); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::StartPolygonNotFound); } @@ -118,7 +119,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.85963428020477294921875), @@ -168,7 +169,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.85963428020477294921875), @@ -202,7 +203,7 @@ namespace mPath.clear(); mOut = std::back_inserter(mPath); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.87826788425445556640625), @@ -253,7 +254,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.87826788425445556640625), @@ -289,7 +290,7 @@ namespace mPath.clear(); mOut = std::back_inserter(mPath); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.85963428020477294921875), @@ -346,7 +347,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.96328866481781005859375), @@ -402,7 +403,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.9393787384033203125), @@ -455,7 +456,7 @@ namespace mEnd.x() = 0; mEnd.z() = 300; - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim, mAreaCosts, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(0, 215, 185.33331298828125), @@ -501,7 +502,7 @@ namespace mStart.x() = 0; mEnd.x() = 0; - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mOut), + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ @@ -548,7 +549,7 @@ namespace mStart.x() = 0; mEnd.x() = 0; - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mOut), + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ @@ -595,7 +596,7 @@ namespace mStart.x() = 0; mEnd.x() = 0; - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(0, 215, -94.75363922119140625), @@ -644,7 +645,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.85963428020477294921875), @@ -739,7 +740,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); EXPECT_THAT(mPath, ElementsAre( Vec3fEq(-215, 215, 1.8782780170440673828125), diff --git a/components/detournavigator/areatype.hpp b/components/detournavigator/areatype.hpp index 962a67847..9d99421af 100644 --- a/components/detournavigator/areatype.hpp +++ b/components/detournavigator/areatype.hpp @@ -13,6 +13,14 @@ namespace DetourNavigator AreaType_pathgrid, AreaType_ground = RC_WALKABLE_AREA, }; + + struct AreaCosts + { + float mWater = 1.0f; + float mDoor = 2.0f; + float mPathgrid = 1.0f; + float mGround = 1.0f; + }; } #endif diff --git a/components/detournavigator/findsmoothpath.hpp b/components/detournavigator/findsmoothpath.hpp index 0f8f2c09a..f1de71207 100644 --- a/components/detournavigator/findsmoothpath.hpp +++ b/components/detournavigator/findsmoothpath.hpp @@ -8,6 +8,7 @@ #include "settingsutils.hpp" #include "debug.hpp" #include "status.hpp" +#include "areatype.hpp" #include #include @@ -269,7 +270,7 @@ namespace DetourNavigator template Status findSmoothPath(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, const float stepSize, - const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, + const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, const AreaCosts& areaCosts, const Settings& settings, OutputIterator& out) { dtNavMeshQuery navMeshQuery; @@ -278,6 +279,10 @@ namespace DetourNavigator dtQueryFilter queryFilter; queryFilter.setIncludeFlags(includeFlags); + queryFilter.setAreaCost(AreaType_water, areaCosts.mWater); + queryFilter.setAreaCost(AreaType_door, areaCosts.mDoor); + queryFilter.setAreaCost(AreaType_pathgrid, areaCosts.mPathgrid); + queryFilter.setAreaCost(AreaType_ground, areaCosts.mGround); dtPolyRef startRef = 0; osg::Vec3f startPolygonPosition; diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index cfdf92232..391c63022 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -172,7 +172,8 @@ namespace DetourNavigator */ template Status findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start, - const osg::Vec3f& end, const Flags includeFlags, OutputIterator& out) const + const osg::Vec3f& end, const Flags includeFlags, const DetourNavigator::AreaCosts& areaCosts, + OutputIterator& out) const { static_assert( std::is_same< @@ -187,7 +188,7 @@ namespace DetourNavigator const auto settings = getSettings(); return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start), - toNavMeshCoordinates(settings, end), includeFlags, settings, out); + toNavMeshCoordinates(settings, end), includeFlags, areaCosts, settings, out); } /** From a6493ce3292eee305701f7cc987fca7223c9897b Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 13 Jun 2020 01:51:27 +0100 Subject: [PATCH 217/227] Don't exit prebuild script on nonzero exit code when we already check it We have `set -e` enabled, so normally exit the script if a command fails. We also had explicit exit code checks in a few places with user-friendly error messages. These were never printed as the script exited before we could check the exit code due to a bad exit code. --- CI/before_script.msvc.sh | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 90654559b..59bf4cb37 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -213,8 +213,8 @@ run_cmd() { shift if [ -z $VERBOSE ]; then - eval $CMD $@ > output.log 2>&1 - RET=$? + RET=0 + eval $CMD $@ > output.log 2>&1 || RET=$? if [ $RET -ne 0 ]; then if [ -z $APPVEYOR ]; then @@ -230,8 +230,9 @@ run_cmd() { return $RET else - eval $CMD $@ - return $? + RET=0 + eval $CMD $@ || RET=$? + return RET fi } @@ -256,15 +257,16 @@ download() { printf " Downloading $FILE... " if [ -z $VERBOSE ]; then - curl --silent --retry 10 -kLy 5 -o $FILE $URL - RET=$? + RET=0 + curl --silent --retry 10 -kLy 5 -o $FILE $URL || RET=$? else - curl --retry 10 -kLy 5 -o $FILE $URL - RET=$? + RET=0 + curl --retry 10 -kLy 5 -o $FILE $URL || RET=$? fi if [ $RET -ne 0 ]; then echo "Failed!" + wrappedExit $RET else echo "Done." fi @@ -997,8 +999,8 @@ if [ -z $VERBOSE ]; then else echo "- cmake .. $CMAKE_OPTS" fi -run_cmd cmake .. $CMAKE_OPTS -RET=$? +RET=0 +run_cmd cmake .. $CMAKE_OPTS || RET=$? if [ -z $VERBOSE ]; then if [ $RET -eq 0 ]; then echo Done. @@ -1006,6 +1008,9 @@ if [ -z $VERBOSE ]; then echo Failed. fi fi +if [ $RET -ne 0 ]; then + wrappedExit $RET +fi if [ -n $ACTIVATE_MSVC ]; then echo From 6f94848dec1a313f00cedf534738ee13b0db8214 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 14 Jun 2020 23:02:03 +0300 Subject: [PATCH 218/227] Remove 63 UV set limit (now 65535) --- components/nif/data.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index d19c8321e..f8b6da036 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -47,10 +47,9 @@ void ShapeData::read(NIFStream *nif) if(nif->getInt()) nif->getVector4s(colors, verts); - // Only the first 6 bits are used as a count. I think the rest are - // flags of some sort. + // In Morrowind this field only corresponds to the number of UV sets. + // NifTools research is inaccurate. int uvs = nif->getUShort(); - uvs &= 0x3f; if(nif->getInt()) { From 4b831f99da3c65c76213c593aad7e773f90951a6 Mon Sep 17 00:00:00 2001 From: apommel Date: Mon, 15 Jun 2020 10:13:22 +0900 Subject: [PATCH 219/227] Allow vswhere to detect build tools installations --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 90654559b..0f541f53f 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -961,7 +961,7 @@ if ! [ -z $ACTIVATE_MSVC ]; then echo -n "- Activating MSVC in the current shell... " command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; } - MSVC_INSTALLATION_PATH=$(vswhere -legacy -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) + MSVC_INSTALLATION_PATH=$(vswhere -legacy -products '*' -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) if [ $MSVC_REAL_VER -ge 15 ]; then echo "@\"${MSVC_INSTALLATION_PATH}\Common7\Tools\VsDevCmd.bat\" -no_logo -arch=$([ $BITS -eq 64 ] && echo "amd64" || echo "x86") -host_arch=$([ $(uname -m) == 'x86_64' ] && echo "amd64" || echo "x86")" > ActivateMSVC.bat else From f30cb9f8bc0c81cb5a5b96d6e87303b00e65f32d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 15 Jun 2020 12:43:51 +0400 Subject: [PATCH 220/227] Calculate viewNormal only when needed --- files/shaders/objects_fragment.glsl | 9 ++++++++- files/shaders/objects_vertex.glsl | 4 ++++ files/shaders/terrain_fragment.glsl | 11 +++++++++-- files/shaders/terrain_vertex.glsl | 6 +++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 74a0cc80f..ece53f047 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -83,7 +83,9 @@ void main() mat3 tbnTranspose = mat3(normalizedTangent, binormal, normalizedNormal); vec3 viewNormal = gl_NormalMatrix * normalize(tbnTranspose * (normalTex.xyz * 2.0 - 1.0)); -#else +#endif + +#if (!@normalMap && (@parallax || @forcePPL)) vec3 viewNormal = gl_NormalMatrix * normalize(passNormal); #endif @@ -184,7 +186,12 @@ void main() #endif if (matSpec != vec3(0.0)) + { +#if (!normalMap && !@parallax && !forcePPL) + vec3 viewNormal = gl_NormalMatrix * normalize(passNormal); +#endif gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; + } #if @radialFog float depth; // For the less detailed mesh of simple water we need to recalculate depth on per-pixel basis diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index bd97b2526..40c448de9 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -63,7 +63,9 @@ void main(void) euclideanDepth = length(viewPos.xyz); linearDepth = gl_Position.z; +#if (@envMap || !PER_PIXEL_LIGHTING || @shadows_enabled) vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); +#endif #if @envMap vec3 viewVec = normalize(viewPos.xyz); @@ -112,5 +114,7 @@ void main(void) passViewPos = viewPos.xyz; passNormal = gl_Normal.xyz; +#if (@shadows_enabled) setupShadowCoords(viewPos, viewNormal); +#endif } diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index ffc19fac0..21e50b4e9 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -43,8 +43,10 @@ void main() mat3 tbnTranspose = mat3(tangent, binormal, normalizedNormal); vec3 viewNormal = normalize(gl_NormalMatrix * (tbnTranspose * (normalTex.xyz * 2.0 - 1.0))); -#else - vec3 viewNormal = normalize(gl_NormalMatrix * passNormal); +#endif + +#if (!@normalMap && (@parallax || @forcePPL)) + vec3 viewNormal = gl_NormalMatrix * normalize(passNormal); #endif #if @parallax @@ -93,7 +95,12 @@ void main() #endif if (matSpec != vec3(0.0)) + { +#if (!normalMap && !@parallax && !forcePPL) + vec3 viewNormal = gl_NormalMatrix * normalize(passNormal); +#endif gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing; + } #if @radialFog float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); diff --git a/files/shaders/terrain_vertex.glsl b/files/shaders/terrain_vertex.glsl index dc4ea802f..bf337cf54 100644 --- a/files/shaders/terrain_vertex.glsl +++ b/files/shaders/terrain_vertex.glsl @@ -26,8 +26,10 @@ void main(void) gl_ClipVertex = viewPos; euclideanDepth = length(viewPos.xyz); linearDepth = gl_Position.z; - + +#if (!PER_PIXEL_LIGHTING || @shadows_enabled) vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); +#endif #if !PER_PIXEL_LIGHTING lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting); @@ -38,5 +40,7 @@ void main(void) uv = gl_MultiTexCoord0.xy; +#if (@shadows_enabled) setupShadowCoords(viewPos, viewNormal); +#endif } From 761558f6129349680b78fb7f3b45c112aae28a93 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 19 May 2020 01:32:08 +0100 Subject: [PATCH 221/227] Remove test data A dummy command was used to check the script would fail if a command was missing. Not being a real command, it always made the script fail as a command was missing. --- CI/ActivateMSVC.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/ActivateMSVC.ps1 b/CI/ActivateMSVC.ps1 index ca78ef588..ae73acfe1 100644 --- a/CI/ActivateMSVC.ps1 +++ b/CI/ActivateMSVC.ps1 @@ -4,8 +4,8 @@ } $MissingTools = $false -$tools = "cl", "link", "rc", "mt", "awooga" -$descriptions = "MSVC Compiler", "MSVC Linker", "MS Windows Resource Compiler", "MS Windows Manifest Tool", "A made up command" +$tools = "cl", "link", "rc", "mt" +$descriptions = "MSVC Compiler", "MSVC Linker", "MS Windows Resource Compiler", "MS Windows Manifest Tool" for ($i = 0; $i -lt $tools.Length; $i++) { $present = $true try { From 079be5d485083d3646ec8e53ad7cbeb09cbc7b2a Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 19 May 2020 17:50:18 +0100 Subject: [PATCH 222/227] Remove annoying warning --- CI/ActivateMSVC.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CI/ActivateMSVC.ps1 b/CI/ActivateMSVC.ps1 index ae73acfe1..a04a23a93 100644 --- a/CI/ActivateMSVC.ps1 +++ b/CI/ActivateMSVC.ps1 @@ -1,6 +1,8 @@ & "${env:COMSPEC}" /c ActivateMSVC.bat "&&" set | ForEach-Object { - $name, $value = $_ -split '=', 2 - Set-Content env:\"$name" $value + if ($_.Contains("=")) { + $name, $value = $_ -split '=', 2 + Set-Content env:\"$name" $value + } } $MissingTools = $false From 00197e1cd9ee645f6c6fde201231258281dadf36 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 15 Jun 2020 23:53:22 +0200 Subject: [PATCH 223/227] Optimize recast mesh size by vertex deduplication --- .../detournavigator/operators.hpp | 34 +++++++-- .../detournavigator/recastmeshbuilder.cpp | 76 +++++++++++-------- .../detournavigator/recastmeshbuilder.cpp | 42 +++++++++- .../detournavigator/recastmeshbuilder.hpp | 2 +- 4 files changed, 114 insertions(+), 40 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/operators.hpp b/apps/openmw_test_suite/detournavigator/operators.hpp index e34d6278a..92740c65f 100644 --- a/apps/openmw_test_suite/detournavigator/operators.hpp +++ b/apps/openmw_test_suite/detournavigator/operators.hpp @@ -23,17 +23,23 @@ namespace DetourNavigator namespace { template - struct Wrapper { + struct Wrapper + { const T& mValue; }; template - inline testing::Message& writeRange(testing::Message& message, const Range& range) + inline testing::Message& writeRange(testing::Message& message, const Range& range, std::size_t newLine) { - message << "{\n"; + message << "{"; + std::size_t i = 0; for (const auto& v : range) - message << Wrapper::type> {v} << ",\n"; - return message << "}"; + { + if (i++ % newLine == 0) + message << "\n"; + message << Wrapper::type> {v} << ", "; + } + return message << "\n}"; } } @@ -60,22 +66,34 @@ namespace testing return (*this) << std::setprecision(std::numeric_limits::max_exponent10) << value.mValue; } + template <> + inline testing::Message& Message::operator <<(const Wrapper& value) + { + return (*this) << value.mValue; + } + template <> inline testing::Message& Message::operator <<(const std::deque& value) { - return writeRange(*this, value); + return writeRange(*this, value, 1); } template <> inline testing::Message& Message::operator <<(const std::vector& value) { - return writeRange(*this, value); + return writeRange(*this, value, 1); } template <> inline testing::Message& Message::operator <<(const std::vector& value) { - return writeRange(*this, value); + return writeRange(*this, value, 3); + } + + template <> + inline testing::Message& Message::operator <<(const std::vector& value) + { + return writeRange(*this, value, 3); } } diff --git a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp index c86dee6e5..bcbf448ac 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp @@ -104,11 +104,9 @@ namespace -0.5, 0, -0.5, -0.5, 0, 0.5, 0.5, 0, -0.5, - 0.5, 0, -0.5, - -0.5, 0, 0.5, 0.5, 0, 0.5, })); - EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2, 3, 4, 5})); + EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2, 2, 1, 3})); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector({AreaType_ground, AreaType_ground})); } @@ -127,7 +125,7 @@ namespace -1, -2, 1, 1, -2, -1, -1, -2, -1, - })); + })) << recastMesh->getVertices(); EXPECT_EQ(recastMesh->getIndices(), std::vector({ 0, 2, 3, 3, 1, 0, @@ -141,7 +139,7 @@ namespace 2, 6, 7, 7, 6, 4, 4, 5, 7, - })); + })) << recastMesh->getIndices(); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector(12, AreaType_ground)); } @@ -166,37 +164,35 @@ namespace ); const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ - 1, 0, -1, - -1, 0, 1, + -1, -2, -1, + -1, -2, 1, -1, 0, -1, - 1, 2, 1, - -1, 2, 1, - 1, 2, -1, + -1, 0, 1, -1, 2, -1, - 1, -2, 1, - -1, -2, 1, + -1, 2, 1, 1, -2, -1, - -1, -2, -1, + 1, -2, 1, 1, 0, -1, - -1, 0, 1, 1, 0, 1, - })); + 1, 2, -1, + 1, 2, 1, + })) << recastMesh->getVertices(); EXPECT_EQ(recastMesh->getIndices(), std::vector({ - 0, 1, 2, - 3, 5, 6, - 6, 4, 3, - 3, 7, 9, - 9, 5, 3, - 3, 4, 8, - 8, 7, 3, - 10, 8, 4, - 4, 6, 10, - 10, 6, 5, - 5, 9, 10, - 10, 9, 7, - 7, 8, 10, - 11, 12, 13, - })); + 8, 3, 2, + 11, 10, 4, + 4, 5, 11, + 11, 7, 6, + 6, 10, 11, + 11, 5, 1, + 1, 7, 11, + 0, 1, 5, + 5, 4, 0, + 0, 4, 10, + 10, 6, 0, + 0, 6, 7, + 7, 1, 0, + 8, 3, 9, + })) << recastMesh->getIndices(); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector(14, AreaType_ground)); } @@ -413,4 +409,24 @@ namespace RecastMesh::Water {1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))} })); } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, add_bhv_triangle_mesh_shape_with_duplicated_vertices) + { + btTriangleMesh mesh; + mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); + mesh.addTriangle(btVector3(1, 1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); + btBvhTriangleMeshShape shape(&mesh, true); + + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), btTransform::getIdentity(), AreaType_ground); + const auto recastMesh = builder.create(mGeneration, mRevision); + EXPECT_EQ(recastMesh->getVertices(), std::vector({ + -1, 0, -1, + -1, 0, 1, + 1, 0, -1, + 1, 0, 1, + })) << recastMesh->getVertices(); + EXPECT_EQ(recastMesh->getIndices(), std::vector({2, 1, 0, 2, 1, 3})); + EXPECT_EQ(recastMesh->getAreaTypes(), std::vector({AreaType_ground, AreaType_ground})); + } } diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index f61368357..59f60394d 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -17,11 +17,50 @@ #include #include +#include namespace DetourNavigator { using BulletHelpers::makeProcessTriangleCallback; + namespace + { + void optimizeRecastMesh(std::vector& indices, std::vector& vertices) + { + std::vector> uniqueVertices; + uniqueVertices.reserve(vertices.size() / 3); + + for (std::size_t i = 0, n = vertices.size() / 3; i < n; ++i) + uniqueVertices.emplace_back(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]); + + std::sort(uniqueVertices.begin(), uniqueVertices.end()); + const auto end = std::unique(uniqueVertices.begin(), uniqueVertices.end()); + uniqueVertices.erase(end, uniqueVertices.end()); + + if (uniqueVertices.size() == vertices.size() / 3) + return; + + for (std::size_t i = 0, n = indices.size(); i < n; ++i) + { + const auto index = indices[i]; + const auto vertex = std::make_tuple(vertices[index * 3], vertices[index * 3 + 1], vertices[index * 3 + 2]); + const auto it = std::lower_bound(uniqueVertices.begin(), uniqueVertices.end(), vertex); + assert(it != uniqueVertices.end()); + assert(*it == vertex); + indices[i] = std::distance(uniqueVertices.begin(), it); + } + + vertices.resize(uniqueVertices.size() * 3); + + for (std::size_t i = 0, n = uniqueVertices.size(); i < n; ++i) + { + vertices[i * 3] = std::get<0>(uniqueVertices[i]); + vertices[i * 3 + 1] = std::get<1>(uniqueVertices[i]); + vertices[i * 3 + 2] = std::get<2>(uniqueVertices[i]); + } + } + } + RecastMeshBuilder::RecastMeshBuilder(const Settings& settings, const TileBounds& bounds) : mSettings(settings) , mBounds(bounds) @@ -112,8 +151,9 @@ namespace DetourNavigator mWater.push_back(RecastMesh::Water {cellSize, transform}); } - std::shared_ptr RecastMeshBuilder::create(std::size_t generation, std::size_t revision) const + std::shared_ptr RecastMeshBuilder::create(std::size_t generation, std::size_t revision) { + optimizeRecastMesh(mIndices, mVertices); return std::make_shared(generation, revision, mIndices, mVertices, mAreaTypes, mWater, mSettings.get().mTrianglesPerChunk); } diff --git a/components/detournavigator/recastmeshbuilder.hpp b/components/detournavigator/recastmeshbuilder.hpp index d28558d0f..fc2bbbc02 100644 --- a/components/detournavigator/recastmeshbuilder.hpp +++ b/components/detournavigator/recastmeshbuilder.hpp @@ -34,7 +34,7 @@ namespace DetourNavigator void addWater(const int mCellSize, const btTransform& transform); - std::shared_ptr create(std::size_t generation, std::size_t revision) const; + std::shared_ptr create(std::size_t generation, std::size_t revision); void reset(); From 5bc44cf2ee813a2fa8c11ec9f1a0b01082342d5d Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Tue, 16 Jun 2020 14:37:06 +0300 Subject: [PATCH 224/227] Use sequenced texture units with .dae/collada --- components/resource/scenemanager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index d06a07273..8264e3b1e 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -360,6 +360,7 @@ namespace Resource // Note, for some formats (.obj/.mtl) that reference other (non-image) files a findFileCallback would be necessary. // but findFileCallback does not support virtual files, so we can't implement it. options->setReadFileCallback(new ImageReadCallback(imageManager)); + if (ext == "dae") options->setOptionString("daeUseSequencedTextureUnits"); osgDB::ReaderWriter::ReadResult result = reader->readNode(*file, options); if (!result.success()) From 805d826d5b27132cd3128da322479832dab59593 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 16 Jun 2020 15:31:50 +0400 Subject: [PATCH 225/227] Fix Clang warnings about invalid overrides --- components/terrain/chunkmanager.hpp | 2 +- components/terrain/quadtreeworld.hpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 11e5769de..02a782c62 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -35,7 +35,7 @@ namespace Terrain public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) override; void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; } void setCompositeMapLevel(float level) { mCompositeMapLevel = level; } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 7b395bff6..47cf46138 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -26,22 +26,22 @@ namespace Terrain void accept(osg::NodeVisitor& nv); - virtual void enable(bool enabled); + void enable(bool enabled) override; - virtual void setViewDistance(float distance) { mViewDistance = distance; } + void setViewDistance(float distance) override { mViewDistance = distance; } - void cacheCell(View *view, int x, int y) {} + void cacheCell(View *view, int x, int y) override {} /// @note Not thread safe. - virtual void loadCell(int x, int y); + void loadCell(int x, int y) override; /// @note Not thread safe. - virtual void unloadCell(int x, int y); + void unloadCell(int x, int y) override; - View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); - bool storeView(const View* view, double referenceTime); + View* createView() override; + void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange) override; + bool storeView(const View* view, double referenceTime) override; void rebuildViews() override; - void reportStats(unsigned int frameNumber, osg::Stats* stats); + void reportStats(unsigned int frameNumber, osg::Stats* stats) override; class ChunkManager { From 7ef3a9d8ac57d02fcaaf991e9314222c3f6a1378 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 16 Jun 2020 13:51:25 +0000 Subject: [PATCH 226/227] Remove schoolboy error --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 701218dd2..26676efc8 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -232,7 +232,7 @@ run_cmd() { else RET=0 eval $CMD $@ || RET=$? - return RET + return $RET fi } From 6f8eefcb1635b5ad1b95351e3bc4724ec657e2b9 Mon Sep 17 00:00:00 2001 From: psi29a Date: Tue, 16 Jun 2020 17:53:09 +0000 Subject: [PATCH 227/227] Switching object paging active grid default option to false for now until the light limit problem is resolved. --- files/settings-default.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 686d65adc..c8a147b9e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -110,7 +110,7 @@ max composite geometry size = 4.0 object paging = true # Use object paging for active cells grid -object paging active grid = true +object paging active grid = false # Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory. object paging merge factor = 250