From e568ad30ea27087bc8b87aa1c142eb6bceb5cd36 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 19 Sep 2019 11:35:15 +0400 Subject: [PATCH 001/226] Copy transformations data when we clone node (bug #5163) --- CHANGELOG.md | 1 + components/sceneutil/clone.cpp | 12 +++++++++++- components/sceneutil/clone.hpp | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d9104199..69c1f620c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -144,6 +144,7 @@ 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 #5166: Scripts still should be executed after player's death Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp index d1cca5b31..04fd5d78d 100644 --- a/components/sceneutil/clone.cpp +++ b/components/sceneutil/clone.cpp @@ -6,8 +6,9 @@ #include #include -#include +#include +#include #include namespace SceneUtil @@ -30,6 +31,15 @@ namespace SceneUtil 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); + + return osg::CopyOp::operator()(node); + } + osg::Node* CopyOp::operator ()(const osg::Node* node) const { if (const osgParticle::ParticleProcessor* processor = dynamic_cast(node)) diff --git a/components/sceneutil/clone.hpp b/components/sceneutil/clone.hpp index 662dad543..8a18eeb20 100644 --- a/components/sceneutil/clone.hpp +++ b/components/sceneutil/clone.hpp @@ -32,6 +32,7 @@ namespace SceneUtil 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: // maps new ParticleProcessor to their old ParticleSystem pointer From 480302d634fa5bf418956be596e2738ea59a9a5f Mon Sep 17 00:00:00 2001 From: bzzt Date: Sat, 10 Aug 2019 13:37:00 +0000 Subject: [PATCH 002/226] terrainclusterculling --- components/terrain/chunkmanager.cpp | 3 +++ components/terrain/terraindrawable.cpp | 30 +++++++++++++++++++++++++- components/terrain/terraindrawable.hpp | 9 ++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 881397936..3c76945d3 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -198,6 +199,8 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve for (unsigned int i=0; isetTexCoordArray(i, mBufferCache.getUVBuffer(numVerts)); + geometry->createClusterCullingCallback(); + if (useCompositeMap) { osg::ref_ptr compositeMap = new CompositeMap; diff --git a/components/terrain/terraindrawable.cpp b/components/terrain/terraindrawable.cpp index 151977cd4..c37074dac 100644 --- a/components/terrain/terraindrawable.cpp +++ b/components/terrain/terraindrawable.cpp @@ -1,5 +1,6 @@ #include "terraindrawable.hpp" +#include #include #include @@ -36,6 +37,23 @@ inline float distance(const osg::Vec3& coord,const osg::Matrix& matrix) return -((float)coord[0]*(float)matrix(0,2)+(float)coord[1]*(float)matrix(1,2)+(float)coord[2]*(float)matrix(2,2)+matrix(3,2)); } +//canot use ClusterCullingCallback::cull: viewpoint != eyepoint +// !osgfixpotential! +bool clusterCull(osg::ClusterCullingCallback* cb, const osg::Vec3f& eyePoint, bool shadowcam) +{ + float _deviation = cb->getDeviation(); + const osg::Vec3& _controlPoint = cb->getControlPoint(); + osg::Vec3 _normal = cb->getNormal(); + if (shadowcam) _normal = _normal * -1; //inverting for shadowcam frontfaceculing + float _radius = cb->getRadius(); + if (_deviation<=-1.0f) return false; + osg::Vec3 eye_cp = eyePoint - _controlPoint; + float radius = eye_cp.length(); + if (radius<_radius) return false; + float deviation = (eye_cp * _normal)/radius; + return deviation < _deviation; +} + void TerrainDrawable::cull(osgUtil::CullVisitor *cv) { const osg::BoundingBox& bb = getBoundingBox(); @@ -43,6 +61,11 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) if (_cullingActive && cv->isCulled(getBoundingBox())) return; + bool shadowcam = cv->getCurrentCamera()->getName() == "ShadowCamera"; + + if (cv->getCullingMode() & osg::CullStack::CLUSTER_CULLING && clusterCull(mClusterCullingCallback, cv->getEyePoint(), shadowcam)) + return; + osg::RefMatrix& matrix = *cv->getModelViewMatrix(); if (cv->getComputeNearFarMode() && bb.valid()) @@ -55,7 +78,7 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) if (osg::isNaN(depth)) return; - if (cv->getCurrentCamera()->getName() == "ShadowCamera") + if (shadowcam) { cv->addDrawableAndDepth(this, &matrix, depth); return; @@ -80,6 +103,11 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) cv->popStateSet(); } +void TerrainDrawable::createClusterCullingCallback() +{ + mClusterCullingCallback = new osg::ClusterCullingCallback(this); +} + void TerrainDrawable::setPasses(const TerrainDrawable::PassVector &passes) { mPasses = passes; diff --git a/components/terrain/terraindrawable.hpp b/components/terrain/terraindrawable.hpp index b77b6b784..516b1abdb 100644 --- a/components/terrain/terraindrawable.hpp +++ b/components/terrain/terraindrawable.hpp @@ -3,6 +3,11 @@ #include +namespace osg +{ + class ClusterCullingCallback; +} + namespace osgUtil { class CullVisitor; @@ -43,6 +48,8 @@ namespace Terrain void setLightListCallback(SceneUtil::LightListCallback* lightListCallback); + void createClusterCullingCallback(); + virtual void compileGLObjects(osg::RenderInfo& renderInfo) const; void setCompositeMap(CompositeMap* map) { mCompositeMap = map; } @@ -51,6 +58,8 @@ namespace Terrain private: PassVector mPasses; + osg::ref_ptr mClusterCullingCallback; + osg::ref_ptr mLightListCallback; osg::ref_ptr mCompositeMap; osg::ref_ptr mCompositeMapRenderer; From 94df2114c13024e592cf94907eb79a1f00900226 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 7 Mar 2020 10:31:00 +0400 Subject: [PATCH 003/226] 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 8dc081a60dec3a0b7df2126349e3f57ee2933e89 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 25 Mar 2020 19:52:22 +0100 Subject: [PATCH 004/226] ignore unterminated empty strings --- components/compiler/scanner.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 6d66b0493..24c13a1d2 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -282,6 +282,8 @@ namespace Compiler if (!scanName (name)) return false; + else if(name.empty()) + return true; TokenLoc loc (mLoc); mLoc.mLiteral.clear(); @@ -366,6 +368,13 @@ namespace Compiler { if (mIgnoreNewline) mErrorHandler.warning ("string contains newline character, make sure that it is intended", mLoc); + else if (name.size() == 1 || name.size() == 2 && name[1] == '\r') + { + name.clear(); + mLoc.mLiteral.clear(); + mErrorHandler.warning ("unterminated empty string", mLoc); + break; + } else { error = true; From fea7e68fe9bcaca9c77353cdd452118b47b4cc14 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 25 Mar 2020 20:34:08 +0100 Subject: [PATCH 005/226] silence operator precedence warning --- components/compiler/scanner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 24c13a1d2..b06d683f7 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -368,7 +368,7 @@ namespace Compiler { if (mIgnoreNewline) mErrorHandler.warning ("string contains newline character, make sure that it is intended", mLoc); - else if (name.size() == 1 || name.size() == 2 && name[1] == '\r') + else if (name.size() == 1 || (name.size() == 2 && name[1] == '\r')) { name.clear(); mLoc.mLiteral.clear(); From 2e7712a390828dafec7ef7fd43be44d7f0d1547a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 26 Mar 2020 12:07:32 +0400 Subject: [PATCH 006/226] Fix C5204 warnings by adding default virtual destructors --- apps/openmw/mwgui/bookpage.hpp | 5 +++-- apps/openmw/mwgui/journalviewmodel.hpp | 9 ++++++++- apps/openmw/mwgui/windowbase.hpp | 1 + apps/openmw/mwrender/animation.hpp | 2 ++ apps/openmw/mwworld/cellpreloader.cpp | 2 ++ apps/openmw/mwworld/cellstore.hpp | 1 + apps/openmw/mwworld/containerstore.hpp | 1 + apps/openmw/mwworld/inventorystore.hpp | 1 + components/debug/debugging.hpp | 2 ++ components/detournavigator/navmeshtilescache.hpp | 4 ++++ components/loadinglistener/loadinglistener.hpp | 2 ++ components/widgets/box.hpp | 4 ++++ extern/oics/ICSChannelListener.h | 4 +++- extern/oics/ICSControlListener.h | 4 +++- extern/oics/ICSInputControlSystem.h | 4 ++++ 15 files changed, 41 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index 7f7dfd20a..4e49b8f67 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -34,6 +34,8 @@ namespace MWGui /// right edge. The second integer is the height of all /// text combined prior to pagination. virtual std::pair getSize () const = 0; + + virtual ~TypesetBook() = default; }; struct GlyphInfo @@ -87,8 +89,7 @@ namespace MWGui typedef uint8_t const * Utf8Point; typedef std::pair Utf8Span; - - + virtual ~BookTypesetter() = default; enum Alignment { AlignLeft = -1, diff --git a/apps/openmw/mwgui/journalviewmodel.hpp b/apps/openmw/mwgui/journalviewmodel.hpp index fa4090225..3a9372130 100644 --- a/apps/openmw/mwgui/journalviewmodel.hpp +++ b/apps/openmw/mwgui/journalviewmodel.hpp @@ -39,6 +39,8 @@ namespace MWGui /// and end of the span relative to the body, and a valid topic ID if /// the span represents a keyword, or zero if not. virtual void visitSpans (std::function visitor) const = 0; + + virtual ~Entry() = default; }; /// An interface to topic data. @@ -47,6 +49,8 @@ namespace MWGui /// Returns a pre-formatted span of UTF8 encoded text representing /// the name of the NPC this portion of dialog was heard from. virtual Utf8Span source () const = 0; + + virtual ~TopicEntry() = default; }; /// An interface to journal data. @@ -55,8 +59,9 @@ namespace MWGui /// Returns a pre-formatted span of UTF8 encoded text representing /// the in-game date this entry was added to the journal. virtual Utf8Span timestamp () const = 0; - }; + virtual ~JournalEntry() = default; + }; /// called prior to journal opening virtual void load () = 0; @@ -85,6 +90,8 @@ namespace MWGui // create an instance of the default journal view model implementation static Ptr create (); + + virtual ~JournalViewModel() = default; }; } diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 56249d77d..a729a7920 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -81,6 +81,7 @@ namespace MWGui void onFrame(float dt); virtual void setAlpha(float alpha); + virtual ~NoDrop() = default; private: MyGUI::Widget* mWidget; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index b4d4ac664..17e13f047 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -149,6 +149,8 @@ public: public: virtual void handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, const std::multimap& map) = 0; + + virtual ~TextKeyListener() = default; }; void setTextKeyListener(TextKeyListener* listener); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 77f522ea6..9a96e9806 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -40,6 +40,8 @@ namespace MWWorld return true; } + virtual ~ListModelsVisitor() = default; + std::vector& mOut; }; diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 9529de855..371867631 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -382,6 +382,7 @@ namespace MWWorld public: ///@note must return nullptr if the cell is not found virtual CellStore* getCellStore(const ESM::CellId& cellId) = 0; + virtual ~GetCellStoreCallback() = default; }; /// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved references) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index bbe031d1a..f2858c5aa 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -43,6 +43,7 @@ namespace MWWorld public: virtual void itemAdded(const ConstPtr& item, int count) {} virtual void itemRemoved(const ConstPtr& item, int count) {} + virtual ~ContainerStoreListener() = default; }; class ContainerStore diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 663e7a2d3..e18132f4b 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -34,6 +34,7 @@ namespace MWWorld */ virtual void permanentEffectAdded (const ESM::MagicEffect *magicEffect, bool isNew) {} + virtual ~InventoryStoreListener() = default; }; ///< \brief Variant of the ContainerStore for NPCs diff --git a/components/debug/debugging.hpp b/components/debug/debugging.hpp index e815543a1..1cae4b006 100644 --- a/components/debug/debugging.hpp +++ b/components/debug/debugging.hpp @@ -38,6 +38,8 @@ namespace Debug virtual std::streamsize write(const char *str, std::streamsize size); + virtual ~DebugOutputBase() = default; + protected: static Level getLevelMarker(const char *str); diff --git a/components/detournavigator/navmeshtilescache.hpp b/components/detournavigator/navmeshtilescache.hpp index 5d5db47a8..57f57a56f 100644 --- a/components/detournavigator/navmeshtilescache.hpp +++ b/components/detournavigator/navmeshtilescache.hpp @@ -118,6 +118,8 @@ namespace DetourNavigator public: KeyView() = default; + virtual ~KeyView() = default; + KeyView(const std::string& value) : mValue(&value) {} @@ -161,6 +163,8 @@ namespace DetourNavigator return compare(other.getValue()) < 0; } + virtual ~RecastMeshKeyView() = default; + private: std::reference_wrapper mRecastMesh; std::reference_wrapper> mOffMeshConnections; diff --git a/components/loadinglistener/loadinglistener.hpp b/components/loadinglistener/loadinglistener.hpp index f5cfa5cf3..93467c141 100644 --- a/components/loadinglistener/loadinglistener.hpp +++ b/components/loadinglistener/loadinglistener.hpp @@ -29,6 +29,8 @@ namespace Loading virtual void setProgress (size_t value) {} /// Increase current progress, default by 1. virtual void increaseProgress (size_t increase = 1) {} + + virtual ~Listener() = default; }; /// @brief Used for stopping a loading sequence when the object goes out of scope diff --git a/components/widgets/box.hpp b/components/widgets/box.hpp index 59bb7f88b..e84c3bb1f 100644 --- a/components/widgets/box.hpp +++ b/components/widgets/box.hpp @@ -33,6 +33,8 @@ namespace Gui virtual MyGUI::IntSize getRequestedSize() = 0; + virtual ~AutoSizedWidget() = default; + protected: void notifySizeChange(MyGUI::Widget* w); @@ -94,6 +96,8 @@ namespace Gui public: Box(); + virtual ~Box() = default; + void notifyChildrenSizeChanged(); protected: diff --git a/extern/oics/ICSChannelListener.h b/extern/oics/ICSChannelListener.h index a202e7e22..1f50d0e31 100644 --- a/extern/oics/ICSChannelListener.h +++ b/extern/oics/ICSChannelListener.h @@ -38,9 +38,11 @@ namespace ICS { public: virtual void channelChanged(Channel* channel, float currentValue, float previousValue) = 0; + + virtual ~ChannelListener() = default; }; } -#endif +#endif diff --git a/extern/oics/ICSControlListener.h b/extern/oics/ICSControlListener.h index 97b3940be..73fd06cb1 100644 --- a/extern/oics/ICSControlListener.h +++ b/extern/oics/ICSControlListener.h @@ -38,9 +38,11 @@ namespace ICS { public: virtual void controlChanged(Control* control, float currentValue, float previousValue) = 0; + + virtual ~ControlListener() = default; }; } -#endif +#endif diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h index 3e808dd2e..43e659d0a 100644 --- a/extern/oics/ICSInputControlSystem.h +++ b/extern/oics/ICSInputControlSystem.h @@ -45,6 +45,8 @@ namespace ICS { public: virtual void logMessage(const char* text) = 0; + + virtual ~InputControlSystemLog() = default; }; class DllExport InputControlSystem @@ -262,6 +264,8 @@ namespace ICS InputControlSystem::MouseWheelClick click, Control::ControlChangingDirection direction); + virtual ~DetectingBindingListener() = default; + /* OPENMW CODE ENDS HERE * ------------------------------------------------------------------------------------- */ }; From fe75308fdb158455749f72b45c54a86a2e987e44 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 26 Mar 2020 14:51:14 +0400 Subject: [PATCH 007/226] Disable warning C5204 which come from Boost library --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 58376a7ce..1eb69c568 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -716,6 +716,7 @@ if (WIN32) # caused by boost 4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off) 4643 # Forward declaring 'X' in namespace std is not permitted by the C++ Standard. (in *_std_fwd.h files) + 5204 # Class has virtual functions, but its trivial destructor is not virtual # caused by MyGUI 4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception' From 24c8b32d4cefa0bf65bb51e98a58c643058139a1 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 7 Feb 2020 19:04:28 +0200 Subject: [PATCH 008/226] Implement brush outline for terrainshapemode --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/brushdraw.cpp | 136 ++++++++++++++++++ apps/opencs/view/render/brushdraw.hpp | 31 ++++ apps/opencs/view/render/editmode.cpp | 2 + apps/opencs/view/render/editmode.hpp | 2 + apps/opencs/view/render/terrainshapemode.cpp | 17 +++ apps/opencs/view/render/terrainshapemode.hpp | 4 + .../opencs/view/render/terraintexturemode.cpp | 2 + .../opencs/view/render/terraintexturemode.hpp | 2 + apps/opencs/view/render/worldspacewidget.cpp | 2 + 10 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/view/render/brushdraw.cpp create mode 100644 apps/opencs/view/render/brushdraw.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index af8bf8d55..6d0f2ad9f 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -89,7 +89,7 @@ opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller - cellwater terraintexturemode actor terrainselection terrainshapemode + cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw ) opencs_units_noqt (view/render diff --git a/apps/opencs/view/render/brushdraw.cpp b/apps/opencs/view/render/brushdraw.cpp new file mode 100644 index 000000000..a2874bd31 --- /dev/null +++ b/apps/opencs/view/render/brushdraw.cpp @@ -0,0 +1,136 @@ +#include "brushdraw.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +#include "mask.hpp" + +CSVRender::BrushDraw::BrushDraw(osg::Group* parentNode) : + mParentNode(parentNode) +{ + mBrushDrawNode = new osg::Group(); + mGeometry = new osg::Geometry(); + mBrushDrawNode->addChild(mGeometry); + mParentNode->addChild(mBrushDrawNode); +} + +CSVRender::BrushDraw::~BrushDraw() +{ + if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); + if (mParentNode->containsNode(mBrushDrawNode)) mParentNode->removeChild(mBrushDrawNode); +} + +float CSVRender::BrushDraw::getIntersectionHeight (const osg::Vec3d& point) +{ + osg::Vec3d start = point; + osg::Vec3d end = point; + start.z() += 8000.0f; // these numbers need fixing + end.z() -= 8000.0f; + osg::Vec3d direction = end - start; + + // Get intersection + osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector( + osgUtil::Intersector::MODEL, start, end) ); + intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); + osgUtil::IntersectionVisitor visitor(intersector); + + visitor.setTraversalMask(Mask_Terrain); + + mParentNode->accept(visitor); + + for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin(); + it != intersector->getIntersections().end(); ++it) + { + osgUtil::LineSegmentIntersector::Intersection intersection = *it; + + // reject back-facing polygons + if (direction * intersection.getWorldIntersectNormal() > 0) + { + continue; + } + + return intersection.getWorldIntersectPoint().z(); + } + return 0.0f; +} + + +void CSVRender::BrushDraw::buildGeometry(const float& radius, const osg::Vec3d& point, int amountOfPoints) +{ + osg::ref_ptr geom = new osg::Geometry(); + osg::ref_ptr vertices (new osg::Vec3Array()); + osg::ref_ptr colors (new osg::Vec4Array()); + const float step ((osg::PI * 2.0f) / static_cast(amountOfPoints)); + + for (int i = 0; i < amountOfPoints + 2; i++) + { + float angle (static_cast(i) * step); + vertices->push_back(osg::Vec3d( + point.x() + radius * cosf(angle), + point.y() + radius * sinf(angle), + getIntersectionHeight(osg::Vec3d( + point.x() + radius * cosf(angle), + point.y() + radius * sinf(angle), + point.z()) ))); + colors->push_back(osg::Vec4f( + 50.0f, + 50.0f, + 50.0f, + 100.0f)); + angle = static_cast(i + 1) * step; + vertices->push_back(osg::Vec3d( + point.x() + radius * cosf(angle), + point.y() + radius * sinf(angle), + getIntersectionHeight(osg::Vec3d( + point.x() + radius * cosf(angle), + point.y() + radius * sinf(angle), + point.z()) ) + 200.0f)); + colors->push_back(osg::Vec4f( + 50.0f, + 50.0f, + 50.0f, + 100.0f)); + } + + geom->setVertexArray(vertices); + geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX); + geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP, 0, (amountOfPoints + 2) * 2 - 2)); + mGeometry = geom; +} + +void CSVRender::BrushDraw::update(osg::Vec3d point, int brushSize) +{ + if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); + mBrushDrawNode->setNodeMask (Mask_EditModeCursor); + float radius = static_cast(brushSize * mLandSizeFactor); + int amountOfPoints = (osg::PI * 2.0f) * radius / 20; + + buildGeometry(radius, point, amountOfPoints); + + osg::BlendFunc* blendFunc = new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA); + mGeometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc); + + mGeometry->getOrCreateStateSet()->setMode( GL_CULL_FACE, osg::StateAttribute::OFF ); + + osg::ref_ptr material = new osg::Material; + material->setColorMode(osg::Material::AMBIENT); + material->setAmbient (osg::Material::FRONT_AND_BACK, osg::Vec4(0.3, 0.3, 0.3, 1)); + material->setAlpha(osg::Material::FRONT_AND_BACK, 0.5); + + mGeometry->getOrCreateStateSet()->setAttributeAndModes(material.get(), osg::StateAttribute::ON); + mGeometry->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); + mGeometry->getOrCreateStateSet()->setRenderingHint (osg::StateSet::TRANSPARENT_BIN); + + mBrushDrawNode->addChild(mGeometry); +} + +void CSVRender::BrushDraw::hide() +{ + if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); +} diff --git a/apps/opencs/view/render/brushdraw.hpp b/apps/opencs/view/render/brushdraw.hpp new file mode 100644 index 000000000..5e0a095ce --- /dev/null +++ b/apps/opencs/view/render/brushdraw.hpp @@ -0,0 +1,31 @@ +#ifndef CSV_RENDER_BRUSHDRAW_H +#define CSV_RENDER_BRUSHDRAW_H + +#include +#include + +#include + +namespace CSVRender +{ + class BrushDraw + { + public: + BrushDraw(osg::Group* parentNode); + ~BrushDraw(); + + void update(osg::Vec3d point, int brushSize); + void hide(); + + private: + void buildGeometry(const float& radius, const osg::Vec3d& point, int amountOfPoints); + float getIntersectionHeight (const osg::Vec3d& point); + + osg::Group* mParentNode; + osg::ref_ptr mBrushDrawNode; + osg::ref_ptr mGeometry; + float mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE / 2; + }; +} + +#endif diff --git a/apps/opencs/view/render/editmode.cpp b/apps/opencs/view/render/editmode.cpp index 03451bc1b..ca4aa0fd5 100644 --- a/apps/opencs/view/render/editmode.cpp +++ b/apps/opencs/view/render/editmode.cpp @@ -73,6 +73,8 @@ void CSVRender::EditMode::dropEvent (QDropEvent *event) {} void CSVRender::EditMode::dragMoveEvent (QDragMoveEvent *event) {} +void CSVRender::EditMode::mouseMoveEvent (QMouseEvent *event) {} + int CSVRender::EditMode::getSubMode() const { return -1; diff --git a/apps/opencs/view/render/editmode.hpp b/apps/opencs/view/render/editmode.hpp index 9f3b28957..911594327 100644 --- a/apps/opencs/view/render/editmode.hpp +++ b/apps/opencs/view/render/editmode.hpp @@ -98,6 +98,8 @@ namespace CSVRender /// Default-implementation: ignored virtual void dragMoveEvent (QDragMoveEvent *event); + virtual void mouseMoveEvent (QMouseEvent *event); + /// Default: return -1 virtual int getSubMode() const; }; diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 2a6d7f33f..53fe92f8e 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -37,6 +37,7 @@ #include "../../model/world/tablemimedata.hpp" #include "../../model/world/universalid.hpp" +#include "brushdraw.hpp" #include "editmode.hpp" #include "pagedworldspacewidget.hpp" #include "tagbase.hpp" @@ -49,6 +50,12 @@ CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidge { } +CSVRender::TerrainShapeMode::~TerrainShapeMode () +{ + if (mBrushDraw) + mBrushDraw.reset(); +} + void CSVRender::TerrainShapeMode::activate(CSVWidget::SceneToolbar* toolbar) { if (!mTerrainShapeSelection) @@ -67,6 +74,9 @@ void CSVRender::TerrainShapeMode::activate(CSVWidget::SceneToolbar* toolbar) connect(mShapeBrushScenetool->mShapeBrushWindow->mToolStrengthSlider, SIGNAL(valueChanged(int)), this, SLOT(setShapeEditToolStrength(int))); } + if (!mBrushDraw) + mBrushDraw.reset(new BrushDraw(mParentNode)); + EditMode::activate(toolbar); toolbar->addTool (mShapeBrushScenetool); } @@ -1382,6 +1392,13 @@ void CSVRender::TerrainShapeMode::dragMoveEvent (QDragMoveEvent *event) { } +void CSVRender::TerrainShapeMode::mouseMoveEvent (QMouseEvent *event) +{ + WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask()); + if (hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->update(hit.worldPos, mBrushSize); + if (!hit.hit && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->hide(); +} + void CSVRender::TerrainShapeMode::setBrushSize(int brushSize) { mBrushSize = brushSize; diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 68f2fbf9d..fb80e79af 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -19,6 +19,7 @@ #include "../widget/brushshapes.hpp" #endif +#include "brushdraw.hpp" #include "terrainselection.hpp" namespace CSVWidget @@ -57,6 +58,7 @@ namespace CSVRender /// Editmode for terrain shape grid TerrainShapeMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); + ~TerrainShapeMode(); void primaryOpenPressed (const WorldspaceHitResult& hit) final; @@ -89,6 +91,7 @@ namespace CSVRender void dragWheel (int diff, double speedFactor) final; void dragMoveEvent (QDragMoveEvent *event) final; + void mouseMoveEvent (QMouseEvent *event) final; private: @@ -168,6 +171,7 @@ namespace CSVRender std::string mBrushTexture; int mBrushSize = 1; CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point; + std::unique_ptr mBrushDraw; std::vector> mCustomBrushShape; CSVWidget::SceneToolShapeBrush *mShapeBrushScenetool = nullptr; int mDragMode = InteractionType_None; diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index efdb600b8..e25d65a89 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -707,6 +707,8 @@ void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) { } +void CSVRender::TerrainTextureMode::mouseMoveEvent (QMouseEvent *event) {} + void CSVRender::TerrainTextureMode::setBrushSize(int brushSize) { mBrushSize = brushSize; diff --git a/apps/opencs/view/render/terraintexturemode.hpp b/apps/opencs/view/render/terraintexturemode.hpp index 4176abefe..3b94097ee 100644 --- a/apps/opencs/view/render/terraintexturemode.hpp +++ b/apps/opencs/view/render/terraintexturemode.hpp @@ -81,6 +81,8 @@ namespace CSVRender void dragWheel (int diff, double speedFactor) final; void dragMoveEvent (QDragMoveEvent *event) final; + void mouseMoveEvent (QMouseEvent *event) final; + private: /// \brief Handle brush mechanics, maths regarding worldspace hit etc. void editTerrainTextureGrid (const WorldspaceHitResult& hit); diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 6ab4b041b..4755de97b 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -613,6 +613,8 @@ void CSVRender::WorldspaceWidget::updateOverlay() void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) { + dynamic_cast (*mEditMode->getCurrent()).mouseMoveEvent (event); + if (mDragging) { int diffX = event->x() - mDragX; From 731e5b57f5b13923ec3b512f7ab9cc9a7b0fa93f Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 15 Feb 2020 16:09:49 +0200 Subject: [PATCH 009/226] Tool outline for terraintexturemode and square shape. Various fixes. --- apps/opencs/view/render/brushdraw.cpp | 100 ++++++++++++++++-- apps/opencs/view/render/brushdraw.hpp | 14 ++- apps/opencs/view/render/terrainshapemode.cpp | 5 +- .../opencs/view/render/terraintexturemode.cpp | 44 +++++--- .../opencs/view/render/terraintexturemode.hpp | 18 ++-- .../view/widget/scenetooltexturebrush.cpp | 23 ++-- .../view/widget/scenetooltexturebrush.hpp | 11 +- 7 files changed, 162 insertions(+), 53 deletions(-) diff --git a/apps/opencs/view/render/brushdraw.cpp b/apps/opencs/view/render/brushdraw.cpp index a2874bd31..b963b09f5 100644 --- a/apps/opencs/view/render/brushdraw.cpp +++ b/apps/opencs/view/render/brushdraw.cpp @@ -1,5 +1,7 @@ #include "brushdraw.hpp" +#include + #include #include #include @@ -9,29 +11,33 @@ #include +#include "../widget/brushshapes.hpp" + #include "mask.hpp" -CSVRender::BrushDraw::BrushDraw(osg::Group* parentNode) : +CSVRender::BrushDraw::BrushDraw(osg::ref_ptr parentNode, bool textureMode) : mParentNode(parentNode) { mBrushDrawNode = new osg::Group(); mGeometry = new osg::Geometry(); mBrushDrawNode->addChild(mGeometry); mParentNode->addChild(mBrushDrawNode); + if (textureMode) mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_TEXTURE_SIZE / 2; + else mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE / 2; } CSVRender::BrushDraw::~BrushDraw() { - if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); - if (mParentNode->containsNode(mBrushDrawNode)) mParentNode->removeChild(mBrushDrawNode); + mBrushDrawNode->removeChild(mGeometry); + mParentNode->removeChild(mBrushDrawNode); } float CSVRender::BrushDraw::getIntersectionHeight (const osg::Vec3d& point) { osg::Vec3d start = point; osg::Vec3d end = point; - start.z() += 8000.0f; // these numbers need fixing - end.z() -= 8000.0f; + start.z() = std::numeric_limits::max(); + end.z() = std::numeric_limits::min(); osg::Vec3d direction = end - start; // Get intersection @@ -60,13 +66,67 @@ float CSVRender::BrushDraw::getIntersectionHeight (const osg::Vec3d& point) return 0.0f; } +void CSVRender::BrushDraw::buildPointGeometry(const float& radius, const osg::Vec3d& point) +{ + // Not implemented +} -void CSVRender::BrushDraw::buildGeometry(const float& radius, const osg::Vec3d& point, int amountOfPoints) +void CSVRender::BrushDraw::buildSquareGeometry(const float& radius, const osg::Vec3d& point) { osg::ref_ptr geom = new osg::Geometry(); osg::ref_ptr vertices (new osg::Vec3Array()); osg::ref_ptr colors (new osg::Vec4Array()); + std::vector corners; + const float brushOutLineHeight (200.0f); + + corners.push_back(osg::Vec3d(point.x() - radius, point.y() - radius, point.z())); + corners.push_back(osg::Vec3d(point.x() + radius, point.y() - radius, point.z())); + corners.push_back(osg::Vec3d(point.x() + radius, point.y() + radius, point.z())); + corners.push_back(osg::Vec3d(point.x() - radius, point.y() + radius, point.z())); + corners.push_back(osg::Vec3d(point.x() - radius, point.y() - radius, point.z())); + + for (const auto& point : corners) + { + vertices->push_back(osg::Vec3d( + point.x(), + point.y(), + getIntersectionHeight(osg::Vec3d( + point.x(), + point.y(), + point.z()) ))); + colors->push_back(osg::Vec4f( + 50.0f, + 50.0f, + 50.0f, + 100.0f)); + vertices->push_back(osg::Vec3d( + point.x(), + point.y(), + getIntersectionHeight(osg::Vec3d( + point.x(), + point.y(), + point.z())) + brushOutLineHeight)); + colors->push_back(osg::Vec4f( + 50.0f, + 50.0f, + 50.0f, + 100.0f)); + } + + geom->setVertexArray(vertices); + geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX); + geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP, 0, (10 + 2) - 2)); + mGeometry = geom; +} + +void CSVRender::BrushDraw::buildCircleGeometry(const float& radius, const osg::Vec3d& point) +{ + osg::ref_ptr geom = new osg::Geometry(); + osg::ref_ptr vertices (new osg::Vec3Array()); + osg::ref_ptr colors (new osg::Vec4Array()); + const int amountOfPoints = (osg::PI * 2.0f) * radius / 20; const float step ((osg::PI * 2.0f) / static_cast(amountOfPoints)); + const float brushOutLineHeight (200.0f); for (int i = 0; i < amountOfPoints + 2; i++) { @@ -90,7 +150,7 @@ void CSVRender::BrushDraw::buildGeometry(const float& radius, const osg::Vec3d& getIntersectionHeight(osg::Vec3d( point.x() + radius * cosf(angle), point.y() + radius * sinf(angle), - point.z()) ) + 200.0f)); + point.z()) ) + brushOutLineHeight)); colors->push_back(osg::Vec4f( 50.0f, 50.0f, @@ -104,14 +164,34 @@ void CSVRender::BrushDraw::buildGeometry(const float& radius, const osg::Vec3d& mGeometry = geom; } -void CSVRender::BrushDraw::update(osg::Vec3d point, int brushSize) +void CSVRender::BrushDraw::buildCustomGeometry(const float& radius, const osg::Vec3d& point) +{ + // Not implemented +} + +void CSVRender::BrushDraw::update(osg::Vec3d point, int brushSize, CSVWidget::BrushShape toolShape) { if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); mBrushDrawNode->setNodeMask (Mask_EditModeCursor); float radius = static_cast(brushSize * mLandSizeFactor); - int amountOfPoints = (osg::PI * 2.0f) * radius / 20; - buildGeometry(radius, point, amountOfPoints); + switch (toolShape) + { + case (CSVWidget::BrushShape_Point) : + buildSquareGeometry(1, point); + //buildPointGeometry(radius, point); + break; + case (CSVWidget::BrushShape_Square) : + buildSquareGeometry(radius, point); + break; + case (CSVWidget::BrushShape_Circle) : + buildCircleGeometry(radius, point); + break; + case (CSVWidget::BrushShape_Custom) : + buildSquareGeometry(1, point); + //buildCustomGeometry + break; + } osg::BlendFunc* blendFunc = new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA); mGeometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc); diff --git a/apps/opencs/view/render/brushdraw.hpp b/apps/opencs/view/render/brushdraw.hpp index 5e0a095ce..6ecfccde7 100644 --- a/apps/opencs/view/render/brushdraw.hpp +++ b/apps/opencs/view/render/brushdraw.hpp @@ -5,26 +5,30 @@ #include #include +#include "../widget/brushshapes.hpp" namespace CSVRender { class BrushDraw { public: - BrushDraw(osg::Group* parentNode); + BrushDraw(osg::ref_ptr parentNode, bool textureMode = false); ~BrushDraw(); - void update(osg::Vec3d point, int brushSize); + void update(osg::Vec3d point, int brushSize, CSVWidget::BrushShape toolShape); void hide(); private: - void buildGeometry(const float& radius, const osg::Vec3d& point, int amountOfPoints); + void buildPointGeometry(const float& radius, const osg::Vec3d& point); + void buildSquareGeometry(const float& radius, const osg::Vec3d& point); + void buildCircleGeometry(const float& radius, const osg::Vec3d& point); + void buildCustomGeometry(const float& radius, const osg::Vec3d& point); float getIntersectionHeight (const osg::Vec3d& point); - osg::Group* mParentNode; + osg::ref_ptr mParentNode; osg::ref_ptr mBrushDrawNode; osg::ref_ptr mGeometry; - float mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE / 2; + float mLandSizeFactor; }; } diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 53fe92f8e..31292645d 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -93,6 +93,9 @@ void CSVRender::TerrainShapeMode::deactivate(CSVWidget::SceneToolbar* toolbar) mTerrainShapeSelection.reset(); } + if (mBrushDraw) + mBrushDraw.reset(); + EditMode::deactivate(toolbar); } @@ -1395,7 +1398,7 @@ void CSVRender::TerrainShapeMode::dragMoveEvent (QDragMoveEvent *event) void CSVRender::TerrainShapeMode::mouseMoveEvent (QMouseEvent *event) { WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask()); - if (hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->update(hit.worldPos, mBrushSize); + if (hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape); if (!hit.hit && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->hide(); } diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index e25d65a89..c1bbb2825 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -32,7 +32,9 @@ #include "../../model/world/resourcetable.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/universalid.hpp" +#include "../widget/brushshapes.hpp" +#include "brushdraw.hpp" #include "editmode.hpp" #include "pagedworldspacewidget.hpp" #include "object.hpp" // Something small needed regarding pointers from here () @@ -50,6 +52,12 @@ CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceW { } +CSVRender::TerrainTextureMode::~TerrainTextureMode () +{ + if (mBrushDraw) + mBrushDraw.reset(); +} + void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar) { if(!mTextureBrushScenetool) @@ -57,7 +65,7 @@ void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar) mTextureBrushScenetool = new CSVWidget::SceneToolTextureBrush (toolbar, "scenetooltexturebrush", getWorldspaceWidget().getDocument()); connect(mTextureBrushScenetool, SIGNAL (clicked()), mTextureBrushScenetool, SLOT (activate())); connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passBrushSize(int)), this, SLOT(setBrushSize(int))); - connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setBrushShape(int))); + connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passBrushShape(CSVWidget::BrushShape)), this, SLOT(setBrushShape(CSVWidget::BrushShape))); connect(mTextureBrushScenetool->mTextureBrushWindow->mSizeSliders->mBrushSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(setBrushSize(int))); connect(mTextureBrushScenetool, SIGNAL(passTextureId(std::string)), this, SLOT(setBrushTexture(std::string))); connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passTextureId(std::string)), this, SLOT(setBrushTexture(std::string))); @@ -72,6 +80,9 @@ void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar) mTerrainTextureSelection.reset(new TerrainSelection(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Texture)); } + if (!mBrushDraw) + mBrushDraw.reset(new BrushDraw(mParentNode, true)); + EditMode::activate(toolbar); toolbar->addTool (mTextureBrushScenetool); } @@ -90,6 +101,9 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar) mTerrainTextureSelection.reset(); } + if (mBrushDraw) + mBrushDraw.reset(); + EditMode::deactivate(toolbar); } @@ -332,7 +346,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe int r = mBrushSize / 2 + 1; int distance = 0; - if (mBrushShape == 0) + if (mBrushShape == CSVWidget::BrushShape_Point) { CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value(); CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer); @@ -344,7 +358,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe } } - if (mBrushShape == 1) + if (mBrushShape == CSVWidget::BrushShape_Square) { int upperLeftCellX = cellX - std::floor(r / landTextureSize); int upperLeftCellY = cellY - std::floor(r / landTextureSize); @@ -394,7 +408,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe } } - if (mBrushShape == 2) + if (mBrushShape == CSVWidget::BrushShape_Circle) { int upperLeftCellX = cellX - std::floor(r / landTextureSize); int upperLeftCellY = cellY - std::floor(r / landTextureSize); @@ -454,7 +468,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe } } - if (mBrushShape == 3) + if (mBrushShape == CSVWidget::BrushShape_Custom) { CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value(); CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer); @@ -506,12 +520,12 @@ void CSVRender::TerrainTextureMode::selectTerrainTextures(const std::pair> selections; - if (mBrushShape == 0) + if (mBrushShape == CSVWidget::BrushShape_Point) { if (isInCellSelection(texCoords.first, texCoords.second)) selections.emplace_back(texCoords); } - if (mBrushShape == 1) + if (mBrushShape == CSVWidget::BrushShape_Square) { for (int i = -r; i <= r; i++) { @@ -527,7 +541,7 @@ void CSVRender::TerrainTextureMode::selectTerrainTextures(const std::pairpos(), getInteractionMask()); + if (hit.hit && mBrushDraw) mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape); + if (!hit.hit) mBrushDraw->hide(); +} + void CSVRender::TerrainTextureMode::setBrushSize(int brushSize) { mBrushSize = brushSize; } -void CSVRender::TerrainTextureMode::setBrushShape(int brushShape) +void CSVRender::TerrainTextureMode::setBrushShape(CSVWidget::BrushShape brushShape) { mBrushShape = brushShape; //Set custom brush shape - if (mBrushShape == 3 && !mTerrainTextureSelection->getTerrainSelection().empty()) + if (mBrushShape == CSVWidget::BrushShape_Custom && !mTerrainTextureSelection->getTerrainSelection().empty()) { auto terrainSelection = mTerrainTextureSelection->getTerrainSelection(); int selectionCenterX = 0; diff --git a/apps/opencs/view/render/terraintexturemode.hpp b/apps/opencs/view/render/terraintexturemode.hpp index 3b94097ee..cc77bb331 100644 --- a/apps/opencs/view/render/terraintexturemode.hpp +++ b/apps/opencs/view/render/terraintexturemode.hpp @@ -17,6 +17,8 @@ #include "../../model/world/commands.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/landtexture.hpp" +#include "../widget/brushshapes.hpp" +#include "brushdraw.hpp" #endif #include "terrainselection.hpp" @@ -50,6 +52,7 @@ namespace CSVRender /// \brief Editmode for terrain texture grid TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); + ~TerrainTextureMode(); void primaryOpenPressed (const WorldspaceHitResult& hit) final; @@ -104,14 +107,15 @@ namespace CSVRender bool allowLandTextureEditing(std::string textureFileName); std::string mCellId; - std::string mBrushTexture; - int mBrushSize; - int mBrushShape; + std::string mBrushTexture = "L0#0"; + int mBrushSize = 1; + CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point; + std::unique_ptr mBrushDraw; std::vector> mCustomBrushShape; - CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool; - int mDragMode; + CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool = nullptr; + int mDragMode = InteractionType_None; osg::Group* mParentNode; - bool mIsEditing; + bool mIsEditing = false; std::unique_ptr mTerrainTextureSelection; const int cellSize {ESM::Land::REAL_SIZE}; @@ -123,7 +127,7 @@ namespace CSVRender public slots: void handleDropEvent(QDropEvent *event); void setBrushSize(int brushSize); - void setBrushShape(int brushShape); + void setBrushShape(CSVWidget::BrushShape brushShape); void setBrushTexture(std::string brushShape); }; } diff --git a/apps/opencs/view/widget/scenetooltexturebrush.cpp b/apps/opencs/view/widget/scenetooltexturebrush.cpp index 408187279..0a1ed6683 100644 --- a/apps/opencs/view/widget/scenetooltexturebrush.cpp +++ b/apps/opencs/view/widget/scenetooltexturebrush.cpp @@ -57,9 +57,6 @@ CSVWidget::BrushSizeControls::BrushSizeControls(const QString &title, QWidget *p CSVWidget::TextureBrushWindow::TextureBrushWindow(CSMDoc::Document& document, QWidget *parent) : QFrame(parent, Qt::Popup), - mBrushShape(0), - mBrushSize(1), - mBrushTexture("L0#0"), mDocument(document) { mBrushTextureLabel = "Selected texture: " + mBrushTexture + " "; @@ -207,10 +204,10 @@ void CSVWidget::TextureBrushWindow::setBrushSize(int brushSize) void CSVWidget::TextureBrushWindow::setBrushShape() { - if(mButtonPoint->isChecked()) mBrushShape = 0; - if(mButtonSquare->isChecked()) mBrushShape = 1; - if(mButtonCircle->isChecked()) mBrushShape = 2; - if(mButtonCustom->isChecked()) mBrushShape = 3; + if(mButtonPoint->isChecked()) mBrushShape = CSVWidget::BrushShape_Point; + if(mButtonSquare->isChecked()) mBrushShape = CSVWidget::BrushShape_Square; + if(mButtonCircle->isChecked()) mBrushShape = CSVWidget::BrushShape_Circle; + if(mButtonCustom->isChecked()) mBrushShape = CSVWidget::BrushShape_Custom; emit passBrushShape(mBrushShape); } @@ -228,7 +225,7 @@ CSVWidget::SceneToolTextureBrush::SceneToolTextureBrush (SceneToolbar *parent, c mBrushHistory[0] = "L0#0"; setAcceptDrops(true); - connect(mTextureBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setButtonIcon(int))); + connect(mTextureBrushWindow, SIGNAL(passBrushShape(CSVWidget::BrushShape)), this, SLOT(setButtonIcon(CSVWidget::BrushShape))); setButtonIcon(mTextureBrushWindow->mBrushShape); mPanel = new QFrame (this, Qt::Popup); @@ -258,31 +255,31 @@ CSVWidget::SceneToolTextureBrush::SceneToolTextureBrush (SceneToolbar *parent, c } -void CSVWidget::SceneToolTextureBrush::setButtonIcon (int brushShape) +void CSVWidget::SceneToolTextureBrush::setButtonIcon (CSVWidget::BrushShape brushShape) { QString tooltip = "Change brush settings

Currently selected: "; switch (brushShape) { - case 0: + case BrushShape_Point: setIcon (QIcon (QPixmap (":scenetoolbar/brush-point"))); tooltip += mTextureBrushWindow->toolTipPoint; break; - case 1: + case BrushShape_Square: setIcon (QIcon (QPixmap (":scenetoolbar/brush-square"))); tooltip += mTextureBrushWindow->toolTipSquare; break; - case 2: + case BrushShape_Circle: setIcon (QIcon (QPixmap (":scenetoolbar/brush-circle"))); tooltip += mTextureBrushWindow->toolTipCircle; break; - case 3: + case BrushShape_Custom: setIcon (QIcon (QPixmap (":scenetoolbar/brush-custom"))); tooltip += mTextureBrushWindow->toolTipCustom; diff --git a/apps/opencs/view/widget/scenetooltexturebrush.hpp b/apps/opencs/view/widget/scenetooltexturebrush.hpp index 80e9a9382..5f5ccc6b1 100644 --- a/apps/opencs/view/widget/scenetooltexturebrush.hpp +++ b/apps/opencs/view/widget/scenetooltexturebrush.hpp @@ -15,6 +15,7 @@ #include #ifndef Q_MOC_RUN +#include "brushshapes.hpp" #include "scenetool.hpp" #include "../../model/doc/document.hpp" @@ -65,9 +66,9 @@ namespace CSVWidget const QString toolTipCustom = "Paint custom selection (not implemented yet)"; private: - int mBrushShape; - int mBrushSize; - std::string mBrushTexture; + CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point; + int mBrushSize = 1; + std::string mBrushTexture = "L0#0"; CSMDoc::Document& mDocument; QLabel *mSelectedBrush; QGroupBox *mHorizontalGroupBox; @@ -88,7 +89,7 @@ namespace CSVWidget signals: void passBrushSize (int brushSize); - void passBrushShape(int brushShape); + void passBrushShape(CSVWidget::BrushShape brushShape); void passTextureId(std::string brushTexture); }; @@ -120,7 +121,7 @@ namespace CSVWidget friend class CSVRender::TerrainTextureMode; public slots: - void setButtonIcon(int brushShape); + void setButtonIcon(CSVWidget::BrushShape brushShape); void updateBrushHistory (const std::string& mBrushTexture); void clicked (const QModelIndex& index); virtual void activate(); From e14c390a9b15135a4a0280c52ba893829c608605 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 15 Feb 2020 16:28:41 +0200 Subject: [PATCH 010/226] Fix crash --- apps/opencs/view/render/terrainshapemode.cpp | 2 +- apps/opencs/view/render/terraintexturemode.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 31292645d..cf380e791 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -1399,7 +1399,7 @@ void CSVRender::TerrainShapeMode::mouseMoveEvent (QMouseEvent *event) { WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask()); if (hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape); - if (!hit.hit && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->hide(); + if (!hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->hide(); } void CSVRender::TerrainShapeMode::setBrushSize(int brushSize) diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index c1bbb2825..c6b3ea134 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -725,7 +725,7 @@ void CSVRender::TerrainTextureMode::mouseMoveEvent (QMouseEvent *event) { WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask()); if (hit.hit && mBrushDraw) mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape); - if (!hit.hit) mBrushDraw->hide(); + if (!hit.hit && mBrushDraw) mBrushDraw->hide(); } From 8f625474fd6ce740cb8e75d2f655ef3a58503e0f Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 15 Feb 2020 16:46:03 +0200 Subject: [PATCH 011/226] Hotfix terraintexturemode circle brush radius calculations --- apps/opencs/view/render/terraintexturemode.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index c6b3ea134..95dcd4112 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -342,9 +342,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe std::string mBrushTextureInt = mBrushTexture.substr (hashlocation+1); int brushInt = stoi(mBrushTexture.substr (hashlocation+1))+1; // All indices are offset by +1 - int rf = mBrushSize / 2; - int r = mBrushSize / 2 + 1; - int distance = 0; + int r = static_cast(mBrushSize) / 2; if (mBrushShape == CSVWidget::BrushShape_Point) { @@ -433,7 +431,6 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe { for(int j = 0; j < landTextureSize; j++) { - if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r) { int distanceX(0); @@ -444,7 +441,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j; if (i_cell == cellX) distanceX = abs(i-xHitInCell); if (j_cell == cellY) distanceY = abs(j-yHitInCell); - distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); + float distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); + float rf = static_cast(mBrushSize) / 2; if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt; } else @@ -457,7 +455,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j; if (i_cell == cellX) distanceX = abs(i-xHitInCell); if (j_cell == cellY) distanceY = abs(j-yHitInCell); - distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); + float distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); + float rf = static_cast(mBrushSize) / 2; if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt; } } @@ -548,7 +547,8 @@ void CSVRender::TerrainTextureMode::selectTerrainTextures(const std::pair(mBrushSize) / 2; + if (std::round(coords.length()) < rf) { int x = i + texCoords.first; int y = j + texCoords.second; From da0add904b0cb99702696eecffe2ef6bcaaee8e2 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 5 Mar 2020 20:10:55 +0200 Subject: [PATCH 012/226] Fix and simplify brush outlines and coordinate calculations --- apps/opencs/model/world/cellcoordinates.cpp | 9 +- apps/opencs/model/world/cellcoordinates.hpp | 7 +- apps/opencs/view/render/brushdraw.cpp | 235 +++++++++++++------ apps/opencs/view/render/brushdraw.hpp | 3 +- apps/opencs/view/render/terrainselection.cpp | 35 ++- 5 files changed, 192 insertions(+), 97 deletions(-) diff --git a/apps/opencs/model/world/cellcoordinates.cpp b/apps/opencs/model/world/cellcoordinates.cpp index 9f98c7b4c..af8c26d70 100644 --- a/apps/opencs/model/world/cellcoordinates.cpp +++ b/apps/opencs/model/world/cellcoordinates.cpp @@ -91,9 +91,14 @@ std::pair CSMWorld::CellCoordinates::toVertexCoords(const osg::Vec3d& return std::make_pair(x, y); } -float CSMWorld::CellCoordinates::textureGlobalToWorldCoords(int textureGlobal) +float CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(int textureGlobal) { - return ESM::Land::REAL_SIZE * static_cast(textureGlobal) / ESM::Land::LAND_TEXTURE_SIZE; + return ESM::Land::REAL_SIZE * (static_cast(textureGlobal) + 0.25f) / ESM::Land::LAND_TEXTURE_SIZE; +} + +float CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(int textureGlobal) +{ + return ESM::Land::REAL_SIZE * (static_cast(textureGlobal) - 0.25f) / ESM::Land::LAND_TEXTURE_SIZE; } float CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(int vertexGlobal) diff --git a/apps/opencs/model/world/cellcoordinates.hpp b/apps/opencs/model/world/cellcoordinates.hpp index 77d76f6ef..9317c28b2 100644 --- a/apps/opencs/model/world/cellcoordinates.hpp +++ b/apps/opencs/model/world/cellcoordinates.hpp @@ -54,8 +54,11 @@ namespace CSMWorld ///Converts worldspace coordinates to global vertex selection. static std::pair toVertexCoords(const osg::Vec3d& worldPos); - ///Converts global texture coordinate to worldspace coordinate that is at the upper left corner of the selected texture. - static float textureGlobalToWorldCoords(int textureGlobal); + ///Converts global texture coordinate X to worldspace coordinate, offset by 0.25f. + static float textureGlobalXToWorldCoords(int textureGlobal); + + ///Converts global texture coordinate Y to worldspace coordinate, offset by 0.25f. + static float textureGlobalYToWorldCoords(int textureGlobal); ///Converts global vertex coordinate to worldspace coordinate static float vertexGlobalToWorldCoords(int vertexGlobal); diff --git a/apps/opencs/view/render/brushdraw.cpp b/apps/opencs/view/render/brushdraw.cpp index b963b09f5..0280d19ce 100644 --- a/apps/opencs/view/render/brushdraw.cpp +++ b/apps/opencs/view/render/brushdraw.cpp @@ -5,25 +5,23 @@ #include #include #include -#include -#include -#include #include +#include "../../model/world/cellcoordinates.hpp" #include "../widget/brushshapes.hpp" #include "mask.hpp" CSVRender::BrushDraw::BrushDraw(osg::ref_ptr parentNode, bool textureMode) : - mParentNode(parentNode) + mParentNode(parentNode), mTextureMode(textureMode) { mBrushDrawNode = new osg::Group(); mGeometry = new osg::Geometry(); mBrushDrawNode->addChild(mGeometry); mParentNode->addChild(mBrushDrawNode); - if (textureMode) mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_TEXTURE_SIZE / 2; - else mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE / 2; + if (mTextureMode) mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_TEXTURE_SIZE; + else mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE; } CSVRender::BrushDraw::~BrushDraw() @@ -37,7 +35,7 @@ float CSVRender::BrushDraw::getIntersectionHeight (const osg::Vec3d& point) osg::Vec3d start = point; osg::Vec3d end = point; start.z() = std::numeric_limits::max(); - end.z() = std::numeric_limits::min(); + end.z() = std::numeric_limits::lowest(); osg::Vec3d direction = end - start; // Get intersection @@ -66,9 +64,52 @@ float CSVRender::BrushDraw::getIntersectionHeight (const osg::Vec3d& point) return 0.0f; } -void CSVRender::BrushDraw::buildPointGeometry(const float& radius, const osg::Vec3d& point) +void CSVRender::BrushDraw::buildPointGeometry(const osg::Vec3d& point) { - // Not implemented + osg::ref_ptr geom = new osg::Geometry(); + osg::ref_ptr vertices (new osg::Vec3Array()); + osg::ref_ptr colors (new osg::Vec4Array()); + const float brushOutlineHeight (1.0f); + const float crossHeadSize (8.0f); + osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f); + + vertices->push_back(osg::Vec3d( + point.x() - crossHeadSize, + point.y() - crossHeadSize, + getIntersectionHeight(osg::Vec3d( + point.x() - crossHeadSize, + point.y() - crossHeadSize, + point.z()) ) + brushOutlineHeight)); + colors->push_back(lineColor); + vertices->push_back(osg::Vec3d( + point.x() + crossHeadSize, + point.y() + crossHeadSize, + getIntersectionHeight(osg::Vec3d( + point.x() + crossHeadSize, + point.y() + crossHeadSize, + point.z()) ) + brushOutlineHeight)); + colors->push_back(lineColor); + vertices->push_back(osg::Vec3d( + point.x() + crossHeadSize, + point.y() - crossHeadSize, + getIntersectionHeight(osg::Vec3d( + point.x() + crossHeadSize, + point.y() - crossHeadSize, + point.z()) ) + brushOutlineHeight)); + colors->push_back(lineColor); + vertices->push_back(osg::Vec3d( + point.x() - crossHeadSize, + point.y() + crossHeadSize, + getIntersectionHeight(osg::Vec3d( + point.x() - crossHeadSize, + point.y() + crossHeadSize, + point.z()) ) + brushOutlineHeight)); + colors->push_back(lineColor); + + geom->setVertexArray(vertices); + geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX); + geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 4)); + mGeometry = geom; } void CSVRender::BrushDraw::buildSquareGeometry(const float& radius, const osg::Vec3d& point) @@ -76,46 +117,95 @@ void CSVRender::BrushDraw::buildSquareGeometry(const float& radius, const osg::V osg::ref_ptr geom = new osg::Geometry(); osg::ref_ptr vertices (new osg::Vec3Array()); osg::ref_ptr colors (new osg::Vec4Array()); - std::vector corners; - const float brushOutLineHeight (200.0f); - corners.push_back(osg::Vec3d(point.x() - radius, point.y() - radius, point.z())); - corners.push_back(osg::Vec3d(point.x() + radius, point.y() - radius, point.z())); - corners.push_back(osg::Vec3d(point.x() + radius, point.y() + radius, point.z())); - corners.push_back(osg::Vec3d(point.x() - radius, point.y() + radius, point.z())); - corners.push_back(osg::Vec3d(point.x() - radius, point.y() - radius, point.z())); + const float brushOutlineHeight (1.0f); + float diameter = radius * 2; + int resolution = (diameter / mLandSizeFactor) * 2; //half a vertex resolution + float resAdjustedLandSizeFactor = mLandSizeFactor / 2; + osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f); - for (const auto& point : corners) + for (int i = 0; i < resolution; i++) { - vertices->push_back(osg::Vec3d( - point.x(), - point.y(), + int step = i * resAdjustedLandSizeFactor; + int step2 = (i + 1) * resAdjustedLandSizeFactor; + + osg::Vec3d upHorizontalLinePoint1( + point.x() - radius + step, + point.y() - radius, getIntersectionHeight(osg::Vec3d( - point.x(), - point.y(), - point.z()) ))); - colors->push_back(osg::Vec4f( - 50.0f, - 50.0f, - 50.0f, - 100.0f)); - vertices->push_back(osg::Vec3d( - point.x(), - point.y(), + point.x() - radius + step, + point.y() - radius, + point.z())) + brushOutlineHeight); + osg::Vec3d upHorizontalLinePoint2( + point.x() - radius + step2, + point.y() - radius, + getIntersectionHeight(osg::Vec3d( + point.x() - radius + step2, + point.y() - radius, + point.z())) + brushOutlineHeight); + osg::Vec3d upVerticalLinePoint1( + point.x() - radius, + point.y() - radius + step, getIntersectionHeight(osg::Vec3d( - point.x(), - point.y(), - point.z())) + brushOutLineHeight)); - colors->push_back(osg::Vec4f( - 50.0f, - 50.0f, - 50.0f, - 100.0f)); + point.x() - radius, + point.y() - radius + step, + point.z())) + brushOutlineHeight); + osg::Vec3d upVerticalLinePoint2( + point.x() - radius, + point.y() - radius + step2, + getIntersectionHeight(osg::Vec3d( + point.x() - radius, + point.y() - radius + step2, + point.z())) + brushOutlineHeight); + osg::Vec3d downHorizontalLinePoint1( + point.x() + radius - step, + point.y() + radius, + getIntersectionHeight(osg::Vec3d( + point.x() + radius - step, + point.y() + radius, + point.z())) + brushOutlineHeight); + osg::Vec3d downHorizontalLinePoint2( + point.x() + radius - step2, + point.y() + radius, + getIntersectionHeight(osg::Vec3d( + point.x() + radius - step2, + point.y() + radius, + point.z())) + brushOutlineHeight); + osg::Vec3d downVerticalLinePoint1( + point.x() + radius, + point.y() + radius - step, + getIntersectionHeight(osg::Vec3d( + point.x() + radius, + point.y() + radius - step, + point.z())) + brushOutlineHeight); + osg::Vec3d downVerticalLinePoint2( + point.x() + radius, + point.y() + radius - step2, + getIntersectionHeight(osg::Vec3d( + point.x() + radius, + point.y() + radius - step2, + point.z())) + brushOutlineHeight); + vertices->push_back(upHorizontalLinePoint1); + colors->push_back(lineColor); + vertices->push_back(upHorizontalLinePoint2); + colors->push_back(lineColor); + vertices->push_back(upVerticalLinePoint1); + colors->push_back(lineColor); + vertices->push_back(upVerticalLinePoint2); + colors->push_back(lineColor); + vertices->push_back(downHorizontalLinePoint1); + colors->push_back(lineColor); + vertices->push_back(downHorizontalLinePoint2); + colors->push_back(lineColor); + vertices->push_back(downVerticalLinePoint1); + colors->push_back(lineColor); + vertices->push_back(downVerticalLinePoint2); + colors->push_back(lineColor); } geom->setVertexArray(vertices); geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX); - geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP, 0, (10 + 2) - 2)); + geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, resolution * 8)); mGeometry = geom; } @@ -126,7 +216,8 @@ void CSVRender::BrushDraw::buildCircleGeometry(const float& radius, const osg::V osg::ref_ptr colors (new osg::Vec4Array()); const int amountOfPoints = (osg::PI * 2.0f) * radius / 20; const float step ((osg::PI * 2.0f) / static_cast(amountOfPoints)); - const float brushOutLineHeight (200.0f); + const float brushOutlineHeight (1.0f); + osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f); for (int i = 0; i < amountOfPoints + 2; i++) { @@ -137,12 +228,8 @@ void CSVRender::BrushDraw::buildCircleGeometry(const float& radius, const osg::V getIntersectionHeight(osg::Vec3d( point.x() + radius * cosf(angle), point.y() + radius * sinf(angle), - point.z()) ))); - colors->push_back(osg::Vec4f( - 50.0f, - 50.0f, - 50.0f, - 100.0f)); + point.z()) ) + brushOutlineHeight)); + colors->push_back(lineColor); angle = static_cast(i + 1) * step; vertices->push_back(osg::Vec3d( point.x() + radius * cosf(angle), @@ -150,17 +237,13 @@ void CSVRender::BrushDraw::buildCircleGeometry(const float& radius, const osg::V getIntersectionHeight(osg::Vec3d( point.x() + radius * cosf(angle), point.y() + radius * sinf(angle), - point.z()) ) + brushOutLineHeight)); - colors->push_back(osg::Vec4f( - 50.0f, - 50.0f, - 50.0f, - 100.0f)); + point.z()) ) + brushOutlineHeight)); + colors->push_back(lineColor); } geom->setVertexArray(vertices); geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX); - geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP, 0, (amountOfPoints + 2) * 2 - 2)); + geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, amountOfPoints * 2)); mGeometry = geom; } @@ -173,39 +256,45 @@ void CSVRender::BrushDraw::update(osg::Vec3d point, int brushSize, CSVWidget::Br { if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); mBrushDrawNode->setNodeMask (Mask_EditModeCursor); - float radius = static_cast(brushSize * mLandSizeFactor); + float radius = (mLandSizeFactor * brushSize) / 2; + osg::Vec3d snapToGridPoint = point; + if (mTextureMode) + { + std::pair snapToGridXY = CSMWorld::CellCoordinates::toTextureCoords(point); + float offsetToMiddle = mLandSizeFactor * 0.5f; + snapToGridPoint = osg::Vec3d( + CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(snapToGridXY.first) + offsetToMiddle, + CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(snapToGridXY.second) + offsetToMiddle, + point.z()); + } + else + { + std::pair snapToGridXY = CSMWorld::CellCoordinates::toVertexCoords(point); + snapToGridPoint = osg::Vec3d( + CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(snapToGridXY.first), + CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(snapToGridXY.second), + point.z()); + } + switch (toolShape) { case (CSVWidget::BrushShape_Point) : - buildSquareGeometry(1, point); - //buildPointGeometry(radius, point); + buildPointGeometry(snapToGridPoint); break; case (CSVWidget::BrushShape_Square) : - buildSquareGeometry(radius, point); + buildSquareGeometry(radius, snapToGridPoint); break; case (CSVWidget::BrushShape_Circle) : - buildCircleGeometry(radius, point); + buildCircleGeometry(radius, snapToGridPoint); break; case (CSVWidget::BrushShape_Custom) : - buildSquareGeometry(1, point); + buildSquareGeometry(1, snapToGridPoint); //buildCustomGeometry break; } - osg::BlendFunc* blendFunc = new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA); - mGeometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc); - - mGeometry->getOrCreateStateSet()->setMode( GL_CULL_FACE, osg::StateAttribute::OFF ); - - osg::ref_ptr material = new osg::Material; - material->setColorMode(osg::Material::AMBIENT); - material->setAmbient (osg::Material::FRONT_AND_BACK, osg::Vec4(0.3, 0.3, 0.3, 1)); - material->setAlpha(osg::Material::FRONT_AND_BACK, 0.5); - - mGeometry->getOrCreateStateSet()->setAttributeAndModes(material.get(), osg::StateAttribute::ON); - mGeometry->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); - mGeometry->getOrCreateStateSet()->setRenderingHint (osg::StateSet::TRANSPARENT_BIN); + mGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); mBrushDrawNode->addChild(mGeometry); } diff --git a/apps/opencs/view/render/brushdraw.hpp b/apps/opencs/view/render/brushdraw.hpp index 6ecfccde7..0551631cd 100644 --- a/apps/opencs/view/render/brushdraw.hpp +++ b/apps/opencs/view/render/brushdraw.hpp @@ -19,7 +19,7 @@ namespace CSVRender void hide(); private: - void buildPointGeometry(const float& radius, const osg::Vec3d& point); + void buildPointGeometry(const osg::Vec3d& point); void buildSquareGeometry(const float& radius, const osg::Vec3d& point); void buildCircleGeometry(const float& radius, const osg::Vec3d& point); void buildCustomGeometry(const float& radius, const osg::Vec3d& point); @@ -28,6 +28,7 @@ namespace CSVRender osg::ref_ptr mParentNode; osg::ref_ptr mBrushDrawNode; osg::ref_ptr mGeometry; + bool mTextureMode; float mLandSizeFactor; }; } diff --git a/apps/opencs/view/render/terrainselection.cpp b/apps/opencs/view/render/terrainselection.cpp index e16a69048..4e209af57 100644 --- a/apps/opencs/view/render/terrainselection.cpp +++ b/apps/opencs/view/render/terrainselection.cpp @@ -171,9 +171,6 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptrpush_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y + 1) - nudgeOffset, calculateLandHeight(x1+(i-1), y2)+2)); - vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y + 1) - nudgeOffset, calculateLandHeight(x1+i, y2)+2)); + float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); + float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); + vertices->push_back(osg::Vec3f(drawPreviousX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y + 1), calculateLandHeight(x1+(i-1), y2)+2)); + vertices->push_back(osg::Vec3f(drawCurrentX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y + 1), calculateLandHeight(x1+i, y2)+2)); } } @@ -208,10 +205,10 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptrpush_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) - nudgeOffset, calculateLandHeight(x1+(i-1), y1)+2)); - vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) - nudgeOffset, calculateLandHeight(x1+i, y1)+2)); + float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + (i - 1) *(ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); + float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); + vertices->push_back(osg::Vec3f(drawPreviousX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y), calculateLandHeight(x1+(i-1), y1)+2)); + vertices->push_back(osg::Vec3f(drawCurrentX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y), calculateLandHeight(x1+i, y1)+2)); } } @@ -220,10 +217,10 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptrpush_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x + 1) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x2, y1+(i-1))+2)); - vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x + 1) + nudgeOffset, drawCurrentY - nudgeOffset, calculateLandHeight(x2, y1+i)+2)); + float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); + float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); + vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x + 1), drawPreviousY, calculateLandHeight(x2, y1+(i-1))+2)); + vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x + 1), drawCurrentY, calculateLandHeight(x2, y1+i)+2)); } } @@ -232,10 +229,10 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptrpush_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x1, y1+(i-1))+2)); - vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + nudgeOffset, drawCurrentY - nudgeOffset, calculateLandHeight(x1, y1+i)+2)); + float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); + float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); + vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x), drawPreviousY, calculateLandHeight(x1, y1+(i-1))+2)); + vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x), drawCurrentY, calculateLandHeight(x1, y1+i)+2)); } } } From 18cdd3bd7ca9b6dc902944cd0364f84abe06f55d Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 6 Mar 2020 12:10:14 +0200 Subject: [PATCH 013/226] rebase-related fixes --- apps/opencs/view/render/brushdraw.cpp | 8 ++++---- apps/opencs/view/render/terraintexturemode.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/render/brushdraw.cpp b/apps/opencs/view/render/brushdraw.cpp index 0280d19ce..d33ef3367 100644 --- a/apps/opencs/view/render/brushdraw.cpp +++ b/apps/opencs/view/render/brushdraw.cpp @@ -8,11 +8,11 @@ #include +#include + #include "../../model/world/cellcoordinates.hpp" #include "../widget/brushshapes.hpp" -#include "mask.hpp" - CSVRender::BrushDraw::BrushDraw(osg::ref_ptr parentNode, bool textureMode) : mParentNode(parentNode), mTextureMode(textureMode) { @@ -44,7 +44,7 @@ float CSVRender::BrushDraw::getIntersectionHeight (const osg::Vec3d& point) intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); osgUtil::IntersectionVisitor visitor(intersector); - visitor.setTraversalMask(Mask_Terrain); + visitor.setTraversalMask(SceneUtil::Mask_Terrain); mParentNode->accept(visitor); @@ -255,7 +255,7 @@ void CSVRender::BrushDraw::buildCustomGeometry(const float& radius, const osg::V void CSVRender::BrushDraw::update(osg::Vec3d point, int brushSize, CSVWidget::BrushShape toolShape) { if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); - mBrushDrawNode->setNodeMask (Mask_EditModeCursor); + mBrushDrawNode->setNodeMask (SceneUtil::Mask_GUI); float radius = (mLandSizeFactor * brushSize) / 2; osg::Vec3d snapToGridPoint = point; if (mTextureMode) diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index 95dcd4112..386ff2f90 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -44,7 +44,7 @@ CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceW : EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference, "Terrain texture editing", parent), mBrushTexture("L0#0"), mBrushSize(1), - mBrushShape(0), + mBrushShape(CSVWidget::BrushShape_Point), mTextureBrushScenetool(nullptr), mDragMode(InteractionType_None), mParentNode(parentNode), From c8c7501d974204cad4cfdfb3e02be44eee8fe59b Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 26 Mar 2020 13:28:51 +0200 Subject: [PATCH 014/226] Add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85fffdd1f..dec3aa347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -264,6 +264,7 @@ Feature #5170: Editor: Land shape editing, land selection Feature #5172: Editor: Delete instances/references with keypress in scene window Feature #5193: Weapon 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 Feature #5274: Editor: Keyboard shortcut to drop objects to ground/obstacle in scene view From ec2ff2a9b0150a887dc80b91754701bf31806e27 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 26 Mar 2020 14:47:40 +0200 Subject: [PATCH 015/226] Fix if oneliners --- apps/opencs/view/render/brushdraw.cpp | 9 ++++++--- apps/opencs/view/render/terrainshapemode.cpp | 6 ++++-- apps/opencs/view/render/terraintexturemode.cpp | 6 ++++-- apps/opencs/view/widget/scenetooltexturebrush.cpp | 12 ++++++++---- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/apps/opencs/view/render/brushdraw.cpp b/apps/opencs/view/render/brushdraw.cpp index d33ef3367..478ff7a9d 100644 --- a/apps/opencs/view/render/brushdraw.cpp +++ b/apps/opencs/view/render/brushdraw.cpp @@ -20,7 +20,8 @@ CSVRender::BrushDraw::BrushDraw(osg::ref_ptr parentNode, bool textur mGeometry = new osg::Geometry(); mBrushDrawNode->addChild(mGeometry); mParentNode->addChild(mBrushDrawNode); - if (mTextureMode) mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_TEXTURE_SIZE; + if (mTextureMode) + mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_TEXTURE_SIZE; else mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE; } @@ -254,7 +255,8 @@ void CSVRender::BrushDraw::buildCustomGeometry(const float& radius, const osg::V void CSVRender::BrushDraw::update(osg::Vec3d point, int brushSize, CSVWidget::BrushShape toolShape) { - if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); + if (mBrushDrawNode->containsNode(mGeometry)) + mBrushDrawNode->removeChild(mGeometry); mBrushDrawNode->setNodeMask (SceneUtil::Mask_GUI); float radius = (mLandSizeFactor * brushSize) / 2; osg::Vec3d snapToGridPoint = point; @@ -301,5 +303,6 @@ void CSVRender::BrushDraw::update(osg::Vec3d point, int brushSize, CSVWidget::Br void CSVRender::BrushDraw::hide() { - if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); + if (mBrushDrawNode->containsNode(mGeometry)) + mBrushDrawNode->removeChild(mGeometry); } diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index cf380e791..ae8e6e89b 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -1398,8 +1398,10 @@ void CSVRender::TerrainShapeMode::dragMoveEvent (QDragMoveEvent *event) void CSVRender::TerrainShapeMode::mouseMoveEvent (QMouseEvent *event) { WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask()); - if (hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape); - if (!hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->hide(); + if (hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) + mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape); + if (!hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) + mBrushDraw->hide(); } void CSVRender::TerrainShapeMode::setBrushSize(int brushSize) diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index 386ff2f90..04189ce8c 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -724,8 +724,10 @@ void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) void CSVRender::TerrainTextureMode::mouseMoveEvent (QMouseEvent *event) { WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask()); - if (hit.hit && mBrushDraw) mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape); - if (!hit.hit && mBrushDraw) mBrushDraw->hide(); + if (hit.hit && mBrushDraw) + mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape); + if (!hit.hit && mBrushDraw) + mBrushDraw->hide(); } diff --git a/apps/opencs/view/widget/scenetooltexturebrush.cpp b/apps/opencs/view/widget/scenetooltexturebrush.cpp index 0a1ed6683..35937f1a6 100644 --- a/apps/opencs/view/widget/scenetooltexturebrush.cpp +++ b/apps/opencs/view/widget/scenetooltexturebrush.cpp @@ -204,10 +204,14 @@ void CSVWidget::TextureBrushWindow::setBrushSize(int brushSize) void CSVWidget::TextureBrushWindow::setBrushShape() { - if(mButtonPoint->isChecked()) mBrushShape = CSVWidget::BrushShape_Point; - if(mButtonSquare->isChecked()) mBrushShape = CSVWidget::BrushShape_Square; - if(mButtonCircle->isChecked()) mBrushShape = CSVWidget::BrushShape_Circle; - if(mButtonCustom->isChecked()) mBrushShape = CSVWidget::BrushShape_Custom; + if (mButtonPoint->isChecked()) + mBrushShape = CSVWidget::BrushShape_Point; + if (mButtonSquare->isChecked()) + mBrushShape = CSVWidget::BrushShape_Square; + if (mButtonCircle->isChecked()) + mBrushShape = CSVWidget::BrushShape_Circle; + if (mButtonCustom->isChecked()) + mBrushShape = CSVWidget::BrushShape_Custom; emit passBrushShape(mBrushShape); } From 66fba7cc514ed83f6cc113fad37615b44620af1c Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 26 Mar 2020 18:06:33 +0200 Subject: [PATCH 016/226] Remove unneeded constructors, only initialize in cpp, minor fixes --- apps/opencs/view/render/brushdraw.cpp | 8 ++++---- apps/opencs/view/render/terrainshapemode.cpp | 6 ------ apps/opencs/view/render/terrainshapemode.hpp | 1 - apps/opencs/view/render/terraintexturemode.cpp | 6 ------ apps/opencs/view/render/terraintexturemode.hpp | 13 ++++++------- 5 files changed, 10 insertions(+), 24 deletions(-) diff --git a/apps/opencs/view/render/brushdraw.cpp b/apps/opencs/view/render/brushdraw.cpp index 478ff7a9d..9a648336e 100644 --- a/apps/opencs/view/render/brushdraw.cpp +++ b/apps/opencs/view/render/brushdraw.cpp @@ -67,7 +67,7 @@ float CSVRender::BrushDraw::getIntersectionHeight (const osg::Vec3d& point) void CSVRender::BrushDraw::buildPointGeometry(const osg::Vec3d& point) { - osg::ref_ptr geom = new osg::Geometry(); + osg::ref_ptr geom (new osg::Geometry()); osg::ref_ptr vertices (new osg::Vec3Array()); osg::ref_ptr colors (new osg::Vec4Array()); const float brushOutlineHeight (1.0f); @@ -115,7 +115,7 @@ void CSVRender::BrushDraw::buildPointGeometry(const osg::Vec3d& point) void CSVRender::BrushDraw::buildSquareGeometry(const float& radius, const osg::Vec3d& point) { - osg::ref_ptr geom = new osg::Geometry(); + osg::ref_ptr geom (new osg::Geometry()); osg::ref_ptr vertices (new osg::Vec3Array()); osg::ref_ptr colors (new osg::Vec4Array()); @@ -212,7 +212,7 @@ void CSVRender::BrushDraw::buildSquareGeometry(const float& radius, const osg::V void CSVRender::BrushDraw::buildCircleGeometry(const float& radius, const osg::Vec3d& point) { - osg::ref_ptr geom = new osg::Geometry(); + osg::ref_ptr geom (new osg::Geometry()); osg::ref_ptr vertices (new osg::Vec3Array()); osg::ref_ptr colors (new osg::Vec4Array()); const int amountOfPoints = (osg::PI * 2.0f) * radius / 20; @@ -222,7 +222,7 @@ void CSVRender::BrushDraw::buildCircleGeometry(const float& radius, const osg::V for (int i = 0; i < amountOfPoints + 2; i++) { - float angle (static_cast(i) * step); + float angle (i * step); vertices->push_back(osg::Vec3d( point.x() + radius * cosf(angle), point.y() + radius * sinf(angle), diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index ae8e6e89b..6df5ee836 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -50,12 +50,6 @@ CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidge { } -CSVRender::TerrainShapeMode::~TerrainShapeMode () -{ - if (mBrushDraw) - mBrushDraw.reset(); -} - void CSVRender::TerrainShapeMode::activate(CSVWidget::SceneToolbar* toolbar) { if (!mTerrainShapeSelection) diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index fb80e79af..d0fec764f 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -58,7 +58,6 @@ namespace CSVRender /// Editmode for terrain shape grid TerrainShapeMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); - ~TerrainShapeMode(); void primaryOpenPressed (const WorldspaceHitResult& hit) final; diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index 04189ce8c..d4656b578 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -52,12 +52,6 @@ CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceW { } -CSVRender::TerrainTextureMode::~TerrainTextureMode () -{ - if (mBrushDraw) - mBrushDraw.reset(); -} - void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar) { if(!mTextureBrushScenetool) diff --git a/apps/opencs/view/render/terraintexturemode.hpp b/apps/opencs/view/render/terraintexturemode.hpp index cc77bb331..0d8c4a94a 100644 --- a/apps/opencs/view/render/terraintexturemode.hpp +++ b/apps/opencs/view/render/terraintexturemode.hpp @@ -52,7 +52,6 @@ namespace CSVRender /// \brief Editmode for terrain texture grid TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); - ~TerrainTextureMode(); void primaryOpenPressed (const WorldspaceHitResult& hit) final; @@ -107,15 +106,15 @@ namespace CSVRender bool allowLandTextureEditing(std::string textureFileName); std::string mCellId; - std::string mBrushTexture = "L0#0"; - int mBrushSize = 1; - CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point; + std::string mBrushTexture; + int mBrushSize; + CSVWidget::BrushShape mBrushShape; std::unique_ptr mBrushDraw; std::vector> mCustomBrushShape; - CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool = nullptr; - int mDragMode = InteractionType_None; + CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool; + int mDragMode; osg::Group* mParentNode; - bool mIsEditing = false; + bool mIsEditing; std::unique_ptr mTerrainTextureSelection; const int cellSize {ESM::Land::REAL_SIZE}; From deac2abc0f5f164866245377210a9d9edb4d151b Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 27 Mar 2020 08:12:06 +0100 Subject: [PATCH 017/226] add launchable type; rename files to be conform to freedesktop.org and debian standards --- CMakeLists.txt | 12 ++++++------ files/openmw.appdata.xml | 5 +++-- files/{openmw-cs.desktop => org.openmw.cs.desktop} | 0 .../{openmw.desktop => org.openmw.launcher.desktop} | 0 4 files changed, 9 insertions(+), 8 deletions(-) rename files/{openmw-cs.desktop => org.openmw.cs.desktop} (100%) rename files/{openmw.desktop => org.openmw.launcher.desktop} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 58376a7ce..af3d9d1ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -401,12 +401,12 @@ configure_resource_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb_205.txt "${OpenMW_BINARY_DIR}" "gamecontrollerdb_205.txt") if (NOT WIN32 AND NOT APPLE) - configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop - "${OpenMW_BINARY_DIR}/openmw.desktop") + configure_file(${OpenMW_SOURCE_DIR}/files/org.openmw.launcher.desktop + "${OpenMW_BINARY_DIR}/org.openmw.launcher.desktop") configure_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml "${OpenMW_BINARY_DIR}/openmw.appdata.xml") - configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.desktop - "${OpenMW_BINARY_DIR}/openmw-cs.desktop") + configure_file(${OpenMW_SOURCE_DIR}/files/org.openmw.cs.desktop + "${OpenMW_BINARY_DIR}/org.openmw.cs.desktop") endif() # CXX Compiler settings @@ -480,11 +480,11 @@ IF(NOT WIN32 AND NOT APPLE) INSTALL(FILES "files/mygui/DejaVu Font License.txt" DESTINATION "${LICDIR}" ) # Install icon and desktop file - INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw") + INSTALL(FILES "${OpenMW_BINARY_DIR}/org.openmw.launcher.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw") INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.appdata.xml" DESTINATION "${DATAROOTDIR}/metainfo" COMPONENT "openmw") IF(BUILD_OPENCS) - INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "opencs") + INSTALL(FILES "${OpenMW_BINARY_DIR}/org.openmw.cs.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "opencs") INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/openmw-cs.png" DESTINATION "${ICONDIR}" COMPONENT "opencs") ENDIF(BUILD_OPENCS) diff --git a/files/openmw.appdata.xml b/files/openmw.appdata.xml index 8271ab6eb..edbeb1a27 100644 --- a/files/openmw.appdata.xml +++ b/files/openmw.appdata.xml @@ -1,10 +1,10 @@ - org.openmw.desktop + org.openmw.launcher.desktop CC0-1.0 GPL-3.0 and MIT OpenMW @@ -20,6 +20,7 @@ Copyright 2018 Bret Curtis You will still need the original game data to play OpenMW.

+ org.openmw.launcher.desktop https://wiki.openmw.org/images/b/b2/Openmw_0.11.1_launcher_1.png diff --git a/files/openmw-cs.desktop b/files/org.openmw.cs.desktop similarity index 100% rename from files/openmw-cs.desktop rename to files/org.openmw.cs.desktop diff --git a/files/openmw.desktop b/files/org.openmw.launcher.desktop similarity index 100% rename from files/openmw.desktop rename to files/org.openmw.launcher.desktop From e07fa37fa35a2d25dc9b0ddd4eefab676e772442 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 29 Mar 2020 22:29:22 +0200 Subject: [PATCH 018/226] ignore final whitespace/comments --- CHANGELOG.md | 1 + components/compiler/scanner.cpp | 27 ++++++++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85fffdd1f..0b559c23f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -207,6 +207,7 @@ 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 #5345: Dopey Necromancy does not work due to a missing quote Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index b06d683f7..83686e4c1 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -368,15 +368,28 @@ namespace Compiler { if (mIgnoreNewline) mErrorHandler.warning ("string contains newline character, make sure that it is intended", mLoc); - else if (name.size() == 1 || (name.size() == 2 && name[1] == '\r')) - { - name.clear(); - mLoc.mLiteral.clear(); - mErrorHandler.warning ("unterminated empty string", mLoc); - break; - } else { + bool allWhitespace = true; + for (size_t i = 1; i < name.size(); i++) + { + //ignore comments + if (name[i] == ';') + break; + else if (name[i] != '\t' && name[i] != ' ' && name[i] != '\r') + { + allWhitespace = false; + break; + } + } + if (allWhitespace) + { + name.clear(); + mLoc.mLiteral.clear(); + mErrorHandler.warning ("unterminated empty string", mLoc); + return true; + } + error = true; mErrorHandler.error ("incomplete string or name", mLoc); break; From 15a95add083c3c76f6c02be864bfc950aa8dc5c5 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 29 Mar 2020 22:40:22 +0200 Subject: [PATCH 019/226] remove false positive --- components/compiler/extensions0.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 24578b238..f23d2d86e 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -33,7 +33,7 @@ namespace Compiler { extensions.registerInstruction ("aiactivate", "c/l", opcodeAIActivate, opcodeAIActivateExplicit); - extensions.registerInstruction ("aitravel", "fff/zx", opcodeAiTravel, + extensions.registerInstruction ("aitravel", "fff/lx", opcodeAiTravel, opcodeAiTravelExplicit); extensions.registerInstruction ("aiescort", "cffff/l", opcodeAiEscort, opcodeAiEscortExplicit); From 16297918856fed409e17414a0891503733db782d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 30 Mar 2020 21:23:41 +0300 Subject: [PATCH 020/226] Port wareya's actor tracer consistency fixes --- apps/openmw/mwphysics/trace.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwphysics/trace.cpp b/apps/openmw/mwphysics/trace.cpp index 57b0a8368..13bc7f798 100644 --- a/apps/openmw/mwphysics/trace.cpp +++ b/apps/openmw/mwphysics/trace.cpp @@ -14,9 +14,9 @@ namespace MWPhysics class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { public: - ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &up, btScalar minSlopeDot) + ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), - mMe(me), mUp(up), mMinSlopeDot(minSlopeDot) + mMe(me), mMotion(motion), mMinCollisionDot(minCollisionDot) { } @@ -34,8 +34,9 @@ public: hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; } - btScalar dotUp = mUp.dot(hitNormalWorld); - if(dotUp < mMinSlopeDot) + // dot product of the motion vector against the collision contact normal + btScalar dotCollision = mMotion.dot(hitNormalWorld); + if(dotCollision <= mMinCollisionDot) return btScalar(1); return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); @@ -43,8 +44,8 @@ public: protected: const btCollisionObject *mMe; - const btVector3 mUp; - const btScalar mMinSlopeDot; + const btVector3 mMotion; + const btScalar mMinCollisionDot; }; @@ -59,22 +60,21 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star from.setOrigin(btstart); to.setOrigin(btend); - ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); + const btVector3 motion = btstart-btend; + ClosestNotMeConvexResultCallback newTraceCallback(actor, motion, btScalar(0.0)); // Inherit the actor's collision group and mask newTraceCallback.m_collisionFilterGroup = actor->getBroadphaseHandle()->m_collisionFilterGroup; newTraceCallback.m_collisionFilterMask = actor->getBroadphaseHandle()->m_collisionFilterMask; const btCollisionShape *shape = actor->getCollisionShape(); assert(shape->isConvex()); - world->convexSweepTest(static_cast(shape), - from, to, newTraceCallback); + world->convexSweepTest(static_cast(shape), from, to, newTraceCallback); // Copy the hit data over to our trace results struct: if(newTraceCallback.hasHit()) { - const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; mFraction = newTraceCallback.m_closestHitFraction; - mPlaneNormal = osg::Vec3f(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); + mPlaneNormal = Misc::Convert::toOsg(newTraceCallback.m_hitNormalWorld); mEndPos = (end-start)*mFraction + start; mHitPoint = Misc::Convert::toOsg(newTraceCallback.m_hitPointWorld); mHitObject = newTraceCallback.m_hitCollisionObject; @@ -91,14 +91,15 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star void ActorTracer::findGround(const Actor* actor, const osg::Vec3f& start, const osg::Vec3f& end, const btCollisionWorld* world) { - const btVector3 btstart(start.x(), start.y(), start.z()); - const btVector3 btend(end.x(), end.y(), end.z()); + const btVector3 btstart = Misc::Convert::toBullet(start); + const btVector3 btend = Misc::Convert::toBullet(end); const btTransform &trans = actor->getCollisionObject()->getWorldTransform(); btTransform from(trans.getBasis(), btstart); btTransform to(trans.getBasis(), btend); - ClosestNotMeConvexResultCallback newTraceCallback(actor->getCollisionObject(), btstart-btend, btScalar(0.0)); + const btVector3 motion = btstart-btend; + ClosestNotMeConvexResultCallback newTraceCallback(actor->getCollisionObject(), motion, btScalar(0.0)); // Inherit the actor's collision group and mask newTraceCallback.m_collisionFilterGroup = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterGroup; newTraceCallback.m_collisionFilterMask = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterMask; @@ -107,9 +108,8 @@ void ActorTracer::findGround(const Actor* actor, const osg::Vec3f& start, const world->convexSweepTest(actor->getConvexShape(), from, to, newTraceCallback); if(newTraceCallback.hasHit()) { - const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; mFraction = newTraceCallback.m_closestHitFraction; - mPlaneNormal = osg::Vec3f(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); + mPlaneNormal = Misc::Convert::toOsg(newTraceCallback.m_hitNormalWorld); mEndPos = (end-start)*mFraction + start; } else From ce588fb39cb92ca6fab470b4eecaab55d30e341b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 30 Mar 2020 21:39:10 +0300 Subject: [PATCH 021/226] Separate DeepestNotMeContactTestResultCallback --- apps/openmw/CMakeLists.txt | 3 +- .../deepestnotmecontacttestresultcallback.cpp | 45 +++++++++++++++++ .../deepestnotmecontacttestresultcallback.hpp | 32 ++++++++++++ apps/openmw/mwphysics/physicssystem.cpp | 49 +------------------ 4 files changed, 80 insertions(+), 49 deletions(-) create mode 100644 apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp create mode 100644 apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2bc85e062..67eb0cd27 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -70,7 +70,8 @@ add_openmw_dir (mwworld ) add_openmw_dir (mwphysics - physicssystem trace collisiontype actor convert object heightfield + physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback + contacttestresultcallback deepestnotmecontacttestresultcallback ) add_openmw_dir (mwclass diff --git a/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp new file mode 100644 index 000000000..be94128c1 --- /dev/null +++ b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp @@ -0,0 +1,45 @@ +#include "deepestnotmecontacttestresultcallback.hpp" + +#include + +#include "../mwworld/class.hpp" + +#include "ptrholder.hpp" + +namespace MWPhysics +{ + + DeepestNotMeContactTestResultCallback::DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3 &origin) + : mMe(me), mTargets(targets), mOrigin(origin), mLeastDistSqr(std::numeric_limits::max()) + { + } + + btScalar DeepestNotMeContactTestResultCallback::addSingleResult(btManifoldPoint& cp, + const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, + const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) + { + const btCollisionObject* collisionObject = col1Wrap->m_collisionObject; + if (collisionObject != mMe) + { + if (!mTargets.empty()) + { + if ((std::find(mTargets.begin(), mTargets.end(), collisionObject) == mTargets.end())) + { + PtrHolder* holder = static_cast(collisionObject->getUserPointer()); + if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor()) + return 0.f; + } + } + + btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA()); + if(!mObject || distsqr < mLeastDistSqr) + { + mObject = collisionObject; + mLeastDistSqr = distsqr; + mContactPoint = cp.getPositionWorldOnA(); + } + } + + return 0.f; + } +} diff --git a/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp new file mode 100644 index 000000000..2fcef66db --- /dev/null +++ b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp @@ -0,0 +1,32 @@ +#ifndef OPENMW_MWPHYSICS_DEEPESTNOTMECONTACTTESTRESULTCALLBACK_H +#define OPENMW_MWPHYSICS_DEEPESTNOTMECONTACTTESTRESULTCALLBACK_H + +#include + +#include +#include + +namespace MWPhysics +{ + class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback + { + const btCollisionObject* mMe; + const std::vector mTargets; + + // Store the real origin, since the shape's origin is its center + btVector3 mOrigin; + + public: + const btCollisionObject *mObject{nullptr}; + btVector3 mContactPoint{0,0,0}; + btScalar mLeastDistSqr; + + DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3 &origin); + + virtual btScalar addSingleResult(btManifoldPoint& cp, + const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, + const btCollisionObjectWrapper* col1Wrap,int partId1,int index1); + }; +} + +#endif diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 69177d95d..0ff433aa4 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -46,6 +46,7 @@ #include "object.hpp" #include "heightfield.hpp" #include "hasspherecollisioncallback.hpp" +#include "deepestnotmecontacttestresultcallback.hpp" namespace MWPhysics { @@ -646,54 +647,6 @@ namespace MWPhysics return true; } - class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback - { - const btCollisionObject* mMe; - const std::vector mTargets; - - // Store the real origin, since the shape's origin is its center - btVector3 mOrigin; - - public: - const btCollisionObject *mObject; - btVector3 mContactPoint; - btScalar mLeastDistSqr; - - DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3 &origin) - : mMe(me), mTargets(targets), mOrigin(origin), mObject(nullptr), mContactPoint(0,0,0), - mLeastDistSqr(std::numeric_limits::max()) - { } - - virtual btScalar addSingleResult(btManifoldPoint& cp, - const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, - const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) - { - const btCollisionObject* collisionObject = col1Wrap->m_collisionObject; - if (collisionObject != mMe) - { - if (!mTargets.empty()) - { - if ((std::find(mTargets.begin(), mTargets.end(), collisionObject) == mTargets.end())) - { - PtrHolder* holder = static_cast(collisionObject->getUserPointer()); - if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor()) - return 0.f; - } - } - - btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA()); - if(!mObject || distsqr < mLeastDistSqr) - { - mObject = collisionObject; - mLeastDistSqr = distsqr; - mContactPoint = cp.getPositionWorldOnA(); - } - } - - return 0.f; - } - }; - std::pair PhysicsSystem::getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f &origin, const osg::Quat &orient, From c94cd775bf9103c2f488216771131f3ae380804b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 30 Mar 2020 21:45:38 +0300 Subject: [PATCH 022/226] Separate ClosestNotMeRayResultCallback --- .../closestnotmerayresultcallback.cpp | 34 +++++++++++++++++++ .../closestnotmerayresultcallback.hpp | 24 +++++++++++++ apps/openmw/mwphysics/physicssystem.cpp | 30 +--------------- 3 files changed, 59 insertions(+), 29 deletions(-) create mode 100644 apps/openmw/mwphysics/closestnotmerayresultcallback.cpp create mode 100644 apps/openmw/mwphysics/closestnotmerayresultcallback.hpp diff --git a/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp b/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp new file mode 100644 index 000000000..86763a793 --- /dev/null +++ b/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp @@ -0,0 +1,34 @@ +#include "closestnotmerayresultcallback.hpp" + +#include + +#include + +#include "../mwworld/class.hpp" + +#include "ptrholder.hpp" + +namespace MWPhysics +{ + ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3& from, const btVector3& to) + : btCollisionWorld::ClosestRayResultCallback(from, to) + , mMe(me), mTargets(targets) + { + } + + btScalar ClosestNotMeRayResultCallback::addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) + { + if (rayResult.m_collisionObject == mMe) + return 1.f; + if (!mTargets.empty()) + { + if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end())) + { + PtrHolder* holder = static_cast(rayResult.m_collisionObject->getUserPointer()); + if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor()) + return 1.f; + } + } + return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); + } +} diff --git a/apps/openmw/mwphysics/closestnotmerayresultcallback.hpp b/apps/openmw/mwphysics/closestnotmerayresultcallback.hpp new file mode 100644 index 000000000..3d2c5704b --- /dev/null +++ b/apps/openmw/mwphysics/closestnotmerayresultcallback.hpp @@ -0,0 +1,24 @@ +#ifndef OPENMW_MWPHYSICS_CLOSESTNOTMERAYRESULTCALLBACK_H +#define OPENMW_MWPHYSICS_CLOSESTNOTMERAYRESULTCALLBACK_H + +#include + +#include + +class btCollisionObject; + +namespace MWPhysics +{ + class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback + { + public: + ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3& from, const btVector3& to); + + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace); + private: + const btCollisionObject* mMe; + const std::vector mTargets; + }; +} + +#endif diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 0ff433aa4..73284cd7f 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -47,6 +47,7 @@ #include "heightfield.hpp" #include "hasspherecollisioncallback.hpp" #include "deepestnotmecontacttestresultcallback.hpp" +#include "closestnotmerayresultcallback.hpp" namespace MWPhysics { @@ -737,35 +738,6 @@ namespace MWPhysics return (point - Misc::Convert::toOsg(cb.m_hitPointWorld)).length(); } - class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback - { - public: - ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3& from, const btVector3& to) - : btCollisionWorld::ClosestRayResultCallback(from, to) - , mMe(me), mTargets(targets) - { - } - - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) - { - if (rayResult.m_collisionObject == mMe) - return 1.f; - if (!mTargets.empty()) - { - if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end())) - { - PtrHolder* holder = static_cast(rayResult.m_collisionObject->getUserPointer()); - if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor()) - return 1.f; - } - } - return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); - } - private: - const btCollisionObject* mMe; - const std::vector mTargets; - }; - PhysicsSystem::RayResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector targets, int mask, int group) const { btVector3 btFrom = Misc::Convert::toBullet(from); From 5d625c12dc052c0db9aea2ed092aa3266d989733 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 30 Mar 2020 21:49:47 +0300 Subject: [PATCH 023/226] Separate ContactTestResultCallback --- .../mwphysics/contacttestresultcallback.cpp | 27 +++++++++++++++++ .../mwphysics/contacttestresultcallback.hpp | 30 +++++++++++++++++++ apps/openmw/mwphysics/physicssystem.cpp | 27 +---------------- 3 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 apps/openmw/mwphysics/contacttestresultcallback.cpp create mode 100644 apps/openmw/mwphysics/contacttestresultcallback.hpp diff --git a/apps/openmw/mwphysics/contacttestresultcallback.cpp b/apps/openmw/mwphysics/contacttestresultcallback.cpp new file mode 100644 index 000000000..f8209e363 --- /dev/null +++ b/apps/openmw/mwphysics/contacttestresultcallback.cpp @@ -0,0 +1,27 @@ +#include "contacttestresultcallback.hpp" + +#include + +#include "ptrholder.hpp" + +namespace MWPhysics +{ + ContactTestResultCallback::ContactTestResultCallback(const btCollisionObject* testedAgainst) + : mTestedAgainst(testedAgainst) + { + } + + btScalar ContactTestResultCallback::addSingleResult(btManifoldPoint& cp, + const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, + const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) + { + const btCollisionObject* collisionObject = col0Wrap->m_collisionObject; + if (collisionObject == mTestedAgainst) + collisionObject = col1Wrap->m_collisionObject; + PtrHolder* holder = static_cast(collisionObject->getUserPointer()); + if (holder) + mResult.push_back(holder->getPtr()); + return 0.f; + } + +} diff --git a/apps/openmw/mwphysics/contacttestresultcallback.hpp b/apps/openmw/mwphysics/contacttestresultcallback.hpp new file mode 100644 index 000000000..bb90eced0 --- /dev/null +++ b/apps/openmw/mwphysics/contacttestresultcallback.hpp @@ -0,0 +1,30 @@ +#ifndef OPENMW_MWPHYSICS_CONTACTTESTRESULTCALLBACK_H +#define OPENMW_MWPHYSICS_CONTACTTESTRESULTCALLBACK_H + +#include + +#include + +#include "../mwworld/ptr.hpp" + +class btCollisionObject; +class btCollisionObjectWrapper; + +namespace MWPhysics +{ + class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback + { + const btCollisionObject* mTestedAgainst; + + public: + ContactTestResultCallback(const btCollisionObject* testedAgainst); + + virtual btScalar addSingleResult(btManifoldPoint& cp, + const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, + const btCollisionObjectWrapper* col1Wrap,int partId1,int index1); + + std::vector mResult; + }; +} + +#endif diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 73284cd7f..bf4bd386e 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -48,6 +48,7 @@ #include "hasspherecollisioncallback.hpp" #include "deepestnotmecontacttestresultcallback.hpp" #include "closestnotmerayresultcallback.hpp" +#include "contacttestresultcallback.hpp" namespace MWPhysics { @@ -882,32 +883,6 @@ namespace MWPhysics return osg::Vec3f(); } - class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback - { - public: - ContactTestResultCallback(const btCollisionObject* testedAgainst) - : mTestedAgainst(testedAgainst) - { - } - - const btCollisionObject* mTestedAgainst; - - std::vector mResult; - - virtual btScalar addSingleResult(btManifoldPoint& cp, - const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, - const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) - { - const btCollisionObject* collisionObject = col0Wrap->m_collisionObject; - if (collisionObject == mTestedAgainst) - collisionObject = col1Wrap->m_collisionObject; - PtrHolder* holder = static_cast(collisionObject->getUserPointer()); - if (holder) - mResult.push_back(holder->getPtr()); - return 0.f; - } - }; - std::vector PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const { btCollisionObject* me = nullptr; From ca6cce0c7e98ab43ebc5550c28c43d4ab7136706 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 30 Mar 2020 23:07:12 +0300 Subject: [PATCH 024/226] Separate Stepper --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwphysics/constants.hpp | 16 +++ apps/openmw/mwphysics/physicssystem.cpp | 150 +----------------------- apps/openmw/mwphysics/physicssystem.hpp | 3 - apps/openmw/mwphysics/stepper.cpp | 148 +++++++++++++++++++++++ apps/openmw/mwphysics/stepper.hpp | 32 +++++ apps/openmw/mwworld/worldimp.cpp | 1 + 7 files changed, 200 insertions(+), 152 deletions(-) create mode 100644 apps/openmw/mwphysics/constants.hpp create mode 100644 apps/openmw/mwphysics/stepper.cpp create mode 100644 apps/openmw/mwphysics/stepper.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 67eb0cd27..0472bd033 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -71,7 +71,7 @@ add_openmw_dir (mwworld add_openmw_dir (mwphysics physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback - contacttestresultcallback deepestnotmecontacttestresultcallback + contacttestresultcallback deepestnotmecontacttestresultcallback stepper ) add_openmw_dir (mwclass diff --git a/apps/openmw/mwphysics/constants.hpp b/apps/openmw/mwphysics/constants.hpp new file mode 100644 index 000000000..46367ab34 --- /dev/null +++ b/apps/openmw/mwphysics/constants.hpp @@ -0,0 +1,16 @@ +#ifndef OPENMW_MWPHYSICS_CONSTANTS_H +#define OPENMW_MWPHYSICS_CONSTANTS_H + +namespace MWPhysics +{ + static const float sStepSizeUp = 34.0f; + static const float sStepSizeDown = 62.0f; + static const float sMinStep = 10.f; + static const float sGroundOffset = 1.0f; + static const float sMaxSlope = 49.0f; + + // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. + static const int sMaxIterations = 8; +} + +#endif diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index bf4bd386e..908ec90f3 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -49,17 +49,11 @@ #include "deepestnotmecontacttestresultcallback.hpp" #include "closestnotmerayresultcallback.hpp" #include "contacttestresultcallback.hpp" +#include "stepper.hpp" +#include "constants.hpp" namespace MWPhysics { - - static const float sStepSizeDown = 62.0f; - static const float sMinStep = 10.f; - static const float sGroundOffset = 1.0f; - - // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. - static const int sMaxIterations = 8; - static bool isActor(const btCollisionObject *obj) { assert(obj); @@ -73,146 +67,6 @@ namespace MWPhysics return (normal.z() > sMaxSlopeCos); } - static bool canStepDown(const ActorTracer &stepper) - { - return stepper.mHitObject && isWalkableSlope(stepper.mPlaneNormal) && !isActor(stepper.mHitObject); - } - - class Stepper - { - private: - const btCollisionWorld *mColWorld; - const btCollisionObject *mColObj; - - ActorTracer mTracer, mUpStepper, mDownStepper; - bool mHaveMoved; - - public: - Stepper(const btCollisionWorld *colWorld, const btCollisionObject *colObj) - : mColWorld(colWorld) - , mColObj(colObj) - , mHaveMoved(true) - {} - - bool step(osg::Vec3f &position, const osg::Vec3f &toMove, float &remainingTime) - { - /* - * Slide up an incline or set of stairs. Should be called only after a - * collision detection otherwise unnecessary tracing will be performed. - * - * NOTE: with a small change this method can be used to step over an obstacle - * of height sStepSize. - * - * If successful return 'true' and update 'position' to the new possible - * location and adjust 'remainingTime'. - * - * If not successful return 'false'. May fail for these reasons: - * - can't move directly up from current position - * - having moved up by between epsilon() and sStepSize, can't move forward - * - having moved forward by between epsilon() and toMove, - * = moved down between 0 and just under sStepSize but slope was too steep, or - * = moved the full sStepSize down (FIXME: this could be a bug) - * - * - * - * Starting position. Obstacle or stairs with height upto sStepSize in front. - * - * +--+ +--+ |XX - * | | -------> toMove | | +--+XX - * | | | | |XXXXX - * | | +--+ | | +--+XXXXX - * | | |XX| | | |XXXXXXXX - * +--+ +--+ +--+ +-------- - * ============================================== - */ - - /* - * Try moving up sStepSize using stepper. - * FIXME: does not work in case there is no front obstacle but there is one above - * - * +--+ +--+ - * | | | | - * | | | | |XX - * | | | | +--+XX - * | | | | |XXXXX - * +--+ +--+ +--+ +--+XXXXX - * |XX| |XXXXXXXX - * +--+ +-------- - * ============================================== - */ - if (mHaveMoved) - { - mHaveMoved = false; - mUpStepper.doTrace(mColObj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), mColWorld); - if(mUpStepper.mFraction < std::numeric_limits::epsilon()) - return false; // didn't even move the smallest representable amount - // (TODO: shouldn't this be larger? Why bother with such a small amount?) - } - - /* - * Try moving from the elevated position using tracer. - * - * +--+ +--+ - * | | |YY| FIXME: collision with object YY - * | | +--+ - * | | - * <------------------->| | - * +--+ +--+ - * |XX| the moved amount is toMove*tracer.mFraction - * +--+ - * ============================================== - */ - osg::Vec3f tracerPos = mUpStepper.mEndPos; - mTracer.doTrace(mColObj, tracerPos, tracerPos + toMove, mColWorld); - if(mTracer.mFraction < std::numeric_limits::epsilon()) - return false; // didn't even move the smallest representable amount - - /* - * Try moving back down sStepSizeDown using stepper. - * NOTE: if there is an obstacle below (e.g. stairs), we'll be "stepping up". - * Below diagram is the case where we "stepped over" an obstacle in front. - * - * +--+ - * |YY| - * +--+ +--+ - * | | - * | | - * +--+ | | - * |XX| | | - * +--+ +--+ - * ============================================== - */ - mDownStepper.doTrace(mColObj, mTracer.mEndPos, mTracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), mColWorld); - if (!canStepDown(mDownStepper)) - { - // Try again with increased step length - if (mTracer.mFraction < 1.0f || toMove.length2() > sMinStep*sMinStep) - return false; - - osg::Vec3f direction = toMove; - direction.normalize(); - mTracer.doTrace(mColObj, tracerPos, tracerPos + direction*sMinStep, mColWorld); - if (mTracer.mFraction < 0.001f) - return false; - - mDownStepper.doTrace(mColObj, mTracer.mEndPos, mTracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), mColWorld); - if (!canStepDown(mDownStepper)) - return false; - } - if (mDownStepper.mFraction < 1.0f) - { - // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. - // TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing - // NOTE: caller's variables 'position' & 'remainingTime' are modified here - position = mDownStepper.mEndPos; - remainingTime *= (1.0f-mTracer.mFraction); // remaining time is proportional to remaining distance - mHaveMoved = true; - return true; - } - return false; - } - }; - class MovementSolver { private: diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index d74e2de16..0f2ecc092 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -50,9 +50,6 @@ namespace MWPhysics class Object; class Actor; - static const float sMaxSlope = 49.0f; - static const float sStepSizeUp = 34.0f; - class PhysicsSystem { public: diff --git a/apps/openmw/mwphysics/stepper.cpp b/apps/openmw/mwphysics/stepper.cpp new file mode 100644 index 000000000..0ab383dd1 --- /dev/null +++ b/apps/openmw/mwphysics/stepper.cpp @@ -0,0 +1,148 @@ +#include "stepper.hpp" + +#include + +#include +#include + +#include "collisiontype.hpp" +#include "constants.hpp" + +namespace MWPhysics +{ + static bool canStepDown(const ActorTracer &stepper) + { + if (!stepper.mHitObject) + return false; + static const float sMaxSlopeCos = std::cos(osg::DegreesToRadians(sMaxSlope)); + if (stepper.mPlaneNormal.z() <= sMaxSlopeCos) + return false; + + return stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor; + } + + Stepper::Stepper(const btCollisionWorld *colWorld, const btCollisionObject *colObj) + : mColWorld(colWorld) + , mColObj(colObj) + , mHaveMoved(true) + { + } + + bool Stepper::step(osg::Vec3f &position, const osg::Vec3f &toMove, float &remainingTime) + { + /* + * Slide up an incline or set of stairs. Should be called only after a + * collision detection otherwise unnecessary tracing will be performed. + * + * NOTE: with a small change this method can be used to step over an obstacle + * of height sStepSize. + * + * If successful return 'true' and update 'position' to the new possible + * location and adjust 'remainingTime'. + * + * If not successful return 'false'. May fail for these reasons: + * - can't move directly up from current position + * - having moved up by between epsilon() and sStepSize, can't move forward + * - having moved forward by between epsilon() and toMove, + * = moved down between 0 and just under sStepSize but slope was too steep, or + * = moved the full sStepSize down (FIXME: this could be a bug) + * + * Starting position. Obstacle or stairs with height upto sStepSize in front. + * + * +--+ +--+ |XX + * | | -------> toMove | | +--+XX + * | | | | |XXXXX + * | | +--+ | | +--+XXXXX + * | | |XX| | | |XXXXXXXX + * +--+ +--+ +--+ +-------- + * ============================================== + */ + + /* + * Try moving up sStepSize using stepper. + * FIXME: does not work in case there is no front obstacle but there is one above + * + * +--+ +--+ + * | | | | + * | | | | |XX + * | | | | +--+XX + * | | | | |XXXXX + * +--+ +--+ +--+ +--+XXXXX + * |XX| |XXXXXXXX + * +--+ +-------- + * ============================================== + */ + if (mHaveMoved) + { + mHaveMoved = false; + + mUpStepper.doTrace(mColObj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), mColWorld); + if (mUpStepper.mFraction < std::numeric_limits::epsilon()) + return false; // didn't even move the smallest representable amount + // (TODO: shouldn't this be larger? Why bother with such a small amount?) + } + + /* + * Try moving from the elevated position using tracer. + * + * +--+ +--+ + * | | |YY| FIXME: collision with object YY + * | | +--+ + * | | + * <------------------->| | + * +--+ +--+ + * |XX| the moved amount is toMove*tracer.mFraction + * +--+ + * ============================================== + */ + osg::Vec3f tracerPos = mUpStepper.mEndPos; + mTracer.doTrace(mColObj, tracerPos, tracerPos + toMove, mColWorld); + if (mTracer.mFraction < std::numeric_limits::epsilon()) + return false; // didn't even move the smallest representable amount + + /* + * Try moving back down sStepSizeDown using stepper. + * NOTE: if there is an obstacle below (e.g. stairs), we'll be "stepping up". + * Below diagram is the case where we "stepped over" an obstacle in front. + * + * +--+ + * |YY| + * +--+ +--+ + * | | + * | | + * +--+ | | + * |XX| | | + * +--+ +--+ + * ============================================== + */ + mDownStepper.doTrace(mColObj, mTracer.mEndPos, mTracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), mColWorld); + if (!canStepDown(mDownStepper)) + { + // Try again with increased step length + if (mTracer.mFraction < 1.0f || toMove.length2() > sMinStep*sMinStep) + return false; + + osg::Vec3f direction = toMove; + direction.normalize(); + mTracer.doTrace(mColObj, tracerPos, tracerPos + direction*sMinStep, mColWorld); + if (mTracer.mFraction < 0.001f) + return false; + + mDownStepper.doTrace(mColObj, mTracer.mEndPos, mTracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), mColWorld); + if (!canStepDown(mDownStepper)) + return false; + } + + if (mDownStepper.mFraction < 1.0f) + { + // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. + // TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing + // NOTE: caller's variables 'position' & 'remainingTime' are modified here + position = mDownStepper.mEndPos; + remainingTime *= (1.0f-mTracer.mFraction); // remaining time is proportional to remaining distance + mHaveMoved = true; + return true; + } + return false; + } +} diff --git a/apps/openmw/mwphysics/stepper.hpp b/apps/openmw/mwphysics/stepper.hpp new file mode 100644 index 000000000..27e6294b0 --- /dev/null +++ b/apps/openmw/mwphysics/stepper.hpp @@ -0,0 +1,32 @@ +#ifndef OPENMW_MWPHYSICS_STEPPER_H +#define OPENMW_MWPHYSICS_STEPPER_H + +#include "trace.h" + +class btCollisionObject; +class btCollisionWorld; + +namespace osg +{ + class Vec3f; +} + +namespace MWPhysics +{ + class Stepper + { + private: + const btCollisionWorld *mColWorld; + const btCollisionObject *mColObj; + + ActorTracer mTracer, mUpStepper, mDownStepper; + bool mHaveMoved; + + public: + Stepper(const btCollisionWorld *colWorld, const btCollisionObject *colObj); + + bool step(osg::Vec3f &position, const osg::Vec3f &toMove, float &remainingTime); + }; +} + +#endif diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 26eac954b..8598b9b04 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -56,6 +56,7 @@ #include "../mwphysics/actor.hpp" #include "../mwphysics/collisiontype.hpp" #include "../mwphysics/object.hpp" +#include "../mwphysics/constants.hpp" #include "player.hpp" #include "manualref.hpp" From 19010ec045a5149021f1919f239f098db83af128 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 31 Mar 2020 00:05:54 +0300 Subject: [PATCH 025/226] Separate MovementSolver --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwphysics/movementsolver.cpp | 324 ++++++++++++++++++++++ apps/openmw/mwphysics/movementsolver.hpp | 40 +++ apps/openmw/mwphysics/physicssystem.cpp | 334 +---------------------- 4 files changed, 366 insertions(+), 334 deletions(-) create mode 100644 apps/openmw/mwphysics/movementsolver.cpp create mode 100644 apps/openmw/mwphysics/movementsolver.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 0472bd033..4b74d308f 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -71,7 +71,7 @@ add_openmw_dir (mwworld add_openmw_dir (mwphysics physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback - contacttestresultcallback deepestnotmecontacttestresultcallback stepper + contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver ) add_openmw_dir (mwclass diff --git a/apps/openmw/mwphysics/movementsolver.cpp b/apps/openmw/mwphysics/movementsolver.cpp new file mode 100644 index 000000000..6345a76d9 --- /dev/null +++ b/apps/openmw/mwphysics/movementsolver.cpp @@ -0,0 +1,324 @@ +#include "movementsolver.hpp" + +#include +#include +#include + +#include +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include "../mwmechanics/actorutil.hpp" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/movement.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/refdata.hpp" + +#include "actor.hpp" +#include "collisiontype.hpp" +#include "constants.hpp" +#include "stepper.hpp" +#include "trace.h" + +namespace MWPhysics +{ + static bool isActor(const btCollisionObject *obj) + { + assert(obj); + return obj->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor; + } + + template + static bool isWalkableSlope(const Vec3 &normal) + { + static const float sMaxSlopeCos = std::cos(osg::DegreesToRadians(sMaxSlope)); + return (normal.z() > sMaxSlopeCos); + } + + osg::Vec3f MovementSolver::traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight) + { + osg::Vec3f offset = actor->getCollisionObjectPosition() - ptr.getRefData().getPosition().asVec3(); + + ActorTracer tracer; + tracer.findGround(actor, position + offset, position + offset - osg::Vec3f(0,0,maxHeight), collisionWorld); + if (tracer.mFraction >= 1.0f) + { + actor->setOnGround(false); + return position; + } + + actor->setOnGround(true); + + // Check if we actually found a valid spawn point (use an infinitely thin ray this time). + // Required for some broken door destinations in Morrowind.esm, where the spawn point + // intersects with other geometry if the actor's base is taken into account + btVector3 from = Misc::Convert::toBullet(position); + btVector3 to = from - btVector3(0,0,maxHeight); + + btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); + resultCallback1.m_collisionFilterGroup = 0xff; + resultCallback1.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap; + + collisionWorld->rayTest(from, to, resultCallback1); + + if (resultCallback1.hasHit() && ((Misc::Convert::toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos + offset).length2() > 35*35 + || !isWalkableSlope(tracer.mPlaneNormal))) + { + actor->setOnSlope(!isWalkableSlope(resultCallback1.m_hitNormalWorld)); + return Misc::Convert::toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, sGroundOffset); + } + + actor->setOnSlope(!isWalkableSlope(tracer.mPlaneNormal)); + + return tracer.mEndPos-offset + osg::Vec3f(0.f, 0.f, sGroundOffset); + } + + osg::Vec3f MovementSolver::move(osg::Vec3f position, const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time, + bool isFlying, float waterlevel, float slowFall, const btCollisionWorld* collisionWorld, + std::map& standingCollisionTracker) + { + const ESM::Position& refpos = ptr.getRefData().getPosition(); + // Early-out for totally static creatures + // (Not sure if gravity should still apply?) + if (!ptr.getClass().isMobile(ptr)) + return position; + + // Reset per-frame data + physicActor->setWalkingOnWater(false); + // Anything to collide with? + if(!physicActor->getCollisionMode()) + { + return position + (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * + osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1)) + ) * movement * time; + } + + const btCollisionObject *colobj = physicActor->getCollisionObject(); + osg::Vec3f halfExtents = physicActor->getHalfExtents(); + + // NOTE: here we don't account for the collision box translation (i.e. physicActor->getPosition() - refpos.pos). + // That means the collision shape used for moving this actor is in a different spot than the collision shape + // other actors are using to collide against this actor. + // While this is strictly speaking wrong, it's needed for MW compatibility. + position.z() += halfExtents.z(); + + static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get().find("fSwimHeightScale")->mValue.getFloat(); + float swimlevel = waterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale); + + ActorTracer tracer; + + osg::Vec3f inertia = physicActor->getInertialForce(); + osg::Vec3f velocity; + + if (position.z() < swimlevel || isFlying) + { + velocity = (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * movement; + } + else + { + velocity = (osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * movement; + + if ((velocity.z() > 0.f && physicActor->getOnGround() && !physicActor->getOnSlope()) + || (velocity.z() > 0.f && velocity.z() + inertia.z() <= -velocity.z() && physicActor->getOnSlope())) + inertia = velocity; + else if (!physicActor->getOnGround() || physicActor->getOnSlope()) + velocity = velocity + inertia; + } + + // dead actors underwater will float to the surface, if the CharacterController tells us to do so + if (movement.z() > 0 && ptr.getClass().getCreatureStats(ptr).isDead() && position.z() < swimlevel) + velocity = osg::Vec3f(0,0,1) * 25; + + if (ptr.getClass().getMovementSettings(ptr).mPosition[2]) + { + const bool isPlayer = (ptr == MWMechanics::getPlayer()); + // Advance acrobatics and set flag for GetPCJumping + if (isPlayer) + { + ptr.getClass().skillUsageSucceeded(ptr, ESM::Skill::Acrobatics, 0); + MWBase::Environment::get().getWorld()->getPlayer().setJumping(true); + } + + // Decrease fatigue + if (!isPlayer || !MWBase::Environment::get().getWorld()->getGodModeState()) + { + const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + const float fFatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat(); + const float fFatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat(); + const float normalizedEncumbrance = std::min(1.f, ptr.getClass().getNormalizedEncumbrance(ptr)); + const float fatigueDecrease = fFatigueJumpBase + normalizedEncumbrance * fFatigueJumpMult; + MWMechanics::DynamicStat fatigue = ptr.getClass().getCreatureStats(ptr).getFatigue(); + fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease); + ptr.getClass().getCreatureStats(ptr).setFatigue(fatigue); + } + ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0; + } + + // Now that we have the effective movement vector, apply wind forces to it + if (MWBase::Environment::get().getWorld()->isInStorm()) + { + osg::Vec3f stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); + float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity / (stormDirection.length() * velocity.length()))); + static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get().find("fStromWalkMult")->mValue.getFloat(); + velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f)); + } + + Stepper stepper(collisionWorld, colobj); + osg::Vec3f origVelocity = velocity; + osg::Vec3f newPosition = position; + /* + * A loop to find newPosition using tracer, if successful different from the starting position. + * nextpos is the local variable used to find potential newPosition, using velocity and remainingTime + * The initial velocity was set earlier (see above). + */ + float remainingTime = time; + for (int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations) + { + osg::Vec3f nextpos = newPosition + velocity * remainingTime; + + // If not able to fly, don't allow to swim up into the air + if(!isFlying && nextpos.z() > swimlevel && newPosition.z() < swimlevel) + { + const osg::Vec3f down(0,0,-1); + velocity = slide(velocity, down); + // NOTE: remainingTime is unchanged before the loop continues + continue; // velocity updated, calculate nextpos again + } + + if((newPosition - nextpos).length2() > 0.0001) + { + // trace to where character would go if there were no obstructions + tracer.doTrace(colobj, newPosition, nextpos, collisionWorld); + + // check for obstructions + if(tracer.mFraction >= 1.0f) + { + newPosition = tracer.mEndPos; // ok to move, so set newPosition + break; + } + } + else + { + // The current position and next position are nearly the same, so just exit. + // Note: Bullet can trigger an assert in debug modes if the positions + // are the same, since that causes it to attempt to normalize a zero + // length vector (which can also happen with nearly identical vectors, since + // precision can be lost due to any math Bullet does internally). Since we + // aren't performing any collision detection, we want to reject the next + // position, so that we don't slowly move inside another object. + break; + } + + // We are touching something. + if (tracer.mFraction < 1E-9f) + { + // Try to separate by backing off slighly to unstuck the solver + osg::Vec3f backOff = (newPosition - tracer.mHitPoint) * 1E-2f; + newPosition += backOff; + } + + // We hit something. Check if we can step up. + float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z(); + osg::Vec3f oldPosition = newPosition; + bool result = false; + if (hitHeight < sStepSizeUp && !isActor(tracer.mHitObject)) + { + // Try to step up onto it. + // NOTE: stepMove does not allow stepping over, modifies newPosition if successful + result = stepper.step(newPosition, velocity*remainingTime, remainingTime); + } + if (result) + { + // don't let pure water creatures move out of water after stepMove + if (ptr.getClass().isPureWaterCreature(ptr) && newPosition.z() + halfExtents.z() > waterlevel) + newPosition = oldPosition; + } + else + { + // Can't move this way, try to find another spot along the plane + osg::Vec3f newVelocity = slide(velocity, tracer.mPlaneNormal); + + // Do not allow sliding upward if there is gravity. + // Stepping will have taken care of that. + if(!(newPosition.z() < swimlevel || isFlying)) + newVelocity.z() = std::min(newVelocity.z(), 0.0f); + + if ((newVelocity-velocity).length2() < 0.01) + break; + if ((newVelocity * origVelocity) <= 0.f) + break; // ^ dot product + + velocity = newVelocity; + } + } + + bool isOnGround = false; + bool isOnSlope = false; + if (!(inertia.z() > 0.f) && !(newPosition.z() < swimlevel)) + { + osg::Vec3f from = newPosition; + osg::Vec3f to = newPosition - (physicActor->getOnGround() ? osg::Vec3f(0,0,sStepSizeDown + 2*sGroundOffset) : osg::Vec3f(0,0,2*sGroundOffset)); + tracer.doTrace(colobj, from, to, collisionWorld); + if(tracer.mFraction < 1.0f && !isActor(tracer.mHitObject)) + { + const btCollisionObject* standingOn = tracer.mHitObject; + PtrHolder* ptrHolder = static_cast(standingOn->getUserPointer()); + if (ptrHolder) + standingCollisionTracker[ptr] = ptrHolder->getPtr(); + + if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Water) + physicActor->setWalkingOnWater(true); + if (!isFlying) + newPosition.z() = tracer.mEndPos.z() + sGroundOffset; + + isOnGround = true; + + isOnSlope = !isWalkableSlope(tracer.mPlaneNormal); + } + else + { + // standing on actors is not allowed (see above). + // in addition to that, apply a sliding effect away from the center of the actor, + // so that we do not stay suspended in air indefinitely. + if (tracer.mFraction < 1.0f && isActor(tracer.mHitObject)) + { + if (osg::Vec3f(velocity.x(), velocity.y(), 0).length2() < 100.f*100.f) + { + btVector3 aabbMin, aabbMax; + tracer.mHitObject->getCollisionShape()->getAabb(tracer.mHitObject->getWorldTransform(), aabbMin, aabbMax); + btVector3 center = (aabbMin + aabbMax) / 2.f; + inertia = osg::Vec3f(position.x() - center.x(), position.y() - center.y(), 0); + inertia.normalize(); + inertia *= 100; + } + } + + isOnGround = false; + } + } + + if((isOnGround && !isOnSlope) || newPosition.z() < swimlevel || isFlying) + physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f)); + else + { + inertia.z() -= time * Constants::GravityConst * Constants::UnitsPerMeter; + if (inertia.z() < 0) + inertia.z() *= slowFall; + if (slowFall < 1.f) { + inertia.x() *= slowFall; + inertia.y() *= slowFall; + } + physicActor->setInertialForce(inertia); + } + physicActor->setOnGround(isOnGround); + physicActor->setOnSlope(isOnSlope); + + newPosition.z() -= halfExtents.z(); // remove what was added at the beginning + return newPosition; + } +} diff --git a/apps/openmw/mwphysics/movementsolver.hpp b/apps/openmw/mwphysics/movementsolver.hpp new file mode 100644 index 000000000..54a417fa7 --- /dev/null +++ b/apps/openmw/mwphysics/movementsolver.hpp @@ -0,0 +1,40 @@ +#ifndef OPENMW_MWPHYSICS_MOVEMENTSOLVER_H +#define OPENMW_MWPHYSICS_MOVEMENTSOLVER_H + +#include + +#include + +#include "../mwworld/ptr.hpp" + +class btCollisionWorld; + +namespace MWPhysics +{ + class Actor; + + class MovementSolver + { + private: + ///Project a vector u on another vector v + static inline osg::Vec3f project(const osg::Vec3f& u, const osg::Vec3f &v) + { + return v * (u * v); + // ^ dot product + } + + ///Helper for computing the character sliding + static inline osg::Vec3f slide(const osg::Vec3f& direction, const osg::Vec3f &planeNormal) + { + return direction - project(direction, planeNormal); + } + + public: + static osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight); + static osg::Vec3f move(osg::Vec3f position, const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time, + bool isFlying, float waterlevel, float slowFall, const btCollisionWorld* collisionWorld, + std::map& standingCollisionTracker); + }; +} + +#endif diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 908ec90f3..a205abeb4 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -29,12 +29,10 @@ #include "../mwbase/environment.hpp" #include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/movement.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" -#include "../mwworld/player.hpp" #include "../mwrender/bulletdebugdraw.hpp" @@ -49,341 +47,11 @@ #include "deepestnotmecontacttestresultcallback.hpp" #include "closestnotmerayresultcallback.hpp" #include "contacttestresultcallback.hpp" -#include "stepper.hpp" #include "constants.hpp" +#include "movementsolver.hpp" namespace MWPhysics { - static bool isActor(const btCollisionObject *obj) - { - assert(obj); - return obj->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor; - } - - template - static bool isWalkableSlope(const Vec3 &normal) - { - static const float sMaxSlopeCos = std::cos(osg::DegreesToRadians(sMaxSlope)); - return (normal.z() > sMaxSlopeCos); - } - - class MovementSolver - { - private: - ///Project a vector u on another vector v - static inline osg::Vec3f project(const osg::Vec3f& u, const osg::Vec3f &v) - { - return v * (u * v); - // ^ dot product - } - - ///Helper for computing the character sliding - static inline osg::Vec3f slide(const osg::Vec3f& direction, const osg::Vec3f &planeNormal) - { - return direction - project(direction, planeNormal); - } - - public: - static osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight) - { - osg::Vec3f offset = actor->getCollisionObjectPosition() - ptr.getRefData().getPosition().asVec3(); - - ActorTracer tracer; - tracer.findGround(actor, position + offset, position + offset - osg::Vec3f(0,0,maxHeight), collisionWorld); - if(tracer.mFraction >= 1.0f) - { - actor->setOnGround(false); - return position; - } - else - { - actor->setOnGround(true); - - // Check if we actually found a valid spawn point (use an infinitely thin ray this time). - // Required for some broken door destinations in Morrowind.esm, where the spawn point - // intersects with other geometry if the actor's base is taken into account - btVector3 from = Misc::Convert::toBullet(position); - btVector3 to = from - btVector3(0,0,maxHeight); - - btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); - resultCallback1.m_collisionFilterGroup = 0xff; - resultCallback1.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap; - - collisionWorld->rayTest(from, to, resultCallback1); - - if (resultCallback1.hasHit() && - ( (Misc::Convert::toOsg(resultCallback1.m_hitPointWorld) - (tracer.mEndPos-offset)).length2() > 35*35 - || !isWalkableSlope(tracer.mPlaneNormal))) - { - actor->setOnSlope(!isWalkableSlope(resultCallback1.m_hitNormalWorld)); - return Misc::Convert::toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, sGroundOffset); - } - else - { - actor->setOnSlope(!isWalkableSlope(tracer.mPlaneNormal)); - } - - return tracer.mEndPos-offset + osg::Vec3f(0.f, 0.f, sGroundOffset); - } - } - - static osg::Vec3f move(osg::Vec3f position, const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time, - bool isFlying, float waterlevel, float slowFall, const btCollisionWorld* collisionWorld, - std::map& standingCollisionTracker) - { - const ESM::Position& refpos = ptr.getRefData().getPosition(); - // Early-out for totally static creatures - // (Not sure if gravity should still apply?) - if (!ptr.getClass().isMobile(ptr)) - return position; - - // Reset per-frame data - physicActor->setWalkingOnWater(false); - // Anything to collide with? - if(!physicActor->getCollisionMode()) - { - return position + (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * - osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1)) - ) * movement * time; - } - - const btCollisionObject *colobj = physicActor->getCollisionObject(); - osg::Vec3f halfExtents = physicActor->getHalfExtents(); - - // NOTE: here we don't account for the collision box translation (i.e. physicActor->getPosition() - refpos.pos). - // That means the collision shape used for moving this actor is in a different spot than the collision shape - // other actors are using to collide against this actor. - // While this is strictly speaking wrong, it's needed for MW compatibility. - position.z() += halfExtents.z(); - - static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get() - .find("fSwimHeightScale")->mValue.getFloat(); - float swimlevel = waterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale); - - ActorTracer tracer; - - osg::Vec3f inertia = physicActor->getInertialForce(); - osg::Vec3f velocity; - - if(position.z() < swimlevel || isFlying) - { - velocity = (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * - osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * movement; - } - else - { - velocity = (osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * movement; - - if ((velocity.z() > 0.f && physicActor->getOnGround() && !physicActor->getOnSlope()) - || (velocity.z() > 0.f && velocity.z() + inertia.z() <= -velocity.z() && physicActor->getOnSlope())) - inertia = velocity; - else if (!physicActor->getOnGround() || physicActor->getOnSlope()) - velocity = velocity + inertia; - } - - // dead actors underwater will float to the surface, if the CharacterController tells us to do so - if (movement.z() > 0 && ptr.getClass().getCreatureStats(ptr).isDead() && position.z() < swimlevel) - velocity = osg::Vec3f(0,0,1) * 25; - - if (ptr.getClass().getMovementSettings(ptr).mPosition[2]) - { - const bool isPlayer = (ptr == MWMechanics::getPlayer()); - // Advance acrobatics and set flag for GetPCJumping - if (isPlayer) - { - ptr.getClass().skillUsageSucceeded(ptr, ESM::Skill::Acrobatics, 0); - MWBase::Environment::get().getWorld()->getPlayer().setJumping(true); - } - - // Decrease fatigue - if (!isPlayer || !MWBase::Environment::get().getWorld()->getGodModeState()) - { - const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - const float fFatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat(); - const float fFatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat(); - const float normalizedEncumbrance = std::min(1.f, ptr.getClass().getNormalizedEncumbrance(ptr)); - const float fatigueDecrease = fFatigueJumpBase + normalizedEncumbrance * fFatigueJumpMult; - MWMechanics::DynamicStat fatigue = ptr.getClass().getCreatureStats(ptr).getFatigue(); - fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease); - ptr.getClass().getCreatureStats(ptr).setFatigue(fatigue); - } - ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0; - } - - // Now that we have the effective movement vector, apply wind forces to it - if (MWBase::Environment::get().getWorld()->isInStorm()) - { - osg::Vec3f stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); - float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity / (stormDirection.length() * velocity.length()))); - static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get() - .find("fStromWalkMult")->mValue.getFloat(); - velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f)); - } - - Stepper stepper(collisionWorld, colobj); - osg::Vec3f origVelocity = velocity; - osg::Vec3f newPosition = position; - /* - * A loop to find newPosition using tracer, if successful different from the starting position. - * nextpos is the local variable used to find potential newPosition, using velocity and remainingTime - * The initial velocity was set earlier (see above). - */ - float remainingTime = time; - for(int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations) - { - osg::Vec3f nextpos = newPosition + velocity * remainingTime; - - // If not able to fly, don't allow to swim up into the air - if(!isFlying && // can't fly - nextpos.z() > swimlevel && // but about to go above water - newPosition.z() < swimlevel) - { - const osg::Vec3f down(0,0,-1); - velocity = slide(velocity, down); - // NOTE: remainingTime is unchanged before the loop continues - continue; // velocity updated, calculate nextpos again - } - - if((newPosition - nextpos).length2() > 0.0001) - { - // trace to where character would go if there were no obstructions - tracer.doTrace(colobj, newPosition, nextpos, collisionWorld); - - // check for obstructions - if(tracer.mFraction >= 1.0f) - { - newPosition = tracer.mEndPos; // ok to move, so set newPosition - break; - } - } - else - { - // The current position and next position are nearly the same, so just exit. - // Note: Bullet can trigger an assert in debug modes if the positions - // are the same, since that causes it to attempt to normalize a zero - // length vector (which can also happen with nearly identical vectors, since - // precision can be lost due to any math Bullet does internally). Since we - // aren't performing any collision detection, we want to reject the next - // position, so that we don't slowly move inside another object. - break; - } - - // We are touching something. - if (tracer.mFraction < 1E-9f) - { - // Try to separate by backing off slighly to unstuck the solver - osg::Vec3f backOff = (newPosition - tracer.mHitPoint) * 1E-2f; - newPosition += backOff; - } - - // We hit something. Check if we can step up. - float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z(); - osg::Vec3f oldPosition = newPosition; - bool result = false; - if (hitHeight < sStepSizeUp && !isActor(tracer.mHitObject)) - { - // Try to step up onto it. - // NOTE: stepMove does not allow stepping over, modifies newPosition if successful - result = stepper.step(newPosition, velocity*remainingTime, remainingTime); - } - if (result) - { - // don't let pure water creatures move out of water after stepMove - if (ptr.getClass().isPureWaterCreature(ptr) - && newPosition.z() + halfExtents.z() > waterlevel) - newPosition = oldPosition; - } - else - { - // Can't move this way, try to find another spot along the plane - osg::Vec3f newVelocity = slide(velocity, tracer.mPlaneNormal); - - // Do not allow sliding upward if there is gravity. - // Stepping will have taken care of that. - if(!(newPosition.z() < swimlevel || isFlying)) - newVelocity.z() = std::min(newVelocity.z(), 0.0f); - - if ((newVelocity-velocity).length2() < 0.01) - break; - if ((newVelocity * origVelocity) <= 0.f) - break; // ^ dot product - - velocity = newVelocity; - } - } - - bool isOnGround = false; - bool isOnSlope = false; - if (!(inertia.z() > 0.f) && !(newPosition.z() < swimlevel)) - { - osg::Vec3f from = newPosition; - osg::Vec3f to = newPosition - (physicActor->getOnGround() ? - osg::Vec3f(0,0,sStepSizeDown + 2*sGroundOffset) : osg::Vec3f(0,0,2*sGroundOffset)); - tracer.doTrace(colobj, from, to, collisionWorld); - if(tracer.mFraction < 1.0f - && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor) - { - const btCollisionObject* standingOn = tracer.mHitObject; - PtrHolder* ptrHolder = static_cast(standingOn->getUserPointer()); - if (ptrHolder) - standingCollisionTracker[ptr] = ptrHolder->getPtr(); - - if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Water) - physicActor->setWalkingOnWater(true); - if (!isFlying) - newPosition.z() = tracer.mEndPos.z() + sGroundOffset; - - isOnGround = true; - - isOnSlope = !isWalkableSlope(tracer.mPlaneNormal); - } - else - { - // standing on actors is not allowed (see above). - // in addition to that, apply a sliding effect away from the center of the actor, - // so that we do not stay suspended in air indefinitely. - if (tracer.mFraction < 1.0f && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor) - { - if (osg::Vec3f(velocity.x(), velocity.y(), 0).length2() < 100.f*100.f) - { - btVector3 aabbMin, aabbMax; - tracer.mHitObject->getCollisionShape()->getAabb(tracer.mHitObject->getWorldTransform(), aabbMin, aabbMax); - btVector3 center = (aabbMin + aabbMax) / 2.f; - inertia = osg::Vec3f(position.x() - center.x(), position.y() - center.y(), 0); - inertia.normalize(); - inertia *= 100; - } - } - - isOnGround = false; - } - } - - if((isOnGround && !isOnSlope) || newPosition.z() < swimlevel || isFlying) - physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f)); - else - { - inertia.z() -= time * Constants::GravityConst * Constants::UnitsPerMeter; - if (inertia.z() < 0) - inertia.z() *= slowFall; - if (slowFall < 1.f) { - inertia.x() *= slowFall; - inertia.y() *= slowFall; - } - physicActor->setInertialForce(inertia); - } - physicActor->setOnGround(isOnGround); - physicActor->setOnSlope(isOnSlope); - - newPosition.z() -= halfExtents.z(); // remove what was added at the beginning - return newPosition; - } - }; - - - // --------------------------------------------------------------- - - PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr parentNode) : mShapeManager(new Resource::BulletShapeManager(resourceSystem->getVFS(), resourceSystem->getSceneManager(), resourceSystem->getNifFileManager())) , mResourceSystem(resourceSystem) From 4f08f6e09bb51d0a5cd0513a4421e1ab50209e92 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 31 Mar 2020 00:25:33 +0300 Subject: [PATCH 026/226] Separate ClosestNotMeConvexResultCallback --- apps/openmw/CMakeLists.txt | 1 + .../closestnotmeconvexresultcallback.cpp | 34 ++++++++++++++++ .../closestnotmeconvexresultcallback.hpp | 24 ++++++++++++ .../deepestnotmecontacttestresultcallback.cpp | 2 + .../deepestnotmecontacttestresultcallback.hpp | 3 +- apps/openmw/mwphysics/trace.cpp | 39 +------------------ 6 files changed, 64 insertions(+), 39 deletions(-) create mode 100644 apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp create mode 100644 apps/openmw/mwphysics/closestnotmeconvexresultcallback.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 4b74d308f..44f37dac0 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -72,6 +72,7 @@ add_openmw_dir (mwworld add_openmw_dir (mwphysics physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver + closestnotmeconvexresultcallback ) add_openmw_dir (mwclass diff --git a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp new file mode 100644 index 000000000..ddfdb8a42 --- /dev/null +++ b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp @@ -0,0 +1,34 @@ +#include "closestnotmeconvexresultcallback.hpp" + +#include + +namespace MWPhysics +{ + ClosestNotMeConvexResultCallback::ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot) + : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), + mMe(me), mMotion(motion), mMinCollisionDot(minCollisionDot) + { + } + + btScalar ClosestNotMeConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) + { + if (convexResult.m_hitCollisionObject == mMe) + return btScalar(1); + + btVector3 hitNormalWorld; + if (normalInWorldSpace) + hitNormalWorld = convexResult.m_hitNormalLocal; + else + { + ///need to transform normal into worldspace + hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; + } + + // dot product of the motion vector against the collision contact normal + btScalar dotCollision = mMotion.dot(hitNormalWorld); + if (dotCollision <= mMinCollisionDot) + return btScalar(1); + + return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); + } +} diff --git a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.hpp b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.hpp new file mode 100644 index 000000000..e4e960ab8 --- /dev/null +++ b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.hpp @@ -0,0 +1,24 @@ +#ifndef OPENMW_MWPHYSICS_CLOSESTNOTMECONVEXRESULTCALLBACK_H +#define OPENMW_MWPHYSICS_CLOSESTNOTMECONVEXRESULTCALLBACK_H + +#include + +class btCollisionObject; + +namespace MWPhysics +{ + class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback + { + public: + ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot); + + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace); + + protected: + const btCollisionObject *mMe; + const btVector3 mMotion; + const btScalar mMinCollisionDot; + }; +} + +#endif diff --git a/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp index be94128c1..0baaa6241 100644 --- a/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp +++ b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp @@ -2,6 +2,8 @@ #include +#include + #include "../mwworld/class.hpp" #include "ptrholder.hpp" diff --git a/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp index 2fcef66db..f1107046b 100644 --- a/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp +++ b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp @@ -3,9 +3,10 @@ #include -#include #include +class btCollisionObject; + namespace MWPhysics { class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback diff --git a/apps/openmw/mwphysics/trace.cpp b/apps/openmw/mwphysics/trace.cpp index 13bc7f798..58082f4db 100644 --- a/apps/openmw/mwphysics/trace.cpp +++ b/apps/openmw/mwphysics/trace.cpp @@ -7,48 +7,11 @@ #include "collisiontype.hpp" #include "actor.hpp" +#include "closestnotmeconvexresultcallback.hpp" namespace MWPhysics { -class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback -{ -public: - ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot) - : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), - mMe(me), mMotion(motion), mMinCollisionDot(minCollisionDot) - { - } - - virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) - { - if(convexResult.m_hitCollisionObject == mMe) - return btScalar( 1 ); - - btVector3 hitNormalWorld; - if(normalInWorldSpace) - hitNormalWorld = convexResult.m_hitNormalLocal; - else - { - ///need to transform normal into worldspace - hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; - } - - // dot product of the motion vector against the collision contact normal - btScalar dotCollision = mMotion.dot(hitNormalWorld); - if(dotCollision <= mMinCollisionDot) - return btScalar(1); - - return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); - } - -protected: - const btCollisionObject *mMe; - const btVector3 mMotion; - const btScalar mMinCollisionDot; -}; - - void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& start, const osg::Vec3f& end, const btCollisionWorld* world) { const btVector3 btstart = Misc::Convert::toBullet(start); From 80239235d3e8c661da49da745abb266b79db14ea Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 31 Mar 2020 13:04:03 +0300 Subject: [PATCH 027/226] Always create NiGeometry nodes as MatrixTransform --- components/nifosg/nifloader.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index ea43fc9c6..51d25210d 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -461,8 +461,6 @@ namespace NifOsg break; } case Nif::RC_NiSwitchNode: - case Nif::RC_NiTriShape: - case Nif::RC_NiTriStrips: case Nif::RC_NiAutoNormalParticles: case Nif::RC_NiRotatingParticles: // Leaf nodes in the NIF hierarchy, so won't be able to dynamically attach children. From 5e2e5b7aa9e2f8c5da97c9c0815f2d84fccd8593 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 31 Mar 2020 18:31:46 +0300 Subject: [PATCH 028/226] Fix btCollisionObjectWrapper forward declaration --- apps/openmw/mwphysics/contacttestresultcallback.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwphysics/contacttestresultcallback.hpp b/apps/openmw/mwphysics/contacttestresultcallback.hpp index bb90eced0..0f469127d 100644 --- a/apps/openmw/mwphysics/contacttestresultcallback.hpp +++ b/apps/openmw/mwphysics/contacttestresultcallback.hpp @@ -8,7 +8,7 @@ #include "../mwworld/ptr.hpp" class btCollisionObject; -class btCollisionObjectWrapper; +struct btCollisionObjectWrapper; namespace MWPhysics { From c8596f782aec42050265effe52d8488ea4adc6aa Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 28 Mar 2020 13:45:44 +0100 Subject: [PATCH 029/226] [macOS, CI] Use prebuilt dependencies with SDL2 2.0.12 --- CI/before_install.osx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 0a4c7313c..e279d1933 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -5,5 +5,5 @@ brew outdated pkgconfig || brew upgrade pkgconfig brew install qt brew install ccache -curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-110f3d3.zip -o ~/openmw-deps.zip +curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-ef2462c.zip -o ~/openmw-deps.zip unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null From e63325ebff69278f451dc64e04f732fb3a117e39 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 14 Mar 2020 22:24:36 +0400 Subject: [PATCH 030/226] Support for camera rotation via gyroscope on Android (feature #5311) --- CHANGELOG.md | 1 + CMakeLists.txt | 8 +- apps/openmw/CMakeLists.txt | 2 - apps/openmw/engine.cpp | 23 +- apps/openmw/mwinput/inputmanagerimp.cpp | 232 +++++++++++- apps/openmw/mwinput/inputmanagerimp.hpp | 33 ++ components/sdlutil/events.hpp | 8 + components/sdlutil/sdlinputwrapper.cpp | 34 +- components/sdlutil/sdlinputwrapper.hpp | 2 + .../reference/modding/settings/input.rst | 77 ++++ files/gamecontrollerdb_204.txt | 268 -------------- files/gamecontrollerdb_205.txt | 336 ------------------ files/settings-default.cfg | 18 + 13 files changed, 407 insertions(+), 635 deletions(-) delete mode 100644 files/gamecontrollerdb_204.txt delete mode 100644 files/gamecontrollerdb_205.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 985c49c09..668c0e169 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -270,6 +270,7 @@ Feature #5224: Handle NiKeyframeController for NiTriShape Feature #5274: Editor: Keyboard shortcut to drop objects to ground/obstacle in scene view Feature #5304: Morrowind-style bump-mapping + Feature #5311: Support for gyroscopes input on Android Feature #5314: Ingredient filter in the alchemy window Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4695: Optimize Distant Terrain memory consumption diff --git a/CMakeLists.txt b/CMakeLists.txt index 58376a7ce..031bffca0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -318,7 +318,7 @@ set(Boost_NO_BOOST_CMAKE ON) find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) find_package(MyGUI 3.2.2 REQUIRED) -find_package(SDL2 REQUIRED) +find_package(SDL2 2.0.9 REQUIRED) find_package(OpenAL REQUIRED) find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath) @@ -394,12 +394,6 @@ copy_resource_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters configure_resource_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb.txt "${OpenMW_BINARY_DIR}" "gamecontrollerdb.txt") -configure_resource_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb_204.txt - "${OpenMW_BINARY_DIR}" "gamecontrollerdb_204.txt") - -configure_resource_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb_205.txt - "${OpenMW_BINARY_DIR}" "gamecontrollerdb_205.txt") - if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop "${OpenMW_BINARY_DIR}/openmw.desktop") diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2bc85e062..19afed761 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -189,8 +189,6 @@ if(APPLE) configure_file("${OpenMW_BINARY_DIR}/settings-default.cfg" ${BUNDLE_RESOURCES_DIR} COPYONLY) configure_file("${OpenMW_BINARY_DIR}/openmw.cfg" ${BUNDLE_RESOURCES_DIR} COPYONLY) configure_file("${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" ${BUNDLE_RESOURCES_DIR} COPYONLY) - configure_file("${OpenMW_BINARY_DIR}/gamecontrollerdb_204.txt" ${BUNDLE_RESOURCES_DIR} COPYONLY) - configure_file("${OpenMW_BINARY_DIR}/gamecontrollerdb_205.txt" ${BUNDLE_RESOURCES_DIR} COPYONLY) add_custom_command(TARGET openmw POST_BUILD diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 57c69be05..435db0841 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -235,7 +235,9 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) { MWClass::registerClasses(); - Uint32 flags = SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE|SDL_INIT_GAMECONTROLLER|SDL_INIT_JOYSTICK; + SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); // We use only gamepads + + Uint32 flags = SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE|SDL_INIT_GAMECONTROLLER|SDL_INIT_JOYSTICK|SDL_INIT_SENSOR; if(SDL_WasInit(flags) == 0) { SDL_SetMainReady(); @@ -505,22 +507,9 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) else Log(Debug::Info) << "Loading keybindings file: " << keybinderUser; - // find correct path to the game controller bindings - // File format for controller mappings is different for SDL <= 2.0.4, 2.0.5, and >= 2.0.6 - SDL_version linkedSdlVersion; - SDL_GetVersion(&linkedSdlVersion); - std::string controllerFileName; - if (linkedSdlVersion.major == 2 && linkedSdlVersion.minor == 0 && linkedSdlVersion.patch <= 4) { - controllerFileName = "gamecontrollerdb_204.txt"; - } else if (linkedSdlVersion.major == 2 && linkedSdlVersion.minor == 0 && linkedSdlVersion.patch == 5) { - controllerFileName = "gamecontrollerdb_205.txt"; - } else { - controllerFileName = "gamecontrollerdb.txt"; - } - - const std::string userdefault = mCfgMgr.getUserConfigPath().string() + "/" + controllerFileName; - const std::string localdefault = mCfgMgr.getLocalPath().string() + "/" + controllerFileName; - const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/" + controllerFileName; + const std::string userdefault = mCfgMgr.getUserConfigPath().string() + "/gamecontrollerdb.txt"; + const std::string localdefault = mCfgMgr.getLocalPath().string() + "/gamecontrollerdb.txt"; + const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/gamecontrollerdb.txt"; std::string userGameControllerdb; if (boost::filesystem::exists(userdefault)){ diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 540efdf89..bef578ab6 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -79,10 +79,20 @@ 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); @@ -113,7 +123,6 @@ namespace MWInput /* Joystick Init */ // Load controller mappings -#if SDL_VERSION_ATLEAST(2,0,2) if(!controllerBindingsFile.empty()) { SDL_GameControllerAddMappingsFromFile(controllerBindingsFile.c_str()); @@ -122,7 +131,6 @@ namespace MWInput { SDL_GameControllerAddMappingsFromFile(userControllerBindingsFile.c_str()); } -#endif // Open all presently connected sticks int numSticks = SDL_NumJoysticks(); @@ -141,6 +149,9 @@ namespace MWInput } } + correctGyroscopeAxes(); + updateSensors(); + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); if (uiScale != 0.f) mInvUiScalingFactor = 1.f / uiScale; @@ -163,6 +174,12 @@ namespace MWInput { mInputBinder->save (mUserFile); + if (mGyroscope != nullptr) + { + SDL_SensorClose(mGyroscope); + mGyroscope = nullptr; + } + delete mInputBinder; delete mInputManager; @@ -170,6 +187,112 @@ 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; @@ -608,6 +731,37 @@ 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; + } + // Disable movement in Gui mode if (!(MWBase::Environment::get().getWindowManager()->isGuiMode() || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)) @@ -803,6 +957,27 @@ 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"); @@ -956,6 +1131,59 @@ 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; + } + + 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); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 12ea9f3fa..23562ba79 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -3,6 +3,8 @@ #include "../mwgui/mode.hpp" +#include + #include #include @@ -65,6 +67,7 @@ namespace MWInput public MWBase::InputManager, public SDLUtil::KeyListener, public SDLUtil::MouseListener, + public SDLUtil::SensorListener, public SDLUtil::WindowListener, public SDLUtil::ControllerListener, public ICS::ChannelListener, @@ -123,6 +126,9 @@ 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); @@ -162,6 +168,17 @@ 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; @@ -218,6 +235,16 @@ namespace MWInput float mInvUiScalingFactor; float mGamepadCursorSpeed; + float mGyroXSpeed; + float mGyroYSpeed; + float mGyroUpdateTimer; + + float mGyroHSensitivity; + float mGyroVSensitivity; + GyroscopeAxis mGyroHAxis; + GyroscopeAxis mGyroVAxis; + float mGyroInputThreshold; + private: void convertMousePosForMyGUI(int& x, int& y); @@ -236,9 +263,14 @@ 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(); @@ -264,6 +296,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 diff --git a/components/sdlutil/events.hpp b/components/sdlutil/events.hpp index b7a736617..4d400e5b8 100644 --- a/components/sdlutil/events.hpp +++ b/components/sdlutil/events.hpp @@ -33,6 +33,14 @@ public: virtual void mouseWheelMoved( const SDL_MouseWheelEvent &arg) = 0; }; +class SensorListener +{ +public: + virtual ~SensorListener() {} + virtual void sensorUpdated(const SDL_SensorEvent &arg) = 0; + virtual void displayOrientationChanged() = 0; +}; + class KeyListener { public: diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index c804f0b2d..60997d281 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -1,6 +1,7 @@ #include "sdlinputwrapper.hpp" #include +#include #include @@ -11,6 +12,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v mSDLWindow(window), mViewer(viewer), mMouseListener(nullptr), + mSensorListener(nullptr), mKeyboardListener(nullptr), mWindowListener(nullptr), mConListener(nullptr), @@ -79,6 +81,9 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v mMouseListener->mouseMoved(_packageMouseMotion(evt)); mMouseListener->mouseWheelMoved(evt.wheel); break; + case SDL_SENSORUPDATE: + mSensorListener->sensorUpdated(evt.sensor); + break; case SDL_MOUSEBUTTONDOWN: mMouseListener->mousePressed(evt.button, evt.button.button); break; @@ -109,11 +114,8 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v case SDL_TEXTINPUT: mKeyboardListener->textInput(evt.text); break; - -#if SDL_VERSION_ATLEAST(2, 0, 4) case SDL_KEYMAPCHANGED: break; -#endif case SDL_JOYHATMOTION: //As we manage everything with GameController, don't even bother with these. case SDL_JOYAXISMOTION: case SDL_JOYBUTTONDOWN: @@ -148,6 +150,17 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v if (mWindowListener) mWindowListener->windowClosed(); break; + case SDL_DISPLAYEVENT: + switch (evt.display.event) + { + case SDL_DISPLAYEVENT_ORIENTATION: + if (mSensorListener && evt.display.display == (unsigned int) Settings::Manager::getInt("screen", "Video")) + mSensorListener->displayOrientationChanged(); + break; + default: + break; + } + break; case SDL_CLIPBOARDUPDATE: break; // We don't need this event, clipboard is retrieved on demand @@ -160,6 +173,21 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v // No use for touch & gesture events break; + case SDL_APP_WILLENTERBACKGROUND: + case SDL_APP_WILLENTERFOREGROUND: + case SDL_APP_DIDENTERBACKGROUND: + case SDL_APP_DIDENTERFOREGROUND: + // We do not need background/foreground switch event for mobile devices so far + break; + + case SDL_APP_TERMINATING: + // There is nothing we can do here. + break; + + case SDL_APP_LOWMEMORY: + Log(Debug::Warning) << "System reports that free RAM on device is running low. You may encounter an unexpected behaviour."; + break; + default: Log(Debug::Info) << "Unhandled SDL event of type 0x" << std::hex << evt.type; break; diff --git a/components/sdlutil/sdlinputwrapper.hpp b/components/sdlutil/sdlinputwrapper.hpp index 8387cbd6f..fde37f35f 100644 --- a/components/sdlutil/sdlinputwrapper.hpp +++ b/components/sdlutil/sdlinputwrapper.hpp @@ -26,6 +26,7 @@ namespace SDLUtil ~InputWrapper(); void setMouseEventCallback(MouseListener* listen) { mMouseListener = listen; } + void setSensorEventCallback(SensorListener* listen) { mSensorListener = listen; } void setKeyboardEventCallback(KeyListener* listen) { mKeyboardListener = listen; } void setWindowEventCallback(WindowListener* listen) { mWindowListener = listen; } void setControllerEventCallback(ControllerListener* listen) { mConListener = listen; } @@ -58,6 +59,7 @@ namespace SDLUtil osg::ref_ptr mViewer; MouseListener* mMouseListener; + SensorListener* mSensorListener; KeyListener* mKeyboardListener; WindowListener* mWindowListener; ControllerListener* mConListener; diff --git a/docs/source/reference/modding/settings/input.rst b/docs/source/reference/modding/settings/input.rst index ef93b72cb..090b27958 100644 --- a/docs/source/reference/modding/settings/input.rst +++ b/docs/source/reference/modding/settings/input.rst @@ -134,3 +134,80 @@ This setting has no effect on the camera rotation speed, which is controlled by camera sensitivity setting. This setting can only be configured by editing the settings configuration file. + +enable gyroscope +---------------- + +:Type: boolean +:Range: True/False +:Default: False + +Enable the support of camera rotation based on the information supplied from the gyroscope through SDL. + +This setting can only be configured by editing the settings configuration file. + +gyro horizontal axis +-------------------- + +:Type: string +:Range: x, y, z, -x, -y, -z +:Default: -x + +This setting sets up an axis of the gyroscope as the horizontal camera axis. +Minus sign swaps the positive and negative direction of the axis. +Keep in mind that while this setting corresponds to the landscape mode of the display, +the portrait mode or any other mode will have this axis corrected automatically. + +This setting can only be configured by editing the settings configuration file. + +gyro vertical axis +------------------ + +:Type: string +:Range: x, y, z, -x, -y, -z +:Default: y + +This setting sets up an axis of the gyroscope as the vertical camera axis. +Minus sign swaps the positive and negative direction of the axis. +Keep in mind that while this setting corresponds to the landscape mode of the display, +the portrait mode or any other mode will have this axis corrected automatically. + +This setting can only be configured by editing the settings configuration file. + +gyro input threshold +-------------------- + +:Type: floating point +:Range: > 0 +:Default: 0.01 + +This setting determines the minimum value of the rotation that will be accepted. +It allows to avoid crosshair oscillation due to gyroscope "noise". + +This setting can only be configured by editing the settings configuration file. + +gyro horizontal sensitivity +--------------------------- + +:Type: float +:Range: >0 +:Default: 1.0 + +This setting controls the overall gyroscope horizontal sensitivity. +The smaller this sensitivity is, the less visible effect the device rotation +will have on the horizontal camera rotation, and vice versa. + +This setting can only be configured by editing the settings configuration file. + +gyro vertical sensitivity +------------------------- + +:Type: float +:Range: >0 +:Default: 1.0 + +This setting controls the overall gyroscope vertical sensitivity. +The smaller this sensitivity is, the less visible effect the device +rotation will have on the vertical camera rotation, and vice versa. + +This setting can only be configured by editing the settings configuration file. diff --git a/files/gamecontrollerdb_204.txt b/files/gamecontrollerdb_204.txt deleted file mode 100644 index 06c80fe00..000000000 --- a/files/gamecontrollerdb_204.txt +++ /dev/null @@ -1,268 +0,0 @@ -# Game Controller DB for SDL in legacy (pre 2.0.5) format -# Source: https://github.com/gabomdq/SDL_GameControllerDB -# Windows -02200090000000000000504944564944,8Bitdo NES30 PRO USB,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -20380900000000000000504944564944,8Bitdo NES30 PRO Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -10280900000000000000504944564944,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, -8f0e1200000000000000504944564944,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, -341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -c0111352000000000000504944564944,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, -d81d0b00000000000000504944564944,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, -e8206058000000000000504944564944,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -5e048e02000000000000504944564944,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -791d0103000000000000504944564944,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -4f0423b3000000000000504944564944,Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -341a0108000000000000504944564944,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -0d0f8500000000000000504944564944,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -0d0f5e00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:a3,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -0d0f5f00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -0d0f8400000000000000504944564944,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -0d0f8700000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -0d0f8800000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, -0d0f2700000000000000504944564944,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -79000600000000000000504944564944,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, -28040140000000000000504944564944,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -45130010000000000000504944564944,Generic USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -0d0f4900000000000000504944564944,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -d8140862000000000000504944564944,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, -0d0f4000000000000000504944564944,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, -0d0f6e00000000000000504944564944,HORIPAD 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -0d0fee00000000000000504944564944,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -0d0f4d00000000000000504944564944,HORIPAD3 A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -25090017000000000000504944564944,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows, -d81d0f00000000000000504944564944,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -d81d1000000000000000504944564944,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -83056020000000000000504944564944,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows, -6f0e2401000000000000504944564944,INJUSTICE FightStick for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -49190204000000000000504944564944,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -6d0418c2000000000000504944564944,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -38075032000000000000504944564944,Mad Catz FightPad PRO PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -38075082000000000000504944564944,Mad Catz FightPad PRO PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -38078433000000000000504944564944,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -38078483000000000000504944564944,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b6,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -38078134000000000000504944564944,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -38078184000000000000504944564944,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -38078034000000000000504944564944,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -38078084000000000000504944564944,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -38078532000000000000504944564944,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -38073888000000000000504944564944,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -38071888000000000000504944564944,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -25090128000000000000504944564944,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows, -79004318000000000000504944564944,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, -8f0e1030000000000000504944564944,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows, -2509e803000000000000504944564944,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, -79000018000000000000504944564944,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -8f0e0d31000000000000504944564944,Multilaser JS071 USB,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -100801e5000000000000504944564944,NEXT Classic USB Game Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -bd1215d0000000000000504944564944,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, -4b12014d000000000000504944564944,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows, -36280100000000000000504944564944,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b15,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b14,x:b1,y:b2,platform:Windows, -4d6963726f736f66742050432d6a6f79,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a5,righty:a4,x:b1,y:b2,platform:Windows, -120cf60e000000000000504944564944,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, -8f0e0300000000000000504944564944,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -d6206dca000000000000504944564944,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -10080100000000000000504944564944,PS1 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -10080300000000000000504944564944,PS2 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows, -88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, -25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows, -10008200000000000000504944564944,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -4c05c405000000000000504944564944,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -300f0011000000000000504944564944,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows, -300f1611000000000000504944564944,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, -222c0020000000000000504944564944,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows, -300f1210000000000000504944564944,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, -341a0104000000000000504944564944,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows, -222c0223000000000000504944564944,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -222c0023000000000000504944564944,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -0d0f1100000000000000504944564944,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -0d0f6a00000000000000504944564944,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -0d0f6b00000000000000504944564944,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -0d0f8a00000000000000504944564944,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -0d0f8b00000000000000504944564944,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -0d0f7000000000000000504944564944,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -0d0f2200000000000000504944564944,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -00f00300000000000000504944564944,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, -00f0f100000000000000504944564944,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, -6f0e1e01000000000000504944564944,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -300f1201000000000000504944564944,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -a3060cff000000000000504944564944,Saitek P2500,a:b2,b:b3,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b9,rightx:a2,righty:a3,start:b4,x:b0,y:b1,platform:Windows, -300f1001000000000000504944564944,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -9b280500000000000000504944564944,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, -79001100000000000000504944564944,Sega Saturn Gamepad,a:b1,b:b2,leftshoulder:b6,lefttrigger:b3,leftx:a0,lefty:a4,rightshoulder:b7,righttrigger:b0,start:b8,x:b4,y:b5,platform:Windows, -4c05cc09000000000000504944564944,Sony DualShock 4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -4c05a00b000000000000504944564944,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -ff113133000000000000504944564944,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows, -4f0415b3000000000000504944564944,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, -4f0400b3000000000000504944564944,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows, -66660488000000000000504944564944,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, -38076652000000000000504944564944,UnKnown,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, -63252305000000000000504944564944,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -79001b18000000000000504944564944,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, - -# Mac OS X -10280000000000000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, -830500000000000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -79000000000000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Mac OS X, -AD1B00000000000001F9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, -0d0f0000000000004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -0d0f0000000000006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -83050000000000006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, -6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -2509000000000000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X, -79000000000000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, -d814000000000000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, -8f0e0000000000000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X, -4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, -4c05000000000000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -891600000000000000fd000000000000,Razer Onza Tournament,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -79000000000000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, -81170000000000007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X, -b4040000000000000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, -351200000000000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, -4c05000000000000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -4c05000000000000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -11010000000000002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X, -11010000000000001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X, -4f0400000000000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X, -4f0400000000000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X, -bd1200000000000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, -10080000000000000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X, -050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X, -050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X, -5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -5e04000000000000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -5e04000000000000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, -5e04000000000000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, - -# Linux -05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, -05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, -030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, -03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, -030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, -03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b1,y:b3,platform:Linux, -0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006f0e00001f01000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, -03000000ff1100004133000010010000,GreenAsia Inc.USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, -03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux, -030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000830500006020000010010000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, -03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux, -030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, -03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, -03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006d04000016c2000011010000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d04000016c2000010010000,Logitech Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux, -05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, -03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, -030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000dd02000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, -030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, -05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, -030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, -050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, -03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, -05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, -03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, -03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, -030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, -050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, -03000000790000001100000010010000,RetroLink Saturn Classic Controller,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, -0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, -0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, -030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, -03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux, -03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux, -03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, -03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux, -030000004c050000c405000011810000,Sony DualShock 4,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -050000004c050000cc09000000810000,Sony DualShock 4 (CUH-ZCT2U) (Bluetooth),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -030000004c050000cc09000011810000,Sony DualShock 4 (CUH-ZCT2U) (USB),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -030000004c050000cc09000011010000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -050000004c050000cc09000000010000,Sony DualShock 4 V2 BT,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000004c050000a00b000011010000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, -030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, -030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, -030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, -030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux, -030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux, -03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, -03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, -030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000d102000001010000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux, -xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, - -# Android -4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, - -# iOS -4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS, -4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS, diff --git a/files/gamecontrollerdb_205.txt b/files/gamecontrollerdb_205.txt deleted file mode 100644 index 565d7df3d..000000000 --- a/files/gamecontrollerdb_205.txt +++ /dev/null @@ -1,336 +0,0 @@ -# Game Controller DB for SDL in 2.0.5 format -# Source: https://github.com/gabomdq/SDL_GameControllerDB -# Windows -03000000022000000090000000000000,8Bitdo NES30 PRO USB,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -03000000203800000900000000000000,8Bitdo NES30 PRO Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, -10280900000000000000504944564944,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, -030000008f0e00001200000000000000,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, -03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, -030000006b1400000055000000000000,bigben ps3padstreetnew,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, -03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, -e8206058000000000000504944564944,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -030000004f04000023b3000000000000,Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00005e00000000000000,Fighting Commander 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:a3,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00005f00000000000000,Fighting Commander 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, -030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows, -03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, -03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows, -030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -030000006d04000016c2000000000000,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000451300000010000000000000,Generic USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00006e00000000000000,HORIPAD 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00004d00000000000000,HORIPAD3 A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows, -03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows, -03000000b50700001403000000000000,IMPACT BLACK,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -030000006f0e00002401000000000000,INJUSTICE FightStick for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -030000006d04000019c2000000000000,Logitech Cordless RumblePad 2 USB,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows, -6d0418c2000000000000504944564944,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000006d04000018c2000000000000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700005032000000000000,Mad Catz FightPad PRO PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700005082000000000000,Mad Catz FightPad PRO PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008433000000000000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008483000000000000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b6,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000008305000031b0000000000000,MaxfireBlaze3,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows, -03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, -030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows, -0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, -03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000001008000001e5000000000000,NEXT Classic USB Game Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, -030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows, -03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b15,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b14,x:b1,y:b2,platform:Windows, -4d6963726f736f66742050432d6a6f79,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a5,righty:a4,x:b1,y:b2,platform:Windows, -03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, -030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000008f0e00007530000000000000,PS (R) Gamepad,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, -03000000100800000100000000000000,PS1 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -03000000100800000300000000000000,PS2 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -03000000888800000803000000000000,PS3,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows, -030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows, -4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows, -88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, -03000000250900000500000000000000,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows, -25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows, -03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -4c05c405000000000000504944564944,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows, -03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, -03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows, -03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, -03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows, -03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, -0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, -030000006f0e00001e01000000000000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, -03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, -03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -03000000a30600000cff000000000000,Saitek P2500,a:b2,b:b3,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b9,rightx:a2,righty:a3,start:b4,x:b0,y:b1,platform:Windows, -03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, -03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, -03000000300f00001101000000000000,saitek rumble pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, -030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, -03000000790000001100000000000000,Sega Saturn Gamepad,a:b1,b:b2,leftshoulder:b6,lefttrigger:b3,leftx:a0,lefty:a4,rightshoulder:b7,righttrigger:b0,start:b8,x:b4,y:b5,platform:Windows, -03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows, -030000004c050000cc09000000000000,Sony DualShock 4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000008f0e00000800000000000000,SpeedLink Strike FX Wireless,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows, -03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -030000004f04000015b3000000000000,Thrustmaster Dual Analog 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, -030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows, -030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, -03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, -03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -03000000380700006652000000000000,UnKnown,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, -03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, - -# Mac OS X -03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, -10280000000000000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, -830500000000000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000790000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Mac OS X, -03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, -030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, -030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X, -03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, -03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, -030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X, -030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, -4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, -030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -4c05000000000000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, -03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X, -03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, -030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, -351200000000000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, -030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -11010000000000002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X, -11010000000000001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X, -030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X, -030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X, -03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, -03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X, -050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X, -050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X, -030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, -030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, - -# Linux -05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, -05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, -03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, -05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, -05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, -030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux, -03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, -03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, -03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, -030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, -03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b1,y:b3,platform:Linux, -0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006f0e00001f01000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, -03000000ff1100004133000010010000,GreenAsia Inc.USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, -03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux, -030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000830500006020000010010000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, -050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux, -030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, -03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, -03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006d04000016c2000011010000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d04000016c2000010010000,Logitech Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux, -05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, -03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, -030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000dd02000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, -030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, -05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, -030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, -050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, -03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, -05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, -05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, -03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, -03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, -030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, -050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, -03000000790000001100000010010000,RetroLink Saturn Classic Controller,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, -0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, -0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, -030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, -03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux, -03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux, -03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, -03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux, -030000004c050000c405000011810000,Sony DualShock 4,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -050000004c050000cc09000000810000,Sony DualShock 4 (CUH-ZCT2U) (Bluetooth),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -030000004c050000cc09000011810000,Sony DualShock 4 (CUH-ZCT2U) (USB),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -030000004c050000a00b000011010000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000004c0500006802000011810000,Sony PLAYSTATION(R)3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -050000004c0500006802000000810000,Sony PLAYSTATION(R)3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, -030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, -030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, -030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, -030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux, -030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux, -03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, -03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux, -03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, -030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, -0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000d102000001010000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux, -xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, - -# Android -4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, - -# iOS -4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS, -4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS, diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 89f873b3a..06950e50d 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -384,6 +384,24 @@ enable controller = true # Emulated gamepad cursor sensitivity. gamepad cursor speed = 1.0 +# Enable gyroscope support. +enable gyroscope = false + +# Gyroscope axis that corresponds to horizontal camera axis. +gyro horizontal axis = -x + +# Gyroscope axis that corresponds to vertical camera axis. +gyro vertical axis = y + +# The minimum gyroscope movement that is able to rotate the camera. +gyro input threshold = 0.01 + +# Horizontal camera axis sensitivity to gyroscope movement. +gyro horizontal sensitivity = 1.0 + +# Vertical camera axis sensitivity to gyroscope movement. +gyro vertical sensitivity = 1.0 + [Saves] # Name of last character played, and default for loading save files. From 2edf1cdf4dbb433374f27e0802b19221363fe25a Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Thu, 2 Apr 2020 09:54:13 +0200 Subject: [PATCH 031/226] rewording --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 668c0e169..93f0455ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -270,7 +270,7 @@ Feature #5224: Handle NiKeyframeController for NiTriShape Feature #5274: Editor: Keyboard shortcut to drop objects to ground/obstacle in scene view Feature #5304: Morrowind-style bump-mapping - Feature #5311: Support for gyroscopes input on Android + Feature #5311: Support for gyroscopic input (e.g. Android) Feature #5314: Ingredient filter in the alchemy window Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4695: Optimize Distant Terrain memory consumption From 8958e29187683194ee9d4e0775caf47884331585 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 2 Apr 2020 20:14:52 +0200 Subject: [PATCH 032/226] reset errorhandler context --- apps/openmw/mwscript/scriptmanagerimp.cpp | 2 +- components/compiler/streamerrorhandler.cpp | 17 ++++++++++++++++- components/compiler/streamerrorhandler.hpp | 13 ++++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 0dcf34afb..c63beeec3 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -171,7 +171,7 @@ namespace MWScript { Compiler::Locals locals; - mErrorHandler.setContext(name2 + "[local variables]"); + Compiler::ContextRestore restore = mErrorHandler.setContext(name2 + "[local variables]", true); std::istringstream stream (script->mScriptText); Compiler::QuickFileParser parser (mErrorHandler, mCompilerContext, locals); diff --git a/components/compiler/streamerrorhandler.cpp b/components/compiler/streamerrorhandler.cpp index b5a3a8c9f..2528b5379 100644 --- a/components/compiler/streamerrorhandler.cpp +++ b/components/compiler/streamerrorhandler.cpp @@ -56,10 +56,25 @@ namespace Compiler Log(logLevel) << text.str(); } - void StreamErrorHandler::setContext(const std::string &context) + ContextRestore StreamErrorHandler::setContext(const std::string &context, bool restore) { + if (!restore) + { + mContext = context; + return {nullptr, {}}; + } + ContextRestore restorer(this, mContext); mContext = context; + return restorer; } StreamErrorHandler::StreamErrorHandler() {} + + ContextRestore::ContextRestore(StreamErrorHandler* handler, const std::string& context) : mHandler(handler), mContext(context) {} + + ContextRestore::~ContextRestore() + { + if(mHandler) + mHandler->setContext(mContext); + } } diff --git a/components/compiler/streamerrorhandler.hpp b/components/compiler/streamerrorhandler.hpp index e203a6ef7..2b4aeda50 100644 --- a/components/compiler/streamerrorhandler.hpp +++ b/components/compiler/streamerrorhandler.hpp @@ -7,6 +7,7 @@ namespace Compiler { + class ContextRestore; /// \brief Error handler implementation: Write errors into logging stream class StreamErrorHandler : public ErrorHandler @@ -26,13 +27,23 @@ namespace Compiler public: - void setContext(const std::string& context); + ContextRestore setContext(const std::string& context, bool restore = false); // constructors StreamErrorHandler (); ///< constructor }; + + class ContextRestore + { + StreamErrorHandler* mHandler; + const std::string mContext; + public: + ContextRestore (StreamErrorHandler* handler, const std::string& context); + + ~ContextRestore(); + }; } #endif From a16727d5e30a31ab35923ba83ddbd217272e2792 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 2 Apr 2020 20:27:52 +0200 Subject: [PATCH 033/226] implement move constructor --- apps/openmw/mwscript/scriptmanagerimp.cpp | 2 +- components/compiler/streamerrorhandler.cpp | 5 +++++ components/compiler/streamerrorhandler.hpp | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index c63beeec3..996687547 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -171,7 +171,7 @@ namespace MWScript { Compiler::Locals locals; - Compiler::ContextRestore restore = mErrorHandler.setContext(name2 + "[local variables]", true); + const Compiler::ContextRestore&& restore = mErrorHandler.setContext(name2 + "[local variables]", true); std::istringstream stream (script->mScriptText); Compiler::QuickFileParser parser (mErrorHandler, mCompilerContext, locals); diff --git a/components/compiler/streamerrorhandler.cpp b/components/compiler/streamerrorhandler.cpp index 2528b5379..c9a3a4e7f 100644 --- a/components/compiler/streamerrorhandler.cpp +++ b/components/compiler/streamerrorhandler.cpp @@ -72,6 +72,11 @@ namespace Compiler ContextRestore::ContextRestore(StreamErrorHandler* handler, const std::string& context) : mHandler(handler), mContext(context) {} + ContextRestore::ContextRestore(ContextRestore&& other) : mHandler(other.mHandler), mContext(other.mContext) + { + other.mHandler = nullptr; + } + ContextRestore::~ContextRestore() { if(mHandler) diff --git a/components/compiler/streamerrorhandler.hpp b/components/compiler/streamerrorhandler.hpp index 2b4aeda50..848c976a3 100644 --- a/components/compiler/streamerrorhandler.hpp +++ b/components/compiler/streamerrorhandler.hpp @@ -42,6 +42,8 @@ namespace Compiler public: ContextRestore (StreamErrorHandler* handler, const std::string& context); + ContextRestore (ContextRestore&& other); + ~ContextRestore(); }; } From e6a6411b95a9fc36a625283cb27397e7e85977b2 Mon Sep 17 00:00:00 2001 From: "Hristos N. Triantafillou" Date: Thu, 2 Apr 2020 13:32:49 -0500 Subject: [PATCH 034/226] These files aren't in the repo anymore --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 031bffca0..930f51f1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -516,10 +516,6 @@ if(WIN32) INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/settings-default.cfg" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug) INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) - INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb_204.txt" DESTINATION "." CONFIGURATIONS Debug) - INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb_204.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) - INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb_205.txt" DESTINATION "." CONFIGURATIONS Debug) - INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb_205.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) if(BUILD_MYGUI_PLUGIN) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Debug/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Debug) From d222435774f49e587f18f4d10ebc98a78ac2b22a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 2 Apr 2020 22:39:00 +0400 Subject: [PATCH 035/226] Do not disable a mouse cursor until it moved every time when you exit a GUI mode --- apps/openmw/mwgui/windowmanagerimp.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 752738b9c..87a124234 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1162,11 +1162,7 @@ namespace MWGui void WindowManager::setCursorVisible(bool visible) { - if (visible == mCursorVisible) - return; mCursorVisible = visible; - if (!visible) - mCursorActive = false; } void WindowManager::setCursorActive(bool active) From da7032caff82926c8da2c85c5e7becf312a9f220 Mon Sep 17 00:00:00 2001 From: "Hristos N. Triantafillou" Date: Thu, 2 Apr 2020 13:45:11 -0500 Subject: [PATCH 036/226] Two more that I missed --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 930f51f1d..b465a2890 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -487,8 +487,6 @@ IF(NOT WIN32 AND NOT APPLE) INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") - INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb_204.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") - INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb_205.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") IF(BUILD_OPENCS) INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs") From ef4a7089e4b8b19ff5dc768a1cccc3e7cc35017b Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 6 Oct 2019 13:19:55 +0200 Subject: [PATCH 037/226] Use template specialization for writeHNT to write raw arrays To avoid passing explicit size argument where it's possible. --- apps/opencs/model/doc/savingstages.cpp | 2 +- components/esm/esmwriter.hpp | 28 ++++++++++++++++++-------- components/esm/loadland.cpp | 8 ++++---- components/esm/player.cpp | 2 +- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index b7eb369d7..44698cd2e 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -356,7 +356,7 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1]; refRecord.mRefNum.save (writer, false, "MVRF"); - writer.writeHNT ("CNDT", moved.mTarget, 8); + writer.writeHNT ("CNDT", moved.mTarget); } refRecord.save (writer, false, false, ref.mState == CSMWorld::RecordBase::State_Deleted); diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 5d7aebaf9..ba5800f67 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -82,17 +82,23 @@ class ESMWriter endRecord(name); } -private: + template + void writeHNT(const std::string& name, const T (&data)[size]) + { + startSubRecord(name); + writeT(data); + endRecord(name); + } + // Prevent using writeHNT with strings. This already happened by accident and results in // state being discarded without any error on writing or reading it. :( // writeHNString and friends must be used instead. - void writeHNT(const std::string& name, const std::string& data) - { - } - void writeT(const std::string& data) - { - } -public: + void writeHNT(const std::string& name, const std::string& data) = delete; + + void writeT(const std::string& data) = delete; + + template + void writeHNT(const std::string& name, const T (&data)[size], int) = delete; template void writeHNT(const std::string& name, const T& data, int size) @@ -108,6 +114,12 @@ public: write((char*)&data, sizeof(T)); } + template + void writeT(const T (&data)[size]) + { + write(reinterpret_cast(data), size * sizeof(T)); + } + template void writeT(const T& data, size_t size) { diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 0d9e68eb4..6fbc6735a 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -127,7 +127,7 @@ namespace ESM if (mLandData) { if (mDataTypes & Land::DATA_VNML) { - esm.writeHNT("VNML", mLandData->mNormals, sizeof(mLandData->mNormals)); + esm.writeHNT("VNML", mLandData->mNormals); } if (mDataTypes & Land::DATA_VHGT) { VHGT offsets; @@ -175,18 +175,18 @@ namespace ESM wnam[row * LAND_GLOBAL_MAP_LOD_SIZE_SQRT + col] = static_cast(height); } } - esm.writeHNT("WNAM", wnam, 81); + esm.writeHNT("WNAM", wnam); } if (mLandData) { if (mDataTypes & Land::DATA_VCLR) { - esm.writeHNT("VCLR", mLandData->mColours, 3*LAND_NUM_VERTS); + esm.writeHNT("VCLR", mLandData->mColours); } if (mDataTypes & Land::DATA_VTEX) { uint16_t vtex[LAND_NUM_TEXTURES]; transposeTextureData(mLandData->mTextures, vtex); - esm.writeHNT("VTEX", vtex, sizeof(vtex)); + esm.writeHNT("VTEX", vtex); } } diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 7dad34dfb..571a10a8c 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -58,7 +58,7 @@ void ESM::Player::save (ESMWriter &esm) const mCellId.save (esm); - esm.writeHNT ("LKEP", mLastKnownExteriorPosition, 12); + esm.writeHNT ("LKEP", mLastKnownExteriorPosition); if (mHasMark) { From 8c433d587c319ad7052b285a8e71d8c78d67e320 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 4 Apr 2020 14:09:00 +0200 Subject: [PATCH 038/226] less complicated context override --- apps/openmw/mwscript/scriptmanagerimp.cpp | 2 +- components/compiler/streamerrorhandler.cpp | 20 +++++--------------- components/compiler/streamerrorhandler.hpp | 16 +++++++++------- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 996687547..267ff7da6 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -171,7 +171,7 @@ namespace MWScript { Compiler::Locals locals; - const Compiler::ContextRestore&& restore = mErrorHandler.setContext(name2 + "[local variables]", true); + const Compiler::ContextOverride override(mErrorHandler, name2 + "[local variables]"); std::istringstream stream (script->mScriptText); Compiler::QuickFileParser parser (mErrorHandler, mCompilerContext, locals); diff --git a/components/compiler/streamerrorhandler.cpp b/components/compiler/streamerrorhandler.cpp index c9a3a4e7f..1c41d3f7f 100644 --- a/components/compiler/streamerrorhandler.cpp +++ b/components/compiler/streamerrorhandler.cpp @@ -56,30 +56,20 @@ namespace Compiler Log(logLevel) << text.str(); } - ContextRestore StreamErrorHandler::setContext(const std::string &context, bool restore) + void StreamErrorHandler::setContext(const std::string &context) { - if (!restore) - { - mContext = context; - return {nullptr, {}}; - } - ContextRestore restorer(this, mContext); mContext = context; - return restorer; } StreamErrorHandler::StreamErrorHandler() {} - ContextRestore::ContextRestore(StreamErrorHandler* handler, const std::string& context) : mHandler(handler), mContext(context) {} - - ContextRestore::ContextRestore(ContextRestore&& other) : mHandler(other.mHandler), mContext(other.mContext) + ContextOverride::ContextOverride(StreamErrorHandler& handler, const std::string& context) : mHandler(handler), mContext(handler.mContext) { - other.mHandler = nullptr; + mHandler.setContext(context); } - ContextRestore::~ContextRestore() + ContextOverride::~ContextOverride() { - if(mHandler) - mHandler->setContext(mContext); + mHandler.setContext(mContext); } } diff --git a/components/compiler/streamerrorhandler.hpp b/components/compiler/streamerrorhandler.hpp index 848c976a3..1f3b6e1ec 100644 --- a/components/compiler/streamerrorhandler.hpp +++ b/components/compiler/streamerrorhandler.hpp @@ -7,13 +7,14 @@ namespace Compiler { - class ContextRestore; + class ContextOverride; /// \brief Error handler implementation: Write errors into logging stream class StreamErrorHandler : public ErrorHandler { std::string mContext; + friend class ContextOverride; // not implemented StreamErrorHandler (const StreamErrorHandler&); @@ -27,7 +28,7 @@ namespace Compiler public: - ContextRestore setContext(const std::string& context, bool restore = false); + void setContext(const std::string& context); // constructors @@ -35,16 +36,17 @@ namespace Compiler ///< constructor }; - class ContextRestore + class ContextOverride { - StreamErrorHandler* mHandler; + StreamErrorHandler& mHandler; const std::string mContext; public: - ContextRestore (StreamErrorHandler* handler, const std::string& context); + ContextOverride (StreamErrorHandler& handler, const std::string& context); - ContextRestore (ContextRestore&& other); + ContextOverride (const ContextOverride&) = delete; + ContextOverride& operator= (const ContextOverride&) = delete; - ~ContextRestore(); + ~ContextOverride(); }; } From a68d9aed4c86157f15ac2d360edb1a7e6b65839d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 4 Apr 2020 17:45:26 +0400 Subject: [PATCH 039/226] Fix issues, found by CoverityScan --- apps/opencs/view/render/instancemode.cpp | 10 ++++------ apps/openmw/mwgui/alchemywindow.cpp | 9 +++++---- apps/openmw/mwinput/inputmanagerimp.cpp | 2 -- components/esm/loadscpt.cpp | 6 ++++-- components/terrain/cellborder.hpp | 1 - 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 7c0020f1e..0d221beb1 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -719,16 +719,14 @@ void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* mParentNode->accept(visitor); - for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin(); - it != intersector->getIntersections().end(); ++it) + osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin(); + if (it != intersector->getIntersections().end()) { osgUtil::LineSegmentIntersector::Intersection intersection = *it; ESM::Position position = object->getPosition(); object->setEdited (Object::Override_Position); position.pos[2] = intersection.getWorldIntersectPoint().z() + objectHeight; object->setPosition(position.pos); - - return; } } @@ -753,8 +751,8 @@ float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Objec mParentNode->accept(visitor); - for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin(); - it != intersector->getIntersections().end(); ++it) + osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin(); + if (it != intersector->getIntersections().end()) { osgUtil::LineSegmentIntersector::Intersection intersection = *it; float collisionLevel = intersection.getWorldIntersectPoint().z(); diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 8dc44059f..bacd1c769 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -31,6 +31,7 @@ namespace MWGui { AlchemyWindow::AlchemyWindow() : WindowBase("openmw_alchemy_window.layout") + , mCurrentFilter(FilterType::ByName) , mModel(nullptr) , mSortModel(nullptr) , mAlchemy(new MWMechanics::Alchemy()) @@ -192,16 +193,16 @@ namespace MWGui std::set itemNames, itemEffects; for (size_t i = 0; i < mModel->getItemCount(); ++i) { - auto const& base = mModel->getItem(i).mBase; - if (base.getTypeName() != typeid(ESM::Ingredient).name()) + MWWorld::Ptr item = mModel->getItem(i).mBase; + if (item.getTypeName() != typeid(ESM::Ingredient).name()) continue; - itemNames.insert(base.getClass().getName(base)); + itemNames.insert(item.getClass().getName(item)); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); auto const alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); - auto const effects = MWMechanics::Alchemy::effectsDescription(base, alchemySkill); + auto const effects = MWMechanics::Alchemy::effectsDescription(item, alchemySkill); itemEffects.insert(effects.begin(), effects.end()); } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index bef578ab6..acfe4f8cd 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1146,8 +1146,6 @@ namespace MWInput default: return 0.f; } - - return 0.f; } void InputManager::displayOrientationChanged() diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 8db284e1f..a7f348cb1 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -30,9 +30,11 @@ namespace ESM // The tmp buffer is a null-byte separated string list, we // just have to pick out one string at a time. char* str = tmp.data(); - if (!str && mVarNames.size() > 0) + if (!str) { - Log(Debug::Warning) << "SCVR with no variable names"; + if (mVarNames.size() > 0) + Log(Debug::Warning) << "SCVR with no variable names"; + return; } diff --git a/components/terrain/cellborder.hpp b/components/terrain/cellborder.hpp index 7770204e1..49759671f 100644 --- a/components/terrain/cellborder.hpp +++ b/components/terrain/cellborder.hpp @@ -31,7 +31,6 @@ namespace Terrain osg::Group *mRoot; CellGrid mCellBorderNodes; - int mBorderMask; }; } From 27d4fe9ee18df12cf3559fe7cf77253af5e4a828 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 4 Apr 2020 16:39:32 +0300 Subject: [PATCH 040/226] Fix ESM::Land oversights --- components/esm/loadland.cpp | 45 +++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 6fbc6735a..85ebc2c9a 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -75,6 +75,8 @@ namespace ESM mContext = esm.getContext(); mLandData = nullptr; + for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i) + mWnam[i] = 0; // Skip the land data here. Load it when the cell is loaded. while (esm.hasMoreSubs()) @@ -156,30 +158,25 @@ namespace ESM } esm.writeHNT("VHGT", offsets, sizeof(VHGT)); } - } - - if (mDataTypes & Land::DATA_WNAM) - { - // Generate WNAM record - signed char wnam[LAND_GLOBAL_MAP_LOD_SIZE]; - float max = std::numeric_limits::max(); - float min = std::numeric_limits::min(); - float vertMult = static_cast(ESM::Land::LAND_SIZE - 1) / LAND_GLOBAL_MAP_LOD_SIZE_SQRT; - for (int row = 0; row < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++row) + if (mDataTypes & Land::DATA_WNAM) { - for (int col = 0; col < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++col) + // Generate WNAM record + signed char wnam[LAND_GLOBAL_MAP_LOD_SIZE]; + float max = std::numeric_limits::max(); + float min = std::numeric_limits::min(); + float vertMult = static_cast(ESM::Land::LAND_SIZE - 1) / LAND_GLOBAL_MAP_LOD_SIZE_SQRT; + for (int row = 0; row < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++row) { - float height = mLandData->mHeights[int(row * vertMult) * ESM::Land::LAND_SIZE + int(col * vertMult)]; - height /= height > 0 ? 128.f : 16.f; - height = std::min(max, std::max(min, height)); - wnam[row * LAND_GLOBAL_MAP_LOD_SIZE_SQRT + col] = static_cast(height); + for (int col = 0; col < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++col) + { + float height = mLandData->mHeights[int(row * vertMult) * ESM::Land::LAND_SIZE + int(col * vertMult)]; + height /= height > 0 ? 128.f : 16.f; + height = std::min(max, std::max(min, height)); + wnam[row * LAND_GLOBAL_MAP_LOD_SIZE_SQRT + col] = static_cast(height); + } } + esm.writeHNT("WNAM", wnam); } - esm.writeHNT("WNAM", wnam); - } - - if (mLandData) - { if (mDataTypes & Land::DATA_VCLR) { esm.writeHNT("VCLR", mLandData->mColours); } @@ -197,7 +194,7 @@ namespace ESM mPlugin = 0; for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i) - mWnam[0] = 0; + mWnam[i] = 0; if (!mLandData) mLandData = new LandData; @@ -338,7 +335,7 @@ namespace ESM Land::Land (const Land& land) : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), mContext (land.mContext), mDataTypes (land.mDataTypes), - mLandData (land.mLandData ? new LandData (*land.mLandData) : 0) + mLandData (land.mLandData ? new LandData (*land.mLandData) : nullptr) { std::copy(land.mWnam, land.mWnam + LAND_GLOBAL_MAP_LOD_SIZE, mWnam); } @@ -364,7 +361,7 @@ namespace ESM const Land::LandData *Land::getLandData (int flags) const { if (!(flags & mDataTypes)) - return 0; + return nullptr; loadData (flags); return mLandData; @@ -400,7 +397,7 @@ namespace ESM if (!mLandData->mDataLoaded) { delete mLandData; - mLandData = 0; + mLandData = nullptr; } } } From 5732b8e026542cdda5f8070b7b61f7872a88fa68 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 4 Apr 2020 17:53:36 +0300 Subject: [PATCH 041/226] Use std::fill instead of loops to reset some arrays to defaults --- components/esm/loadland.cpp | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 85ebc2c9a..3064d0c31 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -75,8 +75,7 @@ namespace ESM mContext = esm.getContext(); mLandData = nullptr; - for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i) - mWnam[i] = 0; + std::fill(std::begin(mWnam), std::end(mWnam), 0); // Skip the land data here. Load it when the cell is loaded. while (esm.hasMoreSubs()) @@ -193,15 +192,13 @@ namespace ESM { mPlugin = 0; - for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i) - mWnam[i] = 0; + std::fill(std::begin(mWnam), std::end(mWnam), 0); if (!mLandData) mLandData = new LandData; mLandData->mHeightOffset = 0; - for (int i = 0; i < LAND_NUM_VERTS; ++i) - mLandData->mHeights[i] = 0; + std::fill(std::begin(mLandData->mHeights), std::end(mLandData->mHeights), 0); mLandData->mMinHeight = 0; mLandData->mMaxHeight = 0; for (int i = 0; i < LAND_NUM_VERTS; ++i) @@ -210,14 +207,8 @@ namespace ESM mLandData->mNormals[i*3+1] = 0; mLandData->mNormals[i*3+2] = 127; } - for (int i = 0; i < LAND_NUM_TEXTURES; ++i) - mLandData->mTextures[i] = 0; - for (int i = 0; i < LAND_NUM_VERTS; ++i) - { - mLandData->mColours[i*3+0] = 255; - mLandData->mColours[i*3+1] = 255; - mLandData->mColours[i*3+2] = 255; - } + std::fill(std::begin(mLandData->mTextures), std::end(mLandData->mTextures), 0); + std::fill(std::begin(mLandData->mColours), std::end(mLandData->mColours), 255); mLandData->mUnk1 = 0; mLandData->mUnk2 = 0; mLandData->mDataLoaded = Land::DATA_VNML | Land::DATA_VHGT | Land::DATA_WNAM | From 1928bebe9857010ebe07c009eecbc9abded80b18 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 4 Apr 2020 19:20:52 +0300 Subject: [PATCH 042/226] Don't use up light duration if the held light is hidden (bug #5352) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/actors.cpp | 24 +++++++++++++--------- apps/openmw/mwrender/animation.hpp | 1 + apps/openmw/mwrender/creatureanimation.hpp | 2 ++ apps/openmw/mwrender/npcanimation.hpp | 2 ++ 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa838a3eb..5d765c1bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -211,6 +211,7 @@ Bug #5326: Formatting issues in the settings.cfg Bug #5328: Skills aren't properly reset for dead actors Bug #5345: Dopey Necromancy does not work due to a missing quote + Bug #5352: Light source items' duration is decremented while they aren't visible Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 8ee248ee5..e81c9a119 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1268,19 +1268,23 @@ namespace MWMechanics // Use time from the player's light if(isPlayer) { - float timeRemaining = heldIter->getClass().getRemainingUsageTime(*heldIter); - - // -1 is infinite light source. Other negative values are treated as 0. - if(timeRemaining != -1.0f) + // But avoid using it up if the light source is hidden + MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); + if (anim && anim->getCarriedLeftShown()) { - timeRemaining -= duration; + float timeRemaining = heldIter->getClass().getRemainingUsageTime(*heldIter); - if(timeRemaining > 0.0f) - heldIter->getClass().setRemainingUsageTime(*heldIter, timeRemaining); - else + // -1 is infinite light source. Other negative values are treated as 0. + if (timeRemaining != -1.0f) { - inventoryStore.remove(*heldIter, 1, ptr); // remove it - return; + timeRemaining -= duration; + if (timeRemaining <= 0.f) + { + inventoryStore.remove(*heldIter, 1, ptr); // remove it + return; + } + + heldIter->getClass().setRemainingUsageTime(*heldIter, timeRemaining); } } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 17e13f047..7b1e1d3e9 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -459,6 +459,7 @@ public: virtual bool useShieldAnimations() const { return false; } virtual void showWeapons(bool showWeapon) {} + virtual bool getCarriedLeftShown() const { return false; } virtual void showCarriedLeft(bool show) {} virtual void setWeaponGroup(const std::string& group, bool relativeDuration) {} virtual void setVampire(bool vampire) {} diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index 524272584..cdcdafe24 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -31,6 +31,8 @@ namespace MWRender virtual void equipmentChanged() { updateParts(); } virtual void showWeapons(bool showWeapon); + + virtual bool getCarriedLeftShown() const { return mShowCarriedLeft; } virtual void showCarriedLeft(bool show); void updateParts(); diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 2d6d3a05f..9e7969976 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -129,6 +129,8 @@ public: virtual void setPitchFactor(float factor) { mPitchFactor = factor; } virtual void showWeapons(bool showWeapon); + + virtual bool getCarriedLeftShown() const { return mShowCarriedLeft; } virtual void showCarriedLeft(bool show); virtual void attachArrow(); From 7545256d1f3256ed609f4956de1ff511057acccf Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 4 Apr 2020 22:39:13 +0400 Subject: [PATCH 043/226] Do not try to launch magic bolt when direction to target is empty (bug #5350) --- CHANGELOG.md | 1 + apps/openmw/mwworld/projectilemanager.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d765c1bc..f43c9f9d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -211,6 +211,7 @@ Bug #5326: Formatting issues in the settings.cfg Bug #5328: Skills aren't properly reset for dead actors Bug #5345: Dopey Necromancy does not work due to a missing quote + Bug #5350: An attempt to launch magic bolt causes "AL error invalid value" error Bug #5352: Light source items' duration is decremented while they aren't visible Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 73f8ac667..c34c9c77f 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -291,6 +291,12 @@ namespace MWWorld if (state.mEffects.mList.empty()) return; + if (!caster.getClass().isActor() && fallbackDirection.length2() <= 0) + { + Log(Debug::Warning) << "Unable to launch magic bolt (direction to target is empty)"; + return; + } + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0)); MWWorld::Ptr ptr = ref.getPtr(); From 3d6fd2818f8a48f9774cec8d2fe0aaaec880d87f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 31 Mar 2020 12:29:58 +0400 Subject: [PATCH 044/226] Support for per-type sound blockers --- apps/openmw/mwbase/soundmanager.hpp | 4 ++-- apps/openmw/mwgui/windowmanagerimp.cpp | 4 ++-- apps/openmw/mwsound/soundmanagerimp.cpp | 23 ++++++++++++++++------- apps/openmw/mwsound/soundmanagerimp.hpp | 6 +++--- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index a6d0d1223..a780e5ccd 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -168,10 +168,10 @@ namespace MWBase ///< Is the given sound currently playing on the given object? /// If you want to check if sound played with playSound is playing, use empty Ptr - virtual void pauseSounds(int types=static_cast(Type::Mask)) = 0; + virtual void pauseSounds(const std::string& blockerId, int types=int(Type::Mask)) = 0; ///< Pauses all currently playing sounds, including music. - virtual void resumeSounds(int types=static_cast(Type::Mask)) = 0; + virtual void resumeSounds(const std::string& blockerId) = 0; ///< Resumes all previously paused sounds. virtual void update(float duration) = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 87a124234..ac2d5f27f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1893,7 +1893,7 @@ namespace MWGui setCursorVisible(false); if (mVideoWidget->hasAudioStream()) - MWBase::Environment::get().getSoundManager()->pauseSounds( + MWBase::Environment::get().getSoundManager()->pauseSounds("Video", ~MWSound::Type::Movie & MWSound::Type::Mask ); osg::Timer frameTimer; @@ -1921,7 +1921,7 @@ namespace MWGui } mVideoWidget->stop(); - MWBase::Environment::get().getSoundManager()->resumeSounds(); + MWBase::Environment::get().getSoundManager()->resumeSounds("Video"); setKeyFocusWidget(oldKeyFocus); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 5c277d09e..d8d59e243 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -50,7 +50,6 @@ namespace MWSound , mListenerPos(0,0,0) , mListenerDir(1,0,0) , mListenerUp(0,0,1) - , mPausedSoundTypes(0) , mUnderwaterSound(nullptr) , mNearWaterSound(nullptr) { @@ -858,27 +857,36 @@ namespace MWSound } - void SoundManager::pauseSounds(int types) + void SoundManager::pauseSounds(const std::string& blockerId, int types) { if(mOutput->isInitialized()) { + auto found = mPausedSoundTypes.find(blockerId); + if (found != mPausedSoundTypes.end() && found->second != types) + resumeSounds(blockerId); + types = types & Type::Mask; mOutput->pauseSounds(types); - mPausedSoundTypes |= types; + mPausedSoundTypes[blockerId] = types; } } - void SoundManager::resumeSounds(int types) + void SoundManager::resumeSounds(const std::string& blockerId) { if(mOutput->isInitialized()) { - types = types & Type::Mask & mPausedSoundTypes; + mPausedSoundTypes.erase(blockerId); + int types = int(Type::Mask); + for (auto& blocker : mPausedSoundTypes) + { + if (blocker.first != blockerId) + types &= ~blocker.second; + } + mOutput->resumeSounds(types); - mPausedSoundTypes &= ~types; } } - void SoundManager::updateRegionSound(float duration) { static float sTimeToNextEnvSound = 0.0f; @@ -1399,5 +1407,6 @@ namespace MWSound mUnusedStreams.push_back(sound); } mActiveTracks.clear(); + mPausedSoundTypes.clear(); } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 786e3a5a7..a8ebfeca3 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -107,7 +107,7 @@ namespace MWSound osg::Vec3f mListenerDir; osg::Vec3f mListenerUp; - int mPausedSoundTypes; + std::unordered_map mPausedSoundTypes; Sound *mUnderwaterSound; Sound *mNearWaterSound; @@ -244,10 +244,10 @@ namespace MWSound virtual bool getSoundPlaying(const MWWorld::ConstPtr &reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? - virtual void pauseSounds(int types); + virtual void pauseSounds(const std::string& blockerId, int types=int(Type::Mask)); ///< Pauses all currently playing sounds, including music. - virtual void resumeSounds(int types); + virtual void resumeSounds(const std::string& blockerId); ///< Resumes all previously paused sounds. virtual void update(float duration); From 2254256db9a00172cc2ae07befea6037bf5309ae Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 31 Mar 2020 13:15:26 +0400 Subject: [PATCH 045/226] Pause both audio and video playback when the game is minimized (feature #4944) --- CHANGELOG.md | 1 + apps/openmw/engine.cpp | 5 ++++ apps/openmw/mwbase/soundmanager.hpp | 3 +++ apps/openmw/mwgui/videowidget.cpp | 15 ++++++++++++ apps/openmw/mwgui/videowidget.hpp | 4 ++++ apps/openmw/mwgui/windowmanagerimp.cpp | 6 +++++ apps/openmw/mwsound/openal_output.cpp | 32 +++++++++++++++++++++++++ apps/openmw/mwsound/openal_output.hpp | 3 +++ apps/openmw/mwsound/sound_output.hpp | 3 +++ apps/openmw/mwsound/soundmanagerimp.cpp | 23 ++++++++++++++++-- apps/openmw/mwsound/soundmanagerimp.hpp | 8 +++++-- 11 files changed, 99 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d765c1bc..e8c5f4de5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -243,6 +243,7 @@ Feature #4882: Support for NiPalette node Feature #4887: Add openmw command option to set initial random seed Feature #4890: Make Distant Terrain configurable + Feature #4944: Pause audio when OpenMW is minimized Feature #4958: Support eight blood types Feature #4962: Add casting animations for magic items Feature #4968: Scalable UI widget skins diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 435db0841..ec8c1e305 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -96,7 +96,12 @@ bool OMW::Engine::frame(float frametime) // 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()) + { + mEnvironment.getSoundManager()->pausePlayback(); return false; + } + else + mEnvironment.getSoundManager()->resumePlayback(); // sound if (mUseSound) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index a780e5ccd..ac3e05ac7 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -174,6 +174,9 @@ namespace MWBase virtual void resumeSounds(const std::string& blockerId) = 0; ///< Resumes all previously paused sounds. + virtual void pausePlayback() = 0; + virtual void resumePlayback() = 0; + virtual void update(float duration) = 0; virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater) = 0; diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index 5dec6e48a..2aea0018d 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -76,6 +76,21 @@ void VideoWidget::stop() mPlayer->close(); } +void VideoWidget::pause() +{ + mPlayer->pause(); +} + +void VideoWidget::resume() +{ + mPlayer->play(); +} + +bool VideoWidget::isPaused() const +{ + return mPlayer->isPaused(); +} + bool VideoWidget::hasAudioStream() { return mPlayer->hasAudioStream(); diff --git a/apps/openmw/mwgui/videowidget.hpp b/apps/openmw/mwgui/videowidget.hpp index dadd1471a..814b9ca73 100644 --- a/apps/openmw/mwgui/videowidget.hpp +++ b/apps/openmw/mwgui/videowidget.hpp @@ -47,6 +47,10 @@ namespace MWGui /// Stop video and free resources (done automatically on destruction) void stop(); + void pause(); + void resume(); + bool isPaused() const; + /// Adjust the coordinates of this video widget relative to its parent, /// based on the dimensions of the playing video. /// @param stretch Stretch the video to fill the whole screen? If false, diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ac2d5f27f..c6bc06f8f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1905,9 +1905,15 @@ namespace MWGui MWBase::Environment::get().getInputManager()->update(dt, true, false); if (!MWBase::Environment::get().getInputManager()->isWindowVisible()) + { + mVideoWidget->pause(); OpenThreads::Thread::microSleep(5000); + } else { + if (mVideoWidget->isPaused()) + mVideoWidget->resume(); + mViewer->eventTraversal(); mViewer->updateTraversal(); mViewer->renderingTraversals(); diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 9c87cac19..595518726 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -1454,6 +1454,38 @@ void OpenAL_Output::pauseSounds(int types) } } +void OpenAL_Output::pauseActiveDevice() +{ + if (mDevice == nullptr) + return; + + if(alcIsExtensionPresent(mDevice, "ALC_SOFT_PAUSE_DEVICE")) + { + LPALCDEVICEPAUSESOFT alcDevicePauseSOFT = 0; + getALCFunc(alcDevicePauseSOFT, mDevice, "alcDevicePauseSOFT"); + alcDevicePauseSOFT(mDevice); + getALCError(mDevice); + } + + alListenerf(AL_GAIN, 0.0f); +} + +void OpenAL_Output::resumeActiveDevice() +{ + if (mDevice == nullptr) + return; + + if(alcIsExtensionPresent(mDevice, "ALC_SOFT_PAUSE_DEVICE")) + { + LPALCDEVICERESUMESOFT alcDeviceResumeSOFT = 0; + getALCFunc(alcDeviceResumeSOFT, mDevice, "alcDeviceResumeSOFT"); + alcDeviceResumeSOFT(mDevice); + getALCError(mDevice); + } + + alListenerf(AL_GAIN, 1.0f); +} + void OpenAL_Output::resumeSounds(int types) { std::vector sources; diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index d91320ab4..6039d97d6 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -92,6 +92,9 @@ namespace MWSound virtual void pauseSounds(int types); virtual void resumeSounds(int types); + virtual void pauseActiveDevice(); + virtual void resumeActiveDevice(); + OpenAL_Output(SoundManager &mgr); virtual ~OpenAL_Output(); }; diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index eb830c8d0..4075e36cc 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -62,6 +62,9 @@ namespace MWSound virtual void pauseSounds(int types) = 0; virtual void resumeSounds(int types) = 0; + virtual void pauseActiveDevice() = 0; + virtual void resumeActiveDevice() = 0; + Sound_Output& operator=(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index d8d59e243..8462f0929 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -52,6 +52,7 @@ namespace MWSound , mListenerUp(0,0,1) , mUnderwaterSound(nullptr) , mNearWaterSound(nullptr) + , mPlaybackPaused(false) { mMasterVolume = Settings::Manager::getFloat("master volume", "Sound"); mMasterVolume = std::min(std::max(mMasterVolume, 0.0f), 1.0f); @@ -856,7 +857,6 @@ namespace MWSound return false; } - void SoundManager::pauseSounds(const std::string& blockerId, int types) { if(mOutput->isInitialized()) @@ -887,6 +887,24 @@ namespace MWSound } } + void SoundManager::pausePlayback() + { + if (mPlaybackPaused) + return; + + mPlaybackPaused = true; + mOutput->pauseActiveDevice(); + } + + void SoundManager::resumePlayback() + { + if (!mPlaybackPaused) + return; + + mPlaybackPaused = false; + mOutput->resumeActiveDevice(); + } + void SoundManager::updateRegionSound(float duration) { static float sTimeToNextEnvSound = 0.0f; @@ -1209,7 +1227,7 @@ namespace MWSound void SoundManager::update(float duration) { - if(!mOutput->isInitialized()) + if(!mOutput->isInitialized() || mPlaybackPaused) return; updateSounds(duration); @@ -1408,5 +1426,6 @@ namespace MWSound } mActiveTracks.clear(); mPausedSoundTypes.clear(); + mPlaybackPaused = false; } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index a8ebfeca3..b2ccc702a 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -112,6 +112,9 @@ namespace MWSound Sound *mUnderwaterSound; Sound *mNearWaterSound; + std::string mNextMusic; + bool mPlaybackPaused; + Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound); Sound_Buffer *lookupSound(const std::string &soundId) const; @@ -134,8 +137,6 @@ namespace MWSound void updateWaterSound(float duration); void updateMusic(float duration); - std::string mNextMusic; - float volumeFromType(Type type) const; SoundManager(const SoundManager &rhs); @@ -250,6 +251,9 @@ namespace MWSound virtual void resumeSounds(const std::string& blockerId); ///< Resumes all previously paused sounds. + virtual void pausePlayback(); + virtual void resumePlayback(); + virtual void update(float duration); virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater); From e4447669018c20e70565402f22aca98a309b126e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 5 Apr 2020 18:10:05 +0400 Subject: [PATCH 046/226] Use enums for blockers IDs instead of strings --- apps/openmw/mwbase/soundmanager.hpp | 12 ++++++++++-- apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++-- apps/openmw/mwsound/soundmanagerimp.cpp | 21 ++++++++++----------- apps/openmw/mwsound/soundmanagerimp.hpp | 6 +++--- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index ac3e05ac7..cc933d4bb 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -14,6 +14,14 @@ namespace MWWorld namespace MWSound { + // Each entry excepts of MaxCount should be used only in one place + enum BlockerType + { + VideoPlayback, + + MaxCount + }; + class Sound; class Stream; struct Sound_Decoder; @@ -168,10 +176,10 @@ namespace MWBase ///< Is the given sound currently playing on the given object? /// If you want to check if sound played with playSound is playing, use empty Ptr - virtual void pauseSounds(const std::string& blockerId, int types=int(Type::Mask)) = 0; + virtual void pauseSounds(MWSound::BlockerType blocker, int types=int(Type::Mask)) = 0; ///< Pauses all currently playing sounds, including music. - virtual void resumeSounds(const std::string& blockerId) = 0; + virtual void resumeSounds(MWSound::BlockerType blocker) = 0; ///< Resumes all previously paused sounds. virtual void pausePlayback() = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index c6bc06f8f..4f36032e1 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1893,9 +1893,10 @@ namespace MWGui setCursorVisible(false); if (mVideoWidget->hasAudioStream()) - MWBase::Environment::get().getSoundManager()->pauseSounds("Video", + MWBase::Environment::get().getSoundManager()->pauseSounds(MWSound::VideoPlayback, ~MWSound::Type::Movie & MWSound::Type::Mask ); + osg::Timer frameTimer; while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest()) { @@ -1927,7 +1928,7 @@ namespace MWGui } mVideoWidget->stop(); - MWBase::Environment::get().getSoundManager()->resumeSounds("Video"); + MWBase::Environment::get().getSoundManager()->resumeSounds(MWSound::VideoPlayback); setKeyFocusWidget(oldKeyFocus); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8462f0929..eff1cf0fd 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -857,30 +857,29 @@ namespace MWSound return false; } - void SoundManager::pauseSounds(const std::string& blockerId, int types) + void SoundManager::pauseSounds(BlockerType blocker, int types) { if(mOutput->isInitialized()) { - auto found = mPausedSoundTypes.find(blockerId); - if (found != mPausedSoundTypes.end() && found->second != types) - resumeSounds(blockerId); + if (mPausedSoundTypes[blocker] != 0) + resumeSounds(blocker); types = types & Type::Mask; mOutput->pauseSounds(types); - mPausedSoundTypes[blockerId] = types; + mPausedSoundTypes[blocker] = types; } } - void SoundManager::resumeSounds(const std::string& blockerId) + void SoundManager::resumeSounds(BlockerType blocker) { if(mOutput->isInitialized()) { - mPausedSoundTypes.erase(blockerId); + mPausedSoundTypes[blocker] = 0; int types = int(Type::Mask); - for (auto& blocker : mPausedSoundTypes) + for (int currentBlocker = 0; currentBlocker < BlockerType::MaxCount; currentBlocker++) { - if (blocker.first != blockerId) - types &= ~blocker.second; + if (currentBlocker != blocker) + types &= ~mPausedSoundTypes[currentBlocker]; } mOutput->resumeSounds(types); @@ -1425,7 +1424,7 @@ namespace MWSound mUnusedStreams.push_back(sound); } mActiveTracks.clear(); - mPausedSoundTypes.clear(); mPlaybackPaused = false; + std::fill(std::begin(mPausedSoundTypes), std::end(mPausedSoundTypes), 0); } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index b2ccc702a..55588d06f 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -107,7 +107,7 @@ namespace MWSound osg::Vec3f mListenerDir; osg::Vec3f mListenerUp; - std::unordered_map mPausedSoundTypes; + int mPausedSoundTypes[BlockerType::MaxCount] = {}; Sound *mUnderwaterSound; Sound *mNearWaterSound; @@ -245,10 +245,10 @@ namespace MWSound virtual bool getSoundPlaying(const MWWorld::ConstPtr &reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? - virtual void pauseSounds(const std::string& blockerId, int types=int(Type::Mask)); + virtual void pauseSounds(MWSound::BlockerType blocker, int types=int(Type::Mask)); ///< Pauses all currently playing sounds, including music. - virtual void resumeSounds(const std::string& blockerId); + virtual void resumeSounds(MWSound::BlockerType blocker); ///< Resumes all previously paused sounds. virtual void pausePlayback(); From c7cedc4e5ac6b49a6cbad1eee46aefdec58e7211 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 6 Apr 2020 09:43:05 +0400 Subject: [PATCH 047/226] Avoid a hack to initialize mIndex --- apps/opencs/model/world/refcollection.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 30ffb310f..1d91bbd59 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -18,10 +18,10 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool CellRef ref; ref.mNew = false; ESM::MovedCellRef mref; + mref.mRefNum.mIndex = 0; bool isDeleted = false; - // hack to initialise mindex - while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, isDeleted, true, &mref)) + while (ESM::Cell::getNextRef(reader, ref, isDeleted, true, &mref)) { // Keep mOriginalCell empty when in modified (as an indicator that the // original cell will always be equal the current cell). @@ -60,6 +60,8 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool else ref.mCell = cell2.mId; + mref.mRefNum.mIndex = 0; + // ignore content file number std::map::iterator iter = cache.begin(); for (; iter != cache.end(); ++iter) From 023c51ad0e2081c22c856f6ac0ede3a063d35ab0 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 6 Apr 2020 16:18:42 +0400 Subject: [PATCH 048/226] Fix int/float conversions in the CSVRender::BrushDraw --- apps/opencs/view/render/brushdraw.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/brushdraw.cpp b/apps/opencs/view/render/brushdraw.cpp index 9a648336e..51084948e 100644 --- a/apps/opencs/view/render/brushdraw.cpp +++ b/apps/opencs/view/render/brushdraw.cpp @@ -21,8 +21,9 @@ CSVRender::BrushDraw::BrushDraw(osg::ref_ptr parentNode, bool textur mBrushDrawNode->addChild(mGeometry); mParentNode->addChild(mBrushDrawNode); if (mTextureMode) - mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_TEXTURE_SIZE; - else mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE; + mLandSizeFactor = static_cast(ESM::Land::REAL_SIZE) / static_cast(ESM::Land::LAND_TEXTURE_SIZE); + else + mLandSizeFactor = static_cast(ESM::Land::REAL_SIZE) / static_cast(ESM::Land::LAND_SIZE); } CSVRender::BrushDraw::~BrushDraw() @@ -121,7 +122,7 @@ void CSVRender::BrushDraw::buildSquareGeometry(const float& radius, const osg::V const float brushOutlineHeight (1.0f); float diameter = radius * 2; - int resolution = (diameter / mLandSizeFactor) * 2; //half a vertex resolution + int resolution = static_cast(2.f * diameter / mLandSizeFactor); //half a vertex resolution float resAdjustedLandSizeFactor = mLandSizeFactor / 2; osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f); From 613189d7d9da8e21f5c07c098edd5dd470cdb4f1 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 7 Apr 2020 12:55:17 +0200 Subject: [PATCH 049/226] bump to 0.47 for new dev cycle --- CHANGELOG.md | 4 ++ CHANGELOG_PR.md | 97 ++++--------------------------------------------- CMakeLists.txt | 2 +- README.md | 2 +- 4 files changed, 13 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c61f0ebc..31a5271db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +0.47.0 +------ + + 0.46.0 ------ diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index 94b0d45ec..a699a3da7 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -5,10 +5,10 @@ *** -0.46.0 +0.47.0 ------ -The OpenMW team is proud to announce the release of version 0.46.0! Grab it from our Downloads Page for all operating systems. ***short summary: shadows, recastnavigation, etc.*** +The OpenMW team is proud to announce the release of version 0.47.0! Grab it from our Downloads Page for all operating systems. ***short summary: XXX *** 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. @@ -18,99 +18,16 @@ Known Issues: - On macOS, launching OpenMW from OpenMW-CS requires OpenMW.app and OpenMW-CS.app to be siblings New Features: -- NIF files which contain an "AvoidNode" are ignored by the pathfinding algorithm (#1724) -- Navmeshes are used for AI pathfinding which should resolve most related issues (#2229) -- Movement input from gamepad joysticks is transformed into analogue values (#3025) -- Sane default values for openmw.cfg file to overcome the original morrowind.ini file (#3442) -- Option to invert x-axis for controllers (#3610) -- Local variables of objects selected in the console can now be directly read and set without explicitly stating the object (#3893) -- In-game option to enable or disable controllers (#3980) -- Sneak mode can be toggled using a controller (#4001) -- Controllers use original engine's default key bindings (#4360) -- Support for sheathing animations, including weapon holstering, scabbards (except for throwing weapons), and quivers for projectiles (#4673) -- Support for "NiRollController" in NIF files to ensure correct rotation of models in "Weapon Sheathing" mod (#4675) -- Support for native animated containers (#4730) -- Support for VAO ("Vertex Array Objects") from OSG 3.5.6 or later (#4756) -- Support for "NiSwitchNode" in NIF files to allow future implementation of native support for extended features like harvestable plants or glowing - windows (#4812) -- Native support for glowing windows (and other daytime-dependent meshes) by adding internal day-night-mode switch (#4836) -- Shadows (#4851) -- More configuration options for in-game water reflections (#4859) -- Command line option to specify a random seed to be used by the game's random-number generator ("RNG") for debugging purposes (#4887) -- Configuration options for distant terrain to adjust quality and performance impact (#4890) +- ? New Editor Features: -- "Faction Ranks" table for "Faction" records (#4209) -- Changes to height editing can be cancelled without changes to data (press esc to cancel) (#4840) -- Land heightmap/shape editing and vertex selection (#5170) -- Deleting instances with a keypress (#5172) -- Dropping objects with keyboard shortcuts (#5274) +- ? Bug Fixes: -- The Mouse Wheel can now be used for key bindings (#2679) -- Scripted Items cannot be stacked anymore to avoid multiple script execution (#2969) -- Stray text after an "else" statement is now ignored, like in the original engine, to handle mods which erroneously use "else if" statements (#3006) -- "SetPos" and "SetPosition" commands now more closely replicate the original engine's behaviour (#3109) -- "Reserved keys [F3], [F4], [F10], and [F11] cannot be assigned to in-game controls anymore (#3282) -- Windows: Reserved [Windows] key cannot be assigned to in-game controls anymore (#3282)" -- Windows: Windows-internal display scaling no longer breaks main menu (#3623) -- Normal maps on mirrored UVs are no longer inverted (#3733) -- Teleporting attempts are now also detected if teleporting is disabled to ensure compatibility with certain mods (#3765) -- Throwing weapons are now correctly rotated during throwing animation when using the "Improved Thrown Weapon Projectiles" mod (#3778) -- Birthsign abilities are no longer restored upon loading to ensure mod compatibility (#4329) -- Player character's model is no longer scaled in first-person mode to prevent issues with arrows obscuring the crosshair (#4383) -- Optional: Ranged attacks now bypass normal weapon resistance or weakness if ammunition and/or bow are appropriate (#4384) -- Fall damage is now also applied when first reloading a savegame and when your character is near the ground in the loaded game (#4411) -- Rain drops are no longer delayed when your character emerges from water (#4540) -- ESM record for prison markers is now hardcoded like, e.g., door markers or temple markers (#4701) -- Loading a savegame which includes active messages no longer crashes the game (#4714) -- An empty pointer actor no longer throws an exception upon exiting the dialogue menu (#4715) -- Inventory paper doll no longer simultaneously displays shield and two-handed weapon during drawing and holstering animations (#4720) -- "Reset actors" command ("ra") no longer tries to reset actors originating from inactive cells, e.g., followers (#4723) -- "Reset actors" command ("ra") now traces reset actors to the ground and also resets fall damage (#4723)" -- Land texture records can now be overwritten by content files to create mods like "Winter in Morrowind" (#4736) -- Disabling collision no longer forces your character to walking speed, but also allows them to run or sneak (#4746) -- NPCs now also use the skeleton associated with their specified model, not only the animations (#4747) -- Sneaking und swimming idle animations are no longer interrupted if your character is in attack-ready state in first-person view (#4750) -- Numerical fallback values with invalid values (e.g., stray text) in the openmw.cfg file no longer crash or break the game (#4768) -- Character's "jumping" flag is no longer unnecessarily reset to ensure compatibility with certain mods, e.g., "Sotha Sil Expanded" (#4775) -- Calling "GetSpellEffects", "GetEffect", or "GetSpell" function on non-actor objects now returns 0, fixing issues with "Sotha Sil Expanded" - (#4778) -- AI values for actors without AIDT ("AI Data") subrecord are now set to zero upon loading instead of filling in "random" values (#4778) -- Running and sneaking are now also considered in in-game checks when your character is in midair, fixing an issue with the "Reign of Fire" mod - (#4797) -- Collision checks are now immediately updated when an object is moved to ensure compatibility with "Sotha Sil Expanded" (#4800) -- Stray special characters before the "begin" statement of a script are now ignored to ensure, once again, compatibility with "Sotha Sil Expanded" - (#4803) -- Particle nodes with an empty "sizes" array are now correctly loaded and no longer cause an exception (#4804) -- Handling of "root bone" and "bip01" nodes in NIF files now matches the original engine's behaviour to ensure compatibility with "Skyrim: Home of - the Nords" (#4810) -- Creatures without specified sound files now fallback to the sounds of the first creature sharing the same model (#4813) -- "Journal" command now also closes a quest when the specified "finish quest" entry has a lower value than the current one for that quest (#4815) -- Spell effects are no longer applied when a spell is successfully absorbed (#4820) -- World state is no longer updated for every in-game hour your character is in jail but only once, which should significantly reduce loading times - (#4823) -- "NiUVController" in NIF files now only affects textures which use the specified "UV Set" index, usually 0; ensures compatibility with "Glow in the Dahrk" (#4827) -- Visual effects ("VFX") for magic effects are now played immediately after the effect is triggered to not accidentally skip the VFX, e.g., when actors drink potions in battle (#4828) -- Meshes with "NiLODNode" or "NiSwitchNode" no longer cause crashes when they contain particles (#4837) -- Localisations can now make use of implicit keywords to create hyperlinks in dialogue text (#4841) -- Actors outside of the processing range no longer appear for one frame when they are spawned (#4860) -- Stray text after a local-variable declaration is now ignored to ensure mod compatibility (#4867) -- Range and default values of AI data fields now match the original engine's ones (#4876) -- "Startup" scripts are now always run once upon starting OpenMW (#4877) -- Stray explicit reference calls for global variables are now ignored to ensure mod compatibility, e.g., with "Sotha Sil Expanded" (#4888) -- Title screen music now loops (#4896) -- "Specular power" is no longer hardcoded but uses the specified value in the shader code (#4916) -- Werewolves can now also attack if their transformation happened during an attack move (#4922) -- Plug-ins with valid empty subrecords are now correctly loaded, which fixes issues with the "DC - Return of Great House Dagoth" mod (#4938) -- Hand-to-hand attacks are now movement-based when the "always use best attack" option is turned off, like in the original engine (#4942) +- ? Editor Bug Fixes: -- Certain numerical fields now only accept unsigned 8-bit integers to avoid overflows (#2987) -- Preview option is now disabled for levelled lists (#4703) -- Opening the "Scene" view from the "Instances" table now also works for exterior cells (#4705) -- Colour fields in interior-cell records now also use the colour picker widget (#4745) -- Cloned, added, or moved instances no longer disappear at load-time (#4748) -- "Clear" function in the content selector no longer tries to execute a "Remove" action on an empty file list (#4757) -- Terrain texture editing for plugins now correctly handles drags from base file (#4904) -- Engine no longer tries to swap buffers of windows which weren't exposed to Qt's window management system (#4911) -- Minimap doesn't get corrupted, when editing new omwgame (#5177) +- ? Miscellaneous: -- Upgraded to FFMPEG3 for media decoding (#4686) -- Optimised terrain code to drastically increase performance with distant terrain enabled (#4695) -- Windows: Added support for NMake to the prebuild script (#4721) +- ? \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d69352c94..b7329ea7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,7 @@ endif() message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 46) +set(OPENMW_VERSION_MINOR 47) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") diff --git a/README.md b/README.md index 6f453a741..07479933e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ OpenMW is an open-source game engine that supports playing Morrowind by Bethesda OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set. -* Version: 0.46.0 +* Version: 0.47.0 * License: GPLv3 (see [LICENSE](https://github.com/OpenMW/openmw/blob/master/LICENSE) for more information) * Website: https://www.openmw.org * IRC: #openmw on irc.freenode.net From 42c7ec8f1c31f323715f73548d93f97041bea3e3 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Tue, 7 Apr 2020 16:56:47 +0300 Subject: [PATCH 050/226] Minor fix to calculation of mLandSizeFactor (-1) --- apps/opencs/view/render/brushdraw.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/brushdraw.cpp b/apps/opencs/view/render/brushdraw.cpp index 51084948e..992540851 100644 --- a/apps/opencs/view/render/brushdraw.cpp +++ b/apps/opencs/view/render/brushdraw.cpp @@ -23,7 +23,7 @@ CSVRender::BrushDraw::BrushDraw(osg::ref_ptr parentNode, bool textur if (mTextureMode) mLandSizeFactor = static_cast(ESM::Land::REAL_SIZE) / static_cast(ESM::Land::LAND_TEXTURE_SIZE); else - mLandSizeFactor = static_cast(ESM::Land::REAL_SIZE) / static_cast(ESM::Land::LAND_SIZE); + mLandSizeFactor = static_cast(ESM::Land::REAL_SIZE) / static_cast(ESM::Land::LAND_SIZE - 1); } CSVRender::BrushDraw::~BrushDraw() From 12044a607bcad2f9d394dbb43b899635b8e29aed Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 10 Apr 2020 15:45:37 +0100 Subject: [PATCH 051/226] 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 f1b2ab9d67dc17fff9ab7972abbcb7e8e7469a15 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 10 Apr 2020 15:47:33 +0100 Subject: [PATCH 052/226] Add .vs to gitignore Apparently Visual Studio puts stuff in a .vs directory now. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e95139b8f..45c87a2c5 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ cmake-build-* files/windows/*.aps ## qt-creator CMakeLists.txt.user* +.vs ## resources data From e1d635cd71799b387d6ed54dd17c1c4f0541fd54 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 10 Apr 2020 17:47:40 +0300 Subject: [PATCH 053/226] Make extra sure groups with special behavior aren't optimized out --- components/nifosg/nifloader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 51d25210d..5a9693740 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -457,7 +457,7 @@ namespace NifOsg { const Nif::NiLODNode* niLodNode = static_cast(nifNode); node = handleLodNode(niLodNode); - dataVariance = osg::Object::STATIC; + dataVariance = osg::Object::DYNAMIC; break; } case Nif::RC_NiSwitchNode: @@ -476,8 +476,8 @@ namespace NifOsg { bool enabled = nifNode->flags & Nif::NiNode::Flag_ActiveCollision; node = new CollisionSwitch(nifNode->trafo.toMatrix(), enabled); - dataVariance = osg::Object::STATIC; - + // This matrix transform must not be combined with another matrix transform. + dataVariance = osg::Object::DYNAMIC; break; } default: From feeab8a335f02415c6d621dc19809b3ccc9d0c98 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 10 Apr 2020 22:19:06 +0300 Subject: [PATCH 054/226] Make adding non-existent global script more forgiving (bug #5364) --- CHANGELOG.md | 1 + apps/openmw/mwscript/globalscripts.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31a5271db..fc07444d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.47.0 ------ + Bug #5364: Script fails/stops if trying to startscript an unknown script 0.46.0 ------ diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 63b0236c8..786cce072 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -28,7 +28,7 @@ namespace MWScript if (iter==mScripts.end()) { - if (const ESM::Script *script = mStore.get().find (name)) + if (const ESM::Script *script = mStore.get().search(name)) { GlobalScriptDesc desc; desc.mRunning = true; @@ -37,6 +37,10 @@ namespace MWScript 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) { From 39a55318b0710de79a7bb67cade2e3e85f859640 Mon Sep 17 00:00:00 2001 From: fredzio Date: Sun, 7 Jul 2019 09:41:32 +0200 Subject: [PATCH 055/226] Remove unused variable --- components/terrain/quadtreeworld.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index f998b7877..05f33078f 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -215,7 +215,6 @@ private: float mMinSize; osg::ref_ptr mRootNode; - osg::ref_ptr mLodCallback; }; QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int compMapResolution, float compMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize) From ad333e88fe04d7ebbd1d3f7378cba1af6690c0e9 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 11 Apr 2020 00:02:12 +0300 Subject: [PATCH 056/226] Handle enchantment autocalc flag as a flag (bug #5363) --- CHANGELOG.md | 1 + apps/esmtool/labels.cpp | 11 +++++++++++ apps/esmtool/labels.hpp | 1 + apps/esmtool/record.cpp | 2 +- apps/opencs/model/world/data.cpp | 2 +- apps/openmw/mwmechanics/enchanting.cpp | 4 ++-- components/esm/loadench.cpp | 2 +- components/esm/loadench.hpp | 8 ++++++-- 8 files changed, 24 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31a5271db..cd4b75630 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.47.0 ------ + Bug #5363: Enchantment autocalc not always 0/1 0.46.0 ------ diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index 6793e770c..5e1437a6d 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -726,6 +727,16 @@ std::string creatureFlags(int flags) return properties; } +std::string enchantmentFlags(int flags) +{ + std::string properties; + if (flags == 0) properties += "[None] "; + if (flags & ESM::Enchantment::Autocalc) properties += "Autocalc "; + if (flags & (0xFFFFFFFF ^ ESM::Enchantment::Autocalc)) properties += "Invalid "; + properties += Misc::StringUtils::format("(0x%08X)", flags); + return properties; +} + std::string landFlags(int flags) { std::string properties; diff --git a/apps/esmtool/labels.hpp b/apps/esmtool/labels.hpp index 007f93316..b06480a97 100644 --- a/apps/esmtool/labels.hpp +++ b/apps/esmtool/labels.hpp @@ -49,6 +49,7 @@ std::string bodyPartFlags(int flags); std::string cellFlags(int flags); std::string containerFlags(int flags); std::string creatureFlags(int flags); +std::string enchantmentFlags(int flags); std::string landFlags(int flags); std::string creatureListFlags(int flags); std::string itemListFlags(int flags); diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index a3f98792c..bd081249a 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -714,7 +714,7 @@ void Record::print() << " (" << mData.mData.mType << ")" << std::endl; std::cout << " Cost: " << mData.mData.mCost << std::endl; std::cout << " Charge: " << mData.mData.mCharge << std::endl; - std::cout << " AutoCalc: " << mData.mData.mAutocalc << std::endl; + std::cout << " Flags: " << enchantmentFlags(mData.mData.mFlags) << std::endl; printEffectList(mData.mEffects); std::cout << " Deleted: " << mIsDeleted << std::endl; } diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index b45df35b1..23720a99a 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -373,7 +373,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat mEnchantments.addColumn (new EnchantmentTypeColumn); mEnchantments.addColumn (new CostColumn); mEnchantments.addColumn (new ChargesColumn2); - mEnchantments.addColumn (new AutoCalcColumn); + mEnchantments.addColumn (new FlagColumn (Columns::ColumnId_AutoCalc, ESM::Enchantment::Autocalc)); // Enchantment effects mEnchantments.addColumn (new NestedParentColumn (Columns::ColumnId_EffectList)); index = mEnchantments.getColumns()-1; diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 9c4290820..fdf25b7c6 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -63,7 +63,7 @@ namespace MWMechanics const MWWorld::Ptr& player = getPlayer(); MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); ESM::Enchantment enchantment; - enchantment.mData.mAutocalc = 0; + enchantment.mData.mFlags = 0; enchantment.mData.mType = mCastStyle; enchantment.mData.mCost = getBaseCastCost(); @@ -219,7 +219,7 @@ namespace MWMechanics if (iter->mEffects.mList.size() != toFind.mEffects.mList.size()) continue; - if (iter->mData.mAutocalc != toFind.mData.mAutocalc + if (iter->mData.mFlags != toFind.mData.mFlags || iter->mData.mType != toFind.mData.mType || iter->mData.mCost != toFind.mData.mCost || iter->mData.mCharge != toFind.mData.mCharge) diff --git a/components/esm/loadench.cpp b/components/esm/loadench.cpp index 78e2fb5aa..8c4dd8c64 100644 --- a/components/esm/loadench.cpp +++ b/components/esm/loadench.cpp @@ -66,7 +66,7 @@ namespace ESM mData.mType = 0; mData.mCost = 0; mData.mCharge = 0; - mData.mAutocalc = 0; + mData.mFlags = 0; mEffects.mList.clear(); } diff --git a/components/esm/loadench.hpp b/components/esm/loadench.hpp index 7b93b519c..b98549ef3 100644 --- a/components/esm/loadench.hpp +++ b/components/esm/loadench.hpp @@ -29,13 +29,17 @@ struct Enchantment ConstantEffect = 3 }; + enum Flags + { + Autocalc = 0x01 + }; + struct ENDTstruct { int mType; int mCost; int mCharge; - int mAutocalc; // Guessing this is 1 if we are supposed to auto - // calculate + int mFlags; }; std::string mId; From de7ecddbb8868ce8d7c14863e47f95d154fb5b7c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 11 Apr 2020 14:42:04 +0300 Subject: [PATCH 057/226] Don't play equip sound for reactivated quick key items (bug #5367) --- CHANGELOG.md | 1 + apps/openmw/mwgui/quickkeysmenu.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31a5271db..1f5865996 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.47.0 ------ + Bug #5367: Selecting a spell on an enchanted item per hotkey always plays the equip sound 0.46.0 ------ diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index ed5328d36..39278f0fa 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -401,7 +401,8 @@ namespace MWGui return; } - MWBase::Environment::get().getWindowManager()->useItem(item); + if (!store.isEquipped(item)) + MWBase::Environment::get().getWindowManager()->useItem(item); MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); // change draw state only if the item is in player's right hand if (rightHand != store.end() && item == *rightHand) @@ -411,8 +412,8 @@ namespace MWGui } else if (key->type == Type_MagicItem) { - // equip, if it can be equipped - if (!item.getClass().getEquipmentSlots(item).first.empty()) + // equip, if it can be equipped and isn't yet equipped + if (!item.getClass().getEquipmentSlots(item).first.empty() && !store.isEquipped(item)) { MWBase::Environment::get().getWindowManager()->useItem(item); From f79e20379bd1958f25be0c3aa96fed8bfc4b7ae2 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 12 Apr 2020 01:55:42 +0300 Subject: [PATCH 058/226] Make sure it's a crime to unlock owned doors --- apps/openmw/mwmechanics/security.cpp | 6 ++++-- apps/openmw/mwmechanics/spellcasting.cpp | 10 +++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index cafc65b99..ab286cbee 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -43,6 +43,8 @@ namespace MWMechanics x *= pickQuality * mFatigueTerm; x += fPickLockMult * lockStrength; + MWBase::Environment::get().getMechanicsManager()->unlockAttempted(mActor, lock); + resultSound = "Open Lock Fail"; if (x <= 0) resultMessage = "#{sLockImpossible}"; @@ -59,7 +61,6 @@ namespace MWMechanics resultMessage = "#{sLockFail}"; } - MWBase::Environment::get().getMechanicsManager()->unlockAttempted(mActor, lock); int uses = lockpick.getClass().getItemHealth(lockpick); --uses; lockpick.getCellRef().setCharge(uses); @@ -84,6 +85,8 @@ namespace MWMechanics x += fTrapCostMult * trapSpellPoints; x *= probeQuality * mFatigueTerm; + MWBase::Environment::get().getMechanicsManager()->unlockAttempted(mActor, trap); + resultSound = "Disarm Trap Fail"; if (x <= 0) resultMessage = "#{sTrapImpossible}"; @@ -101,7 +104,6 @@ namespace MWMechanics resultMessage = "#{sTrapFail}"; } - MWBase::Environment::get().getMechanicsManager()->unlockAttempted(mActor, trap); int uses = probe.getClass().getItemHealth(probe); --uses; probe.getCellRef().setCharge(uses); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 21fa7f369..3f32485de 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -709,6 +709,11 @@ namespace MWMechanics } else if (effectId == ESM::MagicEffect::Open) { + if (!caster.isEmpty()) + { + MWBase::Environment::get().getMechanicsManager()->unlockAttempted(getPlayer(), target); + // Use the player instead of the caster for vanilla crime compatibility + } const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); const ESM::MagicEffect *magiceffect = store.get().find(effectId); MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target); @@ -726,11 +731,10 @@ namespace MWMechanics target.getCellRef().unlock(); } else + { MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f); + } - if (!caster.isEmpty()) - MWBase::Environment::get().getMechanicsManager()->unlockAttempted(getPlayer(), target); - // Use the player instead of the caster for vanilla crime compatibility return true; } } From 6d0022096d77f782e79cfdee89fd8b2eb5618cad Mon Sep 17 00:00:00 2001 From: fredzio Date: Sat, 11 Apr 2020 22:52:29 +0200 Subject: [PATCH 059/226] Bump copyright year in documentation --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 60b25ae57..7653b94ed 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -53,7 +53,7 @@ master_doc = 'index' # General information about the project. project = u'OpenMW' -copyright = u'2017, OpenMW Team' +copyright = u'2020, OpenMW Team' # The version info for the project you're documenting, acts as replacement for From 8e741a0d0b30ac85e58311e0f4dfdf04438157eb Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 11 Apr 2020 23:01:16 -0700 Subject: [PATCH 060/226] fixed tab focus for pinned windows --- apps/openmw/mwgui/keyboardnavigation.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index 3f98b8607..d6922dc83 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -116,6 +116,13 @@ void KeyboardNavigation::onFrame() if (!mEnabled) return; + MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); + if (mode == GM_None) + { + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); + return; + } + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); if (focus == mCurrentFocus) @@ -215,6 +222,10 @@ bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text, b bool KeyboardNavigation::switchFocus(int direction, bool wrap) { + MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); + if (mode == GM_None) + return false; + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); bool isCycle = (direction == D_Prev || direction == D_Next); From 96a447ee3a9d7b8a746167eb71d0990acc36b6ce Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 10 Apr 2020 23:14:00 +0300 Subject: [PATCH 061/226] Show the soulgem soul in count dialog (feature #5362) --- CHANGELOG.md | 1 + apps/openmw/mwclass/misc.cpp | 13 +------------ apps/openmw/mwgui/companionwindow.cpp | 4 +++- apps/openmw/mwgui/container.cpp | 4 +++- apps/openmw/mwgui/inventorywindow.cpp | 4 +++- apps/openmw/mwgui/tooltips.cpp | 14 ++++++++++++++ apps/openmw/mwgui/tooltips.hpp | 3 +++ apps/openmw/mwgui/tradewindow.cpp | 4 +++- 8 files changed, 31 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31a5271db..3bf58395d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.47.0 ------ + Feature #5362: Show the soul gems' trapped soul in count dialog 0.46.0 ------ diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 4eb3eabef..8d3cda6fe 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -141,8 +141,6 @@ namespace MWClass MWGui::ToolTipInfo info; - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - bool gold = isGold(ptr); if (gold) count *= getValue(ptr); @@ -153,18 +151,9 @@ namespace MWClass else // gold displays its count also if it's 1. countString = " (" + std::to_string(count) + ")"; - info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + countString; + info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + countString + MWGui::ToolTips::getSoulString(ptr.getCellRef()); info.icon = ref->mBase->mIcon; - if (ref->mRef.getSoul() != "") - { - const ESM::Creature *creature = store.get().search(ref->mRef.getSoul()); - if (creature && !creature->mName.empty()) - info.caption += " (" + creature->mName + ")"; - else if (creature) - info.caption += " (" + creature->mId + ")"; - } - std::string text; text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, "#{sWeight}"); diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index c069ae885..b3f6e3339 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -16,6 +16,7 @@ #include "draganddrop.hpp" #include "countdialog.hpp" #include "widgets.hpp" +#include "tooltips.hpp" namespace { @@ -86,7 +87,8 @@ void CompanionWindow::onItemSelected(int index) if (count > 1 && !shift) { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - dialog->openCountDialog(object.getClass().getName(object), "#{sTake}", count); + std::string name = object.getClass().getName(object) + MWGui::ToolTips::getSoulString(object.getCellRef()); + dialog->openCountDialog(name, "#{sTake}", count); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem); } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index f0e025a33..444ce4cb1 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -25,6 +25,7 @@ #include "sortfilteritemmodel.hpp" #include "pickpocketitemmodel.hpp" #include "draganddrop.hpp" +#include "tooltips.hpp" namespace MWGui { @@ -79,7 +80,8 @@ namespace MWGui if (count > 1 && !shift) { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - dialog->openCountDialog(object.getClass().getName(object), "#{sTake}", count); + std::string name = object.getClass().getName(object) + MWGui::ToolTips::getSoulString(object.getCellRef()); + dialog->openCountDialog(name, "#{sTake}", count); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 653f03153..6041838f5 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -38,6 +38,7 @@ #include "tradewindow.hpp" #include "draganddrop.hpp" #include "widgets.hpp" +#include "tooltips.hpp" namespace { @@ -298,7 +299,8 @@ namespace MWGui { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); std::string message = mTrading ? "#{sQuanityMenuMessage01}" : "#{sTake}"; - dialog->openCountDialog(object.getClass().getName(object), message, count); + std::string name = object.getClass().getName(object) + MWGui::ToolTips::getSoulString(object.getCellRef()); + dialog->openCountDialog(name, message, count); dialog->eventOkClicked.clear(); if (mTrading) dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::sellItem); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index b86eba651..e3250e5fe 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -647,6 +647,20 @@ namespace MWGui return " (" + MyGUI::utility::toString(value) + ")"; } + std::string ToolTips::getSoulString(const MWWorld::CellRef& cellref) + { + std::string soul = cellref.getSoul(); + if (soul.empty()) + return std::string(); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Creature *creature = store.get().search(soul); + if (!creature) + return std::string(); + if (creature->mName.empty()) + return " (" + creature->mId + ")"; + return " (" + creature->mName + ")"; + } + std::string ToolTips::getCellRefString(const MWWorld::CellRef& cellref) { std::string ret; diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index afdc7dec0..d7bb87bdb 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -81,6 +81,9 @@ namespace MWGui static std::string getCountString(const int value); ///< @return blank string if count is 1, or else " (value)" + static std::string getSoulString(const MWWorld::CellRef& cellref); + ///< Returns a string containing the name of the creature that the ID in the cellref's soul field belongs to. + static std::string getCellRefString(const MWWorld::CellRef& cellref); ///< Returns a string containing debug tooltip information about the given cellref. diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index b102b13ce..672ccbd06 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -27,6 +27,7 @@ #include "tradeitemmodel.hpp" #include "countdialog.hpp" #include "controllers.hpp" +#include "tooltips.hpp" namespace { @@ -201,7 +202,8 @@ namespace MWGui { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); std::string message = "#{sQuanityMenuMessage02}"; - dialog->openCountDialog(object.getClass().getName(object), message, count); + std::string name = object.getClass().getName(object) + MWGui::ToolTips::getSoulString(object.getCellRef()); + dialog->openCountDialog(name, message, count); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &TradeWindow::sellItem); mItemToSell = mSortModel->mapToSource(index); From 9dbdbbaea7c37f484761ba8119a385940df82515 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 7 Apr 2020 02:06:58 +0300 Subject: [PATCH 062/226] Only clear dialogue history if the dialogue window is closed (bug #5358) --- CHANGELOG.md | 1 + apps/openmw/mwgui/dialogue.cpp | 16 +++++++++++++--- apps/openmw/mwgui/dialogue.hpp | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31a5271db..2bf9aac27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.47.0 ------ + Bug #5358: ForceGreeting always resets the dialogue window completely 0.46.0 ------ diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 6b400c172..bb3f3e4ba 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -354,7 +354,10 @@ namespace MWGui void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) { if (exit()) + { + resetHistory(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + } } void DialogueWindow::onSelectListItem(const std::string& topic, int id) @@ -418,9 +421,7 @@ namespace MWGui bool sameActor = (mPtr == actor); if (!sameActor) { - for (DialogueText* text : mHistoryContents) - delete text; - mHistoryContents.clear(); + // The history is not reset here mKeywords.clear(); mTopicsList->clear(); for (Link* link : mLinks) @@ -475,6 +476,13 @@ namespace MWGui mDeleteLater.clear(); } + void DialogueWindow::resetHistory() + { + for (DialogueText* text : mHistoryContents) + delete text; + mHistoryContents.clear(); + } + void DialogueWindow::setKeywords(std::list keyWords) { if (mKeywords == keyWords && isCompanion() == mIsCompanion) @@ -655,6 +663,7 @@ namespace MWGui void DialogueWindow::onGoodbyeActivated() { + resetHistory(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); resetReference(); @@ -709,6 +718,7 @@ namespace MWGui void DialogueWindow::onReferenceUnavailable() { + resetHistory(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 2538602c6..77c767ed8 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -156,6 +156,7 @@ namespace MWGui void updateDisposition(); void restock(); void deleteLater(); + void resetHistory(); bool mIsCompanion; std::list mKeywords; From 07779d7fcf6e140e40fe074c572d11571aa46687 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 12 Apr 2020 13:14:32 +0300 Subject: [PATCH 063/226] Properly utilize creature levelled list's scale (bug #5369) --- CHANGELOG.md | 1 + apps/openmw/mwclass/creaturelevlist.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31a5271db..fbb2fdfdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.47.0 ------ + Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures 0.46.0 ------ diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index 1f47b483f..2f3ac0d1e 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -125,6 +125,7 @@ namespace MWClass const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); MWWorld::ManualRef manualRef(store, id); manualRef.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition()); + manualRef.getPtr().getCellRef().setScale(ptr.getCellRef().getScale()); MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(manualRef.getPtr(), ptr.getCell() , ptr.getCellRef().getPosition()); customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId(); customData.mSpawn = false; From a34fe1d9b288c4974e8fb7822f3bdfd4e6631915 Mon Sep 17 00:00:00 2001 From: Cody Date: Sun, 12 Apr 2020 10:18:07 -0700 Subject: [PATCH 064/226] proper game mode detection --- apps/openmw/mwgui/keyboardnavigation.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index d6922dc83..6dd66029b 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -116,8 +116,7 @@ void KeyboardNavigation::onFrame() if (!mEnabled) return; - MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); - if (mode == GM_None) + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) { MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); return; @@ -222,8 +221,7 @@ bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text, b bool KeyboardNavigation::switchFocus(int direction, bool wrap) { - MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); - if (mode == GM_None) + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) return false; MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); From 4e22f03ece5d3e8d4a6f374720f00854cf63171b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 13 Apr 2020 20:17:16 +0300 Subject: [PATCH 065/226] Revise esmtool cell reference output --- apps/esmtool/esmtool.cpp | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 4734e1c56..59095c1df 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -258,18 +258,37 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) if(quiet) continue; std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl; - std::cout << " ID: '" << ref.mRefID << "'\n"; - std::cout << " Owner: '" << ref.mOwner << "'\n"; - std::cout << " Global: '" << ref.mGlobalVariable << "'" << std::endl; - std::cout << " Faction: '" << ref.mFaction << "'" << std::endl; - std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl; - std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; - std::cout << " Uses/health: '" << ref.mChargeInt << "'\n"; - std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; - std::cout << " Blocked: '" << static_cast(ref.mReferenceBlocked) << "'" << std::endl; + std::cout << " ID: " << ref.mRefID << std::endl; + std::cout << " Position: (" << ref.mPos.pos[0] << ", " << ref.mPos.pos[1] << ", " << ref.mPos.pos[2] << ")" << std::endl; + if (ref.mScale != 1.f) + std::cout << " Scale: " << ref.mScale << std::endl; + if (!ref.mOwner.empty()) + std::cout << " Owner: " << ref.mOwner << std::endl; + if (!ref.mGlobalVariable.empty()) + std::cout << " Global: " << ref.mGlobalVariable << std::endl; + if (!ref.mFaction.empty()) + std::cout << " Faction: " << ref.mFaction << std::endl; + if (!ref.mFaction.empty() || ref.mFactionRank != -2) + std::cout << " Faction rank: " << ref.mFactionRank << std::endl; + std::cout << " Enchantment charge: " << ref.mEnchantmentCharge << std::endl; + std::cout << " Uses/health: " << ref.mChargeInt << std::endl; + std::cout << " Gold value: " << ref.mGoldValue << std::endl; + std::cout << " Blocked: " << static_cast(ref.mReferenceBlocked) << std::endl; std::cout << " Deleted: " << deleted << std::endl; if (!ref.mKey.empty()) - std::cout << " Key: '" << ref.mKey << "'" << std::endl; + std::cout << " Key: " << ref.mKey << std::endl; + std::cout << " Lock level: " << ref.mLockLevel << std::endl; + if (!ref.mTrap.empty()) + std::cout << " Trap: " << ref.mTrap << std::endl; + if (!ref.mSoul.empty()) + std::cout << " Soul: " << ref.mSoul << std::endl; + if (ref.mTeleport) + { + std::cout << " Destination position: (" << ref.mDoorDest.pos[0] << ", " + << ref.mDoorDest.pos[1] << ", " << ref.mDoorDest.pos[2] << ")" << std::endl; + if (!ref.mDestCell.empty()) + std::cout << " Destination cell: " << ref.mDestCell << std::endl; + } } } From 5dc8da5f672f73a844864493d4938a7962a7275c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 12 Apr 2020 14:40:06 +0300 Subject: [PATCH 066/226] Only disarm traps with keys when the door is locked (bug #5370) --- CHANGELOG.md | 1 + apps/openmw/mwclass/container.cpp | 5 ++--- apps/openmw/mwclass/door.cpp | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 675766ad9..e49ba92ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Bug #5364: Script fails/stops if trying to startscript an unknown script 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 Feature #5362: Show the soul gems' trapped soul in count dialog 0.46.0 diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 3adeb19d9..b4b068c91 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -177,11 +177,10 @@ namespace MWClass } } - if ((isLocked || isTrapped) && hasKey) + if (isLocked && hasKey) { MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}"); - if(isLocked) - ptr.getCellRef().unlock(); + ptr.getCellRef().unlock(); // using a key disarms the trap if(isTrapped) { diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 7d1c1d38a..19ba5351a 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -158,12 +158,11 @@ namespace MWClass } } - if ((isLocked || isTrapped) && hasKey) + if (isLocked && hasKey) { if(actor == MWMechanics::getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox(keyName + " #{sKeyUsed}"); - if(isLocked) - ptr.getCellRef().unlock(); //Call the function here. because that makes sense. + ptr.getCellRef().unlock(); //Call the function here. because that makes sense. // using a key disarms the trap if(isTrapped) { From 36d82abafdf8022a3e51d7f62830687a6f87b821 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 13 Apr 2020 21:09:06 +0300 Subject: [PATCH 067/226] Make book layer have lower priority than the layer for other GUI windows --- files/mygui/openmw_layers.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 24595881a..a98efe07c 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -6,10 +6,10 @@ - + From 208a50340db5506dded60c05272ce9692773baa9 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 13 Apr 2020 20:45:07 +0200 Subject: [PATCH 068/226] Specialize DetourNavigator::ObjectId ctor for pointers To fix msvc error: components\detournavigator\objectid.hpp(14,13): error C2440: 'reinterpret_cast': cannot convert from 'const T' to 'size_t' with [ T=unsigned long ] --- .../tilecachedrecastmeshmanager.cpp | 54 +++++++++---------- components/detournavigator/objectid.hpp | 2 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index 5275d9119..e44ae4878 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -61,15 +61,15 @@ namespace { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); - EXPECT_TRUE(manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); + EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false) { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); - manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); - EXPECT_FALSE(manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); + manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_return_changed_tiles) @@ -77,9 +77,9 @@ namespace TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); - manager.addObject(ObjectId(1ul), boxShape, transform, AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, transform, AreaType::AreaType_ground); EXPECT_THAT( - manager.updateObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground), + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground), ElementsAre(TilePosition(-1, -1), TilePosition(-1, 0), TilePosition(0, -1), TilePosition(0, 0), TilePosition(1, -1), TilePosition(1, 0)) ); @@ -89,9 +89,9 @@ namespace { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); - manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ( - manager.updateObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground), + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground), std::vector() ); } @@ -100,7 +100,7 @@ namespace { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); - manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); @@ -111,7 +111,7 @@ namespace { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); - manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ(manager.getMesh(TilePosition(1, 0)), nullptr); } @@ -121,13 +121,13 @@ namespace const btBoxShape boxShape(btVector3(20, 20, 100)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); - manager.addObject(ObjectId(1ul), boxShape, transform, AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, transform, AreaType::AreaType_ground); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(1, -1)), nullptr); - manager.updateObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); @@ -140,11 +140,11 @@ namespace const btBoxShape boxShape(btVector3(20, 20, 100)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); - manager.addObject(ObjectId(1ul), boxShape, transform, AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, transform, AreaType::AreaType_ground); EXPECT_EQ(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(-1, 0)), nullptr); - manager.updateObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ(manager.getMesh(TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(1, -1)), nullptr); } @@ -153,8 +153,8 @@ namespace { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); - manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); - manager.removeObject(ObjectId(1ul)); + manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.removeObject(ObjectId(&boxShape)); EXPECT_EQ(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(0, -1)), nullptr); @@ -166,13 +166,13 @@ namespace TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); - manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr); - manager.updateObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); @@ -184,7 +184,7 @@ namespace TileCachedRecastMeshManager manager(mSettings); const auto initialRevision = manager.getRevision(); const btBoxShape boxShape(btVector3(20, 20, 100)); - manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ(manager.getRevision(), initialRevision + 1); } @@ -192,9 +192,9 @@ namespace { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); - manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const auto beforeAddRevision = manager.getRevision(); - manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ(manager.getRevision(), beforeAddRevision); } @@ -203,9 +203,9 @@ namespace TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); - manager.addObject(ObjectId(1ul), boxShape, transform, AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, transform, AreaType::AreaType_ground); const auto beforeUpdateRevision = manager.getRevision(); - manager.updateObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1); } @@ -213,9 +213,9 @@ namespace { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); - manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const auto beforeUpdateRevision = manager.getRevision(); - manager.updateObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision); } @@ -223,9 +223,9 @@ namespace { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); - manager.addObject(ObjectId(1ul), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const auto beforeRemoveRevision = manager.getRevision(); - manager.removeObject(ObjectId(1ul)); + manager.removeObject(ObjectId(&boxShape)); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1); } @@ -233,7 +233,7 @@ namespace { TileCachedRecastMeshManager manager(mSettings); const auto beforeRemoveRevision = manager.getRevision(); - manager.removeObject(ObjectId(1ul)); + manager.removeObject(ObjectId(&manager)); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision); } } diff --git a/components/detournavigator/objectid.hpp b/components/detournavigator/objectid.hpp index 3b56924b1..6ddcc9169 100644 --- a/components/detournavigator/objectid.hpp +++ b/components/detournavigator/objectid.hpp @@ -10,7 +10,7 @@ namespace DetourNavigator { public: template - explicit ObjectId(const T value) throw() + explicit ObjectId(T* value) throw() : mValue(reinterpret_cast(value)) { } From fbcc8ef046b8a2ad122fd4af895b29e590c5109f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 14 Apr 2020 08:49:14 +0300 Subject: [PATCH 069/226] Reset dialogue history when the window can't track what closed it --- apps/openmw/mwgui/dialogue.cpp | 6 ++---- apps/openmw/mwgui/dialogue.hpp | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index bb3f3e4ba..bb40bea33 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -355,7 +355,6 @@ namespace MWGui { if (exit()) { - resetHistory(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } } @@ -476,8 +475,9 @@ namespace MWGui mDeleteLater.clear(); } - void DialogueWindow::resetHistory() + void DialogueWindow::onClose() { + // Reset history for (DialogueText* text : mHistoryContents) delete text; mHistoryContents.clear(); @@ -663,7 +663,6 @@ namespace MWGui void DialogueWindow::onGoodbyeActivated() { - resetHistory(); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); resetReference(); @@ -718,7 +717,6 @@ namespace MWGui void DialogueWindow::onReferenceUnavailable() { - resetHistory(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 77c767ed8..2c3fb1a44 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -133,6 +133,8 @@ namespace MWGui void updateTopics(); + void onClose(); + protected: void updateTopicsPane(); bool isCompanion(const MWWorld::Ptr& actor); @@ -156,7 +158,6 @@ namespace MWGui void updateDisposition(); void restock(); void deleteLater(); - void resetHistory(); bool mIsCompanion; std::list mKeywords; From bbed9a70c5f6f1716e2d027bfd373d7027399462 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 14 Apr 2020 17:49:03 +0300 Subject: [PATCH 070/226] Ignore traps of initially unlocked refs with assigned keys --- components/esm/cellref.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 69841528f..ab6ba2754 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -130,7 +130,10 @@ void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted) } if (mLockLevel == 0 && !mKey.empty()) + { mLockLevel = UnbreakableLock; + mTrap.clear(); + } } void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory, bool isDeleted) const From ec7976522d63e285c95a318992736344bf4e2645 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 15 Apr 2020 18:54:36 +0200 Subject: [PATCH 071/226] Fix issue number in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 675766ad9..6eff7dee0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -223,7 +223,7 @@ Bug #5345: Dopey Necromancy does not work due to a missing quote Bug #5350: An attempt to launch magic bolt causes "AL error invalid value" error Bug #5352: Light source items' duration is decremented while they aren't visible - Feature #1774: Handle AvoidNode + Feature #1724: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls Feature #3442: Default values for fallbacks from ini file From 6e2a651252727e655a8e0579948e39eb7241b11d Mon Sep 17 00:00:00 2001 From: Julien Valentin Date: Thu, 16 Apr 2020 02:48:42 +0200 Subject: [PATCH 072/226] fix particle alpha --- components/nifosg/nifloader.cpp | 8 -------- components/nifosg/particle.cpp | 3 +++ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 5a9693740..07cbaee37 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1809,14 +1809,6 @@ namespace NifOsg if (specFlags == 0) mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f,0.f,0.f,0.f)); - // Particles don't have normals, so can't be diffuse lit. - if (particleMaterial) - { - // NB ignoring diffuse.a() - mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,1)); - mat->setColorMode(osg::Material::AMBIENT); - } - if (lightmode == 0) { osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index c1ccede1a..2a79af1c8 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -159,8 +159,11 @@ void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* d { float time = static_cast(particle->getAge()/particle->getLifeTime()); osg::Vec4f color = mData.interpKey(time); + float alpha = color.a(); + color.a() = 1.0f; particle->setColorRange(osgParticle::rangev4(color, color)); + particle->setAlphaRange(osgParticle::rangef(alpha, alpha)); } GravityAffector::GravityAffector(const Nif::NiGravity *gravity) From c3cc8ee1e17ff3b4faa2ec8b26831b90716c1fde Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 17 Apr 2020 14:08:29 +0300 Subject: [PATCH 073/226] Disable specular lighting for 4.0.0.2 and earlier files --- components/nifosg/nifloader.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 5a9693740..74cdcefe3 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1705,7 +1705,8 @@ namespace NifOsg { osg::StateSet* stateset = node->getOrCreateStateSet(); - int specFlags = 0; // Specular is disabled by default, even if there's a specular color in the NiMaterialProperty + // Specular lighting is enabled by default, but there's a quirk... + int specFlags = 1; osg::ref_ptr mat (new osg::Material); mat->setColorMode(hasVertexColors ? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF); @@ -1723,6 +1724,7 @@ namespace NifOsg { case Nif::RC_NiSpecularProperty: { + // Specular property can turn specular lighting off. specFlags = property->flags; break; } @@ -1806,7 +1808,8 @@ namespace NifOsg } } - if (specFlags == 0) + // While NetImmerse and Gamebryo support specular lighting, Morrowind has its support disabled. + if (mVersion <= Nif::NIFFile::NIFVersion::VER_MW || specFlags == 0) mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f,0.f,0.f,0.f)); // Particles don't have normals, so can't be diffuse lit. From 9698c21b367d059cfdab07ada7660fd5f6ce663b Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 17 Apr 2020 13:50:54 +0200 Subject: [PATCH 074/226] 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 075/226] 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 1cf20363868c88ff0df498330999f8cf884a50fd Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 18 Apr 2020 02:38:20 +0100 Subject: [PATCH 076/226] Don't add empty PrimitiveSets (prevent undefined behaviour) --- components/sceneutil/pathgridutil.cpp | 218 +++++++++++++------------- 1 file changed, 112 insertions(+), 106 deletions(-) diff --git a/components/sceneutil/pathgridutil.cpp b/components/sceneutil/pathgridutil.cpp index 58c5d8ad2..ed6894dfc 100644 --- a/components/sceneutil/pathgridutil.cpp +++ b/components/sceneutil/pathgridutil.cpp @@ -85,91 +85,95 @@ namespace SceneUtil osg::ref_ptr gridGeometry = new osg::Geometry(); - osg::ref_ptr vertices = new osg::Vec3Array(VertexCount); - osg::ref_ptr colors = new osg::Vec4Array(ColorCount); - osg::ref_ptr pointIndices = - new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, PointIndexCount); - osg::ref_ptr lineIndices = - new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, EdgeIndexCount); - - // Add each point/node - for (unsigned short pointIndex = 0; pointIndex < PointCount; ++pointIndex) + if (PointIndexCount || EdgeIndexCount) { - const ESM::Pathgrid::Point& point = pathgrid.mPoints[pointIndex]; - osg::Vec3f position = osg::Vec3f(point.mX, point.mY, point.mZ); - - unsigned short vertexOffset = pointIndex * DiamondTotalVertexCount; - unsigned short indexOffset = pointIndex * DiamondIndexCount; - - // Point - for (unsigned short i = 0; i < DiamondVertexCount; ++i) + osg::ref_ptr vertices = new osg::Vec3Array(VertexCount); + osg::ref_ptr colors = new osg::Vec4Array(ColorCount); + osg::ref_ptr pointIndices = + new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, PointIndexCount); + osg::ref_ptr lineIndices = + new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, EdgeIndexCount); + + // Add each point/node + for (unsigned short pointIndex = 0; pointIndex < PointCount; ++pointIndex) { - (*vertices)[vertexOffset + i] = position + DiamondPoints[i]; - (*colors)[vertexOffset + i] = DiamondColors[i]; + const ESM::Pathgrid::Point& point = pathgrid.mPoints[pointIndex]; + osg::Vec3f position = osg::Vec3f(point.mX, point.mY, point.mZ); + + unsigned short vertexOffset = pointIndex * DiamondTotalVertexCount; + unsigned short indexOffset = pointIndex * DiamondIndexCount; + + // Point + for (unsigned short i = 0; i < DiamondVertexCount; ++i) + { + (*vertices)[vertexOffset + i] = position + DiamondPoints[i]; + (*colors)[vertexOffset + i] = DiamondColors[i]; + } + + for (unsigned short i = 0; i < DiamondIndexCount; ++i) + { + pointIndices->setElement(indexOffset + i, vertexOffset + DiamondIndices[i]); + } + + // Connectors + vertexOffset += DiamondVertexCount; + for (unsigned short i = 0; i < DiamondConnectorVertexCount; ++i) + { + (*vertices)[vertexOffset + i] = position + DiamondPoints[DiamondConnectorVertices[i]]; + (*colors)[vertexOffset + i] = DiamondEdgeColor; + } } - for (unsigned short i = 0; i < DiamondIndexCount; ++i) - { - pointIndices->setElement(indexOffset + i, vertexOffset + DiamondIndices[i]); - } + // Add edges + unsigned short lineIndex = 0; - // Connectors - vertexOffset += DiamondVertexCount; - for (unsigned short i = 0; i < DiamondConnectorVertexCount; ++i) + for (ESM::Pathgrid::EdgeList::const_iterator edge = pathgrid.mEdges.begin(); + edge != pathgrid.mEdges.end(); ++edge) { - (*vertices)[vertexOffset + i] = position + DiamondPoints[DiamondConnectorVertices[i]]; - (*colors)[vertexOffset + i] = DiamondEdgeColor; + if (edge->mV0 == edge->mV1 || edge->mV0 < 0 || edge->mV0 >= PointCount || + edge->mV1 < 0 || edge->mV1 >= PointCount) + continue; + + const ESM::Pathgrid::Point& from = pathgrid.mPoints[edge->mV0]; + const ESM::Pathgrid::Point& to = pathgrid.mPoints[edge->mV1]; + + osg::Vec3f fromPos = osg::Vec3f(from.mX, from.mY, from.mZ); + osg::Vec3f toPos = osg::Vec3f(to.mX, to.mY, to.mZ); + osg::Vec3f dir = toPos - fromPos; + dir.normalize(); + + osg::Quat rot = osg::Quat(-osg::PI / 2, osg::Vec3(0, 0, 1)); + dir = rot * dir; + + unsigned short diamondIndex = 0; + if (dir.isNaN()) + diamondIndex = 0; + else if (dir.y() >= 0 && dir.x() > 0) + diamondIndex = 3; + else if (dir.x() <= 0 && dir.y() > 0) + diamondIndex = 1; + else if (dir.y() <= 0 && dir.x() < 0) + diamondIndex = 0; + else if (dir.x() >= 0 && dir.y() < 0) + diamondIndex = 2; + + unsigned short fromIndex = static_cast(edge->mV0); + unsigned short toIndex = static_cast(edge->mV1); + + lineIndices->setElement(lineIndex++, fromIndex * DiamondTotalVertexCount + DiamondVertexCount + diamondIndex); + lineIndices->setElement(lineIndex++, toIndex * DiamondTotalVertexCount + DiamondVertexCount + diamondIndex); } - } - // Add edges - unsigned short lineIndex = 0; + lineIndices->resize(lineIndex); - for (ESM::Pathgrid::EdgeList::const_iterator edge = pathgrid.mEdges.begin(); - edge != pathgrid.mEdges.end(); ++edge) - { - if (edge->mV0 == edge->mV1 || edge->mV0 < 0 || edge->mV0 >= PointCount || - edge->mV1 < 0 || edge->mV1 >= PointCount) - continue; - - const ESM::Pathgrid::Point& from = pathgrid.mPoints[edge->mV0]; - const ESM::Pathgrid::Point& to = pathgrid.mPoints[edge->mV1]; - - osg::Vec3f fromPos = osg::Vec3f(from.mX, from.mY, from.mZ); - osg::Vec3f toPos = osg::Vec3f(to.mX, to.mY, to.mZ); - osg::Vec3f dir = toPos - fromPos; - dir.normalize(); - - osg::Quat rot = osg::Quat(-osg::PI / 2, osg::Vec3(0, 0, 1)); - dir = rot * dir; - - unsigned short diamondIndex = 0; - if (dir.isNaN()) - diamondIndex = 0; - else if (dir.y() >= 0 && dir.x() > 0) - diamondIndex = 3; - else if (dir.x() <= 0 && dir.y() > 0) - diamondIndex = 1; - else if (dir.y() <= 0 && dir.x() < 0) - diamondIndex = 0; - else if (dir.x() >= 0 && dir.y() < 0) - diamondIndex = 2; - - unsigned short fromIndex = static_cast(edge->mV0); - unsigned short toIndex = static_cast(edge->mV1); - - lineIndices->setElement(lineIndex++, fromIndex * DiamondTotalVertexCount + DiamondVertexCount + diamondIndex); - lineIndices->setElement(lineIndex++, toIndex * DiamondTotalVertexCount + DiamondVertexCount + diamondIndex); + gridGeometry->setVertexArray(vertices); + gridGeometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); + if (PointIndexCount) + gridGeometry->addPrimitiveSet(pointIndices); + if (EdgeIndexCount) + gridGeometry->addPrimitiveSet(lineIndices); + gridGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); } - - lineIndices->resize(lineIndex); - - gridGeometry->setVertexArray(vertices); - gridGeometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); - gridGeometry->addPrimitiveSet(pointIndices); - gridGeometry->addPrimitiveSet(lineIndices); - gridGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - return gridGeometry; } @@ -184,44 +188,46 @@ namespace SceneUtil osg::ref_ptr wireframeGeometry = new osg::Geometry(); - osg::ref_ptr vertices = new osg::Vec3Array(VertexCount); - osg::ref_ptr colors = new osg::Vec4Array(ColorCount); - osg::ref_ptr indices = - new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, IndexCount); - - osg::Vec3f wireOffset = osg::Vec3f(0, 0, (1 - DiamondWireframeScalar) * DiamondHalfHeight); - - // Add each point/node - for (unsigned short it = 0; it < PointCount; ++it) + if (IndexCount) { - const ESM::Pathgrid::Point& point = pathgrid.mPoints[selected[it]]; - osg::Vec3f position = osg::Vec3f(point.mX, point.mY, point.mZ) + wireOffset; + osg::ref_ptr vertices = new osg::Vec3Array(VertexCount); + osg::ref_ptr colors = new osg::Vec4Array(ColorCount); + osg::ref_ptr indices = + new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, IndexCount); - unsigned short vertexOffset = it * DiamondVertexCount; - unsigned short indexOffset = it * DiamondWireframeIndexCount; + osg::Vec3f wireOffset = osg::Vec3f(0, 0, (1 - DiamondWireframeScalar) * DiamondHalfHeight); - // Point - for (unsigned short i = 0; i < DiamondVertexCount; ++i) + // Add each point/node + for (unsigned short it = 0; it < PointCount; ++it) { - (*vertices)[vertexOffset + i] = position + DiamondPoints[i] * DiamondWireframeScalar; - - if (it == PointCount - 1) - (*colors)[vertexOffset + i] = DiamondFocusWireColor; - else - (*colors)[vertexOffset + i] = DiamondWireColor; + const ESM::Pathgrid::Point& point = pathgrid.mPoints[selected[it]]; + osg::Vec3f position = osg::Vec3f(point.mX, point.mY, point.mZ) + wireOffset; + + unsigned short vertexOffset = it * DiamondVertexCount; + unsigned short indexOffset = it * DiamondWireframeIndexCount; + + // Point + for (unsigned short i = 0; i < DiamondVertexCount; ++i) + { + (*vertices)[vertexOffset + i] = position + DiamondPoints[i] * DiamondWireframeScalar; + + if (it == PointCount - 1) + (*colors)[vertexOffset + i] = DiamondFocusWireColor; + else + (*colors)[vertexOffset + i] = DiamondWireColor; + } + + for (unsigned short i = 0; i < DiamondWireframeIndexCount; ++i) + { + indices->setElement(indexOffset + i, vertexOffset + DiamondWireframeIndices[i]); + } } - for (unsigned short i = 0; i < DiamondWireframeIndexCount; ++i) - { - indices->setElement(indexOffset + i, vertexOffset + DiamondWireframeIndices[i]); - } + wireframeGeometry->setVertexArray(vertices); + wireframeGeometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); + wireframeGeometry->addPrimitiveSet(indices); + wireframeGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); } - - wireframeGeometry->setVertexArray(vertices); - wireframeGeometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); - wireframeGeometry->addPrimitiveSet(indices); - wireframeGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - return wireframeGeometry; } From bbf6b4e51779a4b7ff45318f199b8864e101b468 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 18 Apr 2020 12:30:01 +0300 Subject: [PATCH 077/226] Don't radialize underwater fog --- files/shaders/water_fragment.glsl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index 6e51a2082..86edd8698 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -203,17 +203,20 @@ void main(void) float ior = (cameraPos.z>0.0)?(1.333/1.0):(1.0/1.333); // air to water; water to air float fresnel = clamp(fresnel_dielectric(vVec, normal, ior), 0.0, 1.0); + float radialise = 1.0; + #if @radialFog float radialDepth = distance(position.xyz, cameraPos); - float radialize = radialDepth / linearDepth; -#else - float radialize = 1.0; + // TODO: Figure out how to properly radialise refraction depth and thus underwater fog + // while avoiding oddities when the water plane is close to the clipping plane + // radialise = radialDepth / linearDepth; #endif + vec2 screenCoordsOffset = normal.xy * REFL_BUMP; #if REFRACTION - float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x) * radialize; - float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-screenCoordsOffset).x) * radialize; - float surfaceDepth = linearizeDepth(gl_FragCoord.z) * radialize; + float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x) * radialise; + float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-screenCoordsOffset).x) * radialise; + float surfaceDepth = linearizeDepth(gl_FragCoord.z) * radialise; float realWaterDepth = depthSample - surfaceDepth; // undistorted water depth in view direction, independent of frustum screenCoordsOffset *= clamp(realWaterDepth / BUMP_SUPPRESS_DEPTH,0,1); #endif From 513ac8986d1f3f57d85c8fa4e2913c1160b5561b Mon Sep 17 00:00:00 2001 From: Frederic Chardon Date: Sat, 18 Apr 2020 20:54:21 +0000 Subject: [PATCH 078/226] Add link to opencs documentation on readthedocs, available through context menu. The documentation opens in default browser. There are 3 contexts: - global: opens the OpenMW CS User Manual main page - when a record is selected: opens the "Tables" page - when the filter field is selected: opens the "Record Filters" page There is also a link to the OpenCS tutorial in the help menu. --- CMakeLists.txt | 3 +++ apps/launcher/maindialog.cpp | 9 +++++++++ apps/launcher/maindialog.hpp | 1 + apps/opencs/model/prefs/state.cpp | 2 ++ apps/opencs/view/doc/view.cpp | 17 ++++++++++++++++ apps/opencs/view/doc/view.hpp | 4 ++++ apps/opencs/view/filter/editwidget.cpp | 27 ++++++++++++++++++++++++++ apps/opencs/view/filter/editwidget.hpp | 4 ++++ apps/opencs/view/world/table.cpp | 16 +++++++++++++++ apps/opencs/view/world/table.hpp | 3 +++ components/CMakeLists.txt | 2 +- components/misc/helpviewer.cpp | 12 ++++++++++++ components/misc/helpviewer.hpp | 7 +++++++ 13 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 components/misc/helpviewer.cpp create mode 100644 components/misc/helpviewer.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b7329ea7c..23f894156 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,8 @@ set(OPENMW_VERSION_COMMITDATE "") set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") +set(OPENMW_DOC_BASEURL "https://openmw.readthedocs.io/en/master/") + set(GIT_CHECKOUT FALSE) if(EXISTS ${PROJECT_SOURCE_DIR}/.git) find_package(Git) @@ -605,6 +607,7 @@ endif() # Components add_subdirectory (components) +target_compile_definitions(components PRIVATE OPENMW_DOC_BASEURL="${OPENMW_DOC_BASEURL}") # Apps and tools if (BUILD_OPENMW) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index f69213c4a..4a2e1ba0e 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -1,6 +1,7 @@ #include "maindialog.hpp" #include +#include #include #include @@ -54,12 +55,15 @@ Launcher::MainDialog::MainDialog(QWidget *parent) iconWidget->setCurrentRow(0); iconWidget->setFlow(QListView::LeftToRight); + QPushButton *helpButton = new QPushButton(tr("Help")); QPushButton *playButton = new QPushButton(tr("Play")); buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close")); + buttonBox->addButton(helpButton, QDialogButtonBox::HelpRole); buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole); connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(play())); + connect(buttonBox, SIGNAL(helpRequested()), this, SLOT(help())); // Remove what's this? button setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); @@ -614,3 +618,8 @@ void Launcher::MainDialog::play() if (mGameInvoker->startProcess(QLatin1String("openmw"), true)) return qApp->quit(); } + +void Launcher::MainDialog::help() +{ + Misc::HelpViewer::openHelp("reference/index.html"); +} diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index 75fe5ab89..d87a43100 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -59,6 +59,7 @@ namespace Launcher public slots: void changePage(QListWidgetItem *current, QListWidgetItem *previous); void play(); + void help(); private slots: void wizardStarted(); diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 89b5283a4..abd1ddfc8 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -271,6 +271,8 @@ void CSMPrefs::State::declare() declareShortcut ("document-file-newaddon", "New Addon", QKeySequence()); declareShortcut ("document-file-open", "Open", QKeySequence(Qt::ControlModifier | Qt::Key_O)); declareShortcut ("document-file-save", "Save", QKeySequence(Qt::ControlModifier | Qt::Key_S)); + declareShortcut ("document-help-help", "Help", QKeySequence(Qt::Key_F1)); + declareShortcut ("document-help-tutorial", "Tutorial", QKeySequence()); declareShortcut ("document-file-verify", "Verify", QKeySequence()); declareShortcut ("document-file-merge", "Merge", QKeySequence()); declareShortcut ("document-file-errorlog", "Open Load Error Log", QKeySequence()); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index dd58cbabc..ce04f6ada 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -31,6 +31,7 @@ #include "../tools/subviews.hpp" +#include #include #include "viewmanager.hpp" @@ -315,6 +316,12 @@ void CSVDoc::View::setupHelpMenu() { QMenu *help = menuBar()->addMenu (tr ("Help")); + QAction* helpInfo = createMenuEntry("Help", ":/info.png", help, "document-help-help"); + connect (helpInfo, SIGNAL (triggered()), this, SLOT (openHelp())); + + QAction* tutorial = createMenuEntry("Tutorial", ":/info.png", help, "document-help-tutorial"); + connect (tutorial, SIGNAL (triggered()), this, SLOT (tutorial())); + QAction* about = createMenuEntry("About OpenMW-CS", ":./info.png", help, "document-help-about"); connect (about, SIGNAL (triggered()), this, SLOT (infoAbout())); @@ -708,6 +715,16 @@ void CSVDoc::View::save() mDocument->save(); } +void CSVDoc::View::openHelp() +{ + Misc::HelpViewer::openHelp("manuals/openmw-cs/index.html"); +} + +void CSVDoc::View::tutorial() +{ + Misc::HelpViewer::openHelp("manuals/openmw-cs/tour.html"); +} + void CSVDoc::View::infoAbout() { // Get current OpenMW version diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 52057ab37..87c312412 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -169,6 +169,10 @@ namespace CSVDoc void exit(); + static void openHelp(); + + static void tutorial(); + void infoAbout(); void infoAboutQt(); diff --git a/apps/opencs/view/filter/editwidget.cpp b/apps/opencs/view/filter/editwidget.cpp index 600fa4f3b..6b585591f 100644 --- a/apps/opencs/view/filter/editwidget.cpp +++ b/apps/opencs/view/filter/editwidget.cpp @@ -1,12 +1,18 @@ #include "editwidget.hpp" #include +#include +#include +#include #include #include +#include + #include "../../model/world/data.hpp" #include "../../model/world/idtablebase.hpp" #include "../../model/world/columns.hpp" +#include "../../model/prefs/shortcut.hpp" CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) : QLineEdit (parent), mParser (data), mIsEmpty(true) @@ -29,6 +35,13 @@ CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) mStateColumnIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Modification); mDescColumnIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Description); + + mHelpAction = new QAction (tr ("Help"), this); + connect (mHelpAction, SIGNAL (triggered()), this, SLOT (openHelp())); + mHelpAction->setIcon(QIcon(":/info.png")); + addAction (mHelpAction); + auto* openHelpShortcut = new CSMPrefs::Shortcut("help", this); + openHelpShortcut->associateAction(mHelpAction); } void CSVFilter::EditWidget::textChanged (const QString& text) @@ -211,3 +224,17 @@ std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std:: return ss.str(); } + +void CSVFilter::EditWidget::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *menu = createStandardContextMenu(); + menu->addAction(mHelpAction); + menu->exec(event->globalPos()); + delete menu; +} + +void CSVFilter::EditWidget::openHelp() +{ + Misc::HelpViewer::openHelp("manuals/openmw-cs/record-filters.html"); +} + diff --git a/apps/opencs/view/filter/editwidget.hpp b/apps/opencs/view/filter/editwidget.hpp index 51a36969a..b47a884a3 100644 --- a/apps/opencs/view/filter/editwidget.hpp +++ b/apps/opencs/view/filter/editwidget.hpp @@ -26,6 +26,7 @@ namespace CSVFilter bool mIsEmpty; int mStateColumnIndex; int mDescColumnIndex; + QAction *mHelpAction; public: @@ -40,6 +41,7 @@ namespace CSVFilter private: std::string generateFilter(std::pair >& seekedString) const; + void contextMenuEvent (QContextMenuEvent *event) override; private slots: @@ -51,6 +53,8 @@ namespace CSVFilter void filterRowsInserted (const QModelIndex& parent, int start, int end); + static void openHelp(); + }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 11c2be5fc..e2bc87a72 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include "../../model/doc/document.hpp" @@ -155,6 +156,9 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) } } + if (mHelpAction) + menu.addAction (mHelpAction); + menu.exec (event->globalPos()); } @@ -387,6 +391,13 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, connect (mEditIdAction, SIGNAL (triggered()), this, SLOT (editCell())); addAction (mEditIdAction); + mHelpAction = new QAction (tr ("Help"), this); + connect (mHelpAction, SIGNAL (triggered()), this, SLOT (openHelp())); + mHelpAction->setIcon(QIcon(":/info.png")); + addAction (mHelpAction); + CSMPrefs::Shortcut* openHelpShortcut = new CSMPrefs::Shortcut("help", this); + openHelpShortcut->associateAction(mHelpAction); + connect (mProxyModel, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), this, SLOT (tableSizeUpdate())); @@ -561,6 +572,11 @@ void CSVWorld::Table::editCell() emit editRequest(mEditIdAction->getCurrentId(), ""); } +void CSVWorld::Table::openHelp() +{ + Misc::HelpViewer::openHelp("manuals/openmw-cs/tables.html"); +} + void CSVWorld::Table::viewRecord() { if (!(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_View)) diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 02f9023e7..36c423b33 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -65,6 +65,7 @@ namespace CSVWorld QAction *mPreviewAction; QAction *mExtendedDeleteAction; QAction *mExtendedRevertAction; + QAction *mHelpAction; TableEditIdAction *mEditIdAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTableBase *mModel; @@ -128,6 +129,8 @@ namespace CSVWorld void editCell(); + static void openHelp(); + void editRecord(); void cloneRecord(); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 8d644c6de..501960176 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -86,7 +86,7 @@ add_component_dir (esmterrain ) add_component_dir (misc - gcd constants utf8stream stringops resourcehelpers rng messageformatparser weakcache + gcd constants utf8stream stringops resourcehelpers rng messageformatparser weakcache helpviewer ) add_component_dir (debug diff --git a/components/misc/helpviewer.cpp b/components/misc/helpviewer.cpp new file mode 100644 index 000000000..0e6dadcaa --- /dev/null +++ b/components/misc/helpviewer.cpp @@ -0,0 +1,12 @@ +#include "helpviewer.hpp" + +#include +#include +#include + +void Misc::HelpViewer::openHelp(const char* url) +{ + QString link {OPENMW_DOC_BASEURL}; + link.append(url); + QDesktopServices::openUrl(QUrl(link)); +} diff --git a/components/misc/helpviewer.hpp b/components/misc/helpviewer.hpp new file mode 100644 index 000000000..7affb2c36 --- /dev/null +++ b/components/misc/helpviewer.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace Misc { + namespace HelpViewer { + void openHelp(const char* url); + } +} From 8c2f3ff00752af7b799f3d9229572343d8f8c0ed Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 19 Apr 2020 16:31:54 +0200 Subject: [PATCH 079/226] clean up code; purge const_cast --- apps/openmw/mwworld/esmstore.cpp | 4 ++-- components/esm/esmcommon.hpp | 2 ++ components/esm/esmreader.cpp | 3 +-- components/esm/esmreader.hpp | 8 +++++--- components/esm/loadcell.cpp | 4 ++-- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index d366a5a68..1f6ed5102 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -45,7 +45,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) const std::vector &masters = esm.getGameFiles(); std::vector *allPlugins = esm.getGlobalReaderList(); for (size_t j = 0; j < masters.size(); j++) { - ESM::Header::MasterData &mast = const_cast(masters[j]); + const ESM::Header::MasterData &mast = masters[j]; std::string fname = mast.name; int index = ~0; for (int i = 0; i < esm.getIndex(); i++) { @@ -63,7 +63,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) + ", but it has not been loaded yet. Please check your load order."; esm.fail(fstring); } - mast.index = index; + esm.addParentFileIndex(index); } // Loop through all records diff --git a/components/esm/esmcommon.hpp b/components/esm/esmcommon.hpp index 97ce88556..f7a8bf126 100644 --- a/components/esm/esmcommon.hpp +++ b/components/esm/esmcommon.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -126,6 +127,7 @@ struct ESM_Context // actually contribute to a specific cell. Therefore, we need to store the index // of the file belonging to this contest. See CellStore::(list/load)refs for details. int index; + std::vector parentFileIndices; // True if subName has been read but not used. bool subCached; diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 92dbba167..1b6eca734 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -20,8 +20,7 @@ ESM_Context ESMReader::getContext() } ESMReader::ESMReader() - : mIdx(0) - , mRecordFlags(0) + : mRecordFlags(0) , mBuffer(50*1024) , mGlobalReaderList(nullptr) , mEncoder(nullptr) diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 72a7b4790..761756e8f 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -79,13 +79,15 @@ public: // terrain palette, but ESMReader does not pass a reference to the correct plugin // to the individual load() methods. This hack allows to pass this reference // indirectly to the load() method. - int mIdx; - void setIndex(const int index) {mIdx = index; mCtx.index = index;} - int getIndex() {return mIdx;} + void setIndex(const int index) { mCtx.index = index;} + int getIndex() {return mCtx.index;} void setGlobalReaderList(std::vector *list) {mGlobalReaderList = list;} std::vector *getGlobalReaderList() {return mGlobalReaderList;} + void addParentFileIndex(int index) { mCtx.parentFileIndices.push_back(index); } + const std::vector& getParentFileIndices() const { return mCtx.parentFileIndices; } + /************************************************************************* * * Medium-level reading shortcuts diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 5b259acef..f92a752a4 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -21,12 +21,12 @@ namespace // If we have an index value that does not make sense, assume that it was an addition // by the present plugin (but a faulty one) - if (local && local <= reader.getGameFiles().size()) + if (local && local <= reader.getParentFileIndices().size()) { // If the most significant 8 bits are used, then this reference already exists. // In this case, do not spawn a new reference, but overwrite the old one. refNum.mIndex &= 0x00ffffff; // delete old plugin ID - refNum.mContentFile = reader.getGameFiles()[local-1].index; + refNum.mContentFile = reader.getParentFileIndices()[local-1]; } else { From 91c2dea2a7a59c06e606990fea2188057aa9d2eb Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 19 Apr 2020 17:35:01 +0200 Subject: [PATCH 080/226] use OSG 3.6.5 from our PPA --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 383e30c61..5ce71a7b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ addons: # Audio, Video and Misc. deps libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, # The other ones from OpenMW ppa - libbullet-dev, libopenscenegraph-3.4-dev, libmygui-dev + libbullet-dev, libopenscenegraph-dev, libmygui-dev ] coverity_scan: # TODO: currently takes too long, disabled openmw/openmw-cs for now. project: From 0630cc482c4c32f78268a54cf9e0c5de02c67cfc Mon Sep 17 00:00:00 2001 From: fredzio Date: Sun, 19 Apr 2020 19:37:40 +0200 Subject: [PATCH 081/226] Compile helpviewer in components only when USE_QT is defined It fixes build of OpenMW alone (wihtout launcher or OpenCS) --- components/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 501960176..26cf38204 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -86,7 +86,7 @@ add_component_dir (esmterrain ) add_component_dir (misc - gcd constants utf8stream stringops resourcehelpers rng messageformatparser weakcache helpviewer + gcd constants utf8stream stringops resourcehelpers rng messageformatparser weakcache ) add_component_dir (debug @@ -196,6 +196,10 @@ if (USE_QT) processinvoker ) + add_component_dir (misc + helpviewer + ) + if (DESIRED_QT_VERSION MATCHES 4) include(${QT_USE_FILE}) QT4_WRAP_UI(ESM_UI_HDR ${ESM_UI}) From ddab86d94046eab0c0713371990deb04b3f423cc Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Sun, 16 Feb 2020 01:26:23 +0000 Subject: [PATCH 082/226] Use approximate comparison for floating point in test This fixes test suite on my machine --- .../detournavigator/navigator.cpp | 452 +++++++++--------- .../detournavigator/recastmeshbuilder.cpp | 9 +- 2 files changed, 234 insertions(+), 227 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index df8be3781..51370c8e0 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -11,10 +11,16 @@ #include #include +#include #include #include +MATCHER_P3(Vec3fEq, x, y, z, "") +{ + return std::abs(arg.x() - x) < 1e-4 && std::abs(arg.y() - y) < 1e-4 && std::abs(arg.z() - z) < 1e-4; +} + namespace { using namespace testing; @@ -113,31 +119,31 @@ namespace EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); - EXPECT_EQ(mPath, std::deque({ - osg::Vec3f(-215, 215, 1.85963428020477294921875), - osg::Vec3f(-194.9653167724609375, 194.9653167724609375, -6.57602214813232421875), - osg::Vec3f(-174.930633544921875, 174.930633544921875, -15.01167774200439453125), - osg::Vec3f(-154.8959503173828125, 154.8959503173828125, -23.4473361968994140625), - osg::Vec3f(-134.86126708984375, 134.86126708984375, -31.8829936981201171875), - osg::Vec3f(-114.82657623291015625, 114.82657623291015625, -40.3186492919921875), - osg::Vec3f(-94.7918853759765625, 94.7918853759765625, -47.3990631103515625), - osg::Vec3f(-74.75719451904296875, 74.75719451904296875, -53.7258148193359375), - osg::Vec3f(-54.722499847412109375, 54.722499847412109375, -60.052555084228515625), - osg::Vec3f(-34.68780517578125, 34.68780517578125, -66.37931060791015625), - osg::Vec3f(-14.6531162261962890625, 14.6531162261962890625, -72.70604705810546875), - osg::Vec3f(5.3815765380859375, -5.3815765380859375, -75.35065460205078125), - osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), - osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375), - osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), - osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.8258514404296875), - osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), - osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625), - osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625), - osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625), - osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625), - osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125), - osg::Vec3f(215, -215, 1.87718021869659423828125), - })) << mPath; + EXPECT_THAT(mPath, ElementsAre( + Vec3fEq(-215, 215, 1.85963428020477294921875), + Vec3fEq(-194.9653167724609375, 194.9653167724609375, -6.57602214813232421875), + Vec3fEq(-174.930633544921875, 174.930633544921875, -15.01167774200439453125), + Vec3fEq(-154.8959503173828125, 154.8959503173828125, -23.4473361968994140625), + Vec3fEq(-134.86126708984375, 134.86126708984375, -31.8829936981201171875), + Vec3fEq(-114.82657623291015625, 114.82657623291015625, -40.3186492919921875), + Vec3fEq(-94.7918853759765625, 94.7918853759765625, -47.3990631103515625), + Vec3fEq(-74.75719451904296875, 74.75719451904296875, -53.7258148193359375), + Vec3fEq(-54.722499847412109375, 54.722499847412109375, -60.052555084228515625), + Vec3fEq(-34.68780517578125, 34.68780517578125, -66.37931060791015625), + Vec3fEq(-14.6531162261962890625, 14.6531162261962890625, -72.70604705810546875), + Vec3fEq(5.3815765380859375, -5.3815765380859375, -75.35065460205078125), + Vec3fEq(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), + Vec3fEq(45.450958251953125, -45.450958251953125, -60.5882568359375), + Vec3fEq(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), + Vec3fEq(85.5203399658203125, -85.5203399658203125, -45.8258514404296875), + Vec3fEq(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), + Vec3fEq(125.5897216796875, -125.5897216796875, -31.063449859619140625), + Vec3fEq(145.6244049072265625, -145.6244049072265625, -23.6822509765625), + Vec3fEq(165.659088134765625, -165.659088134765625, -16.3010501861572265625), + Vec3fEq(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625), + Vec3fEq(205.7284698486328125, -205.7284698486328125, -1.5386505126953125), + Vec3fEq(215, -215, 1.87718021869659423828125) + )); } TEST_F(DetourNavigatorNavigatorTest, add_object_should_change_navmesh) @@ -163,31 +169,31 @@ namespace EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); - EXPECT_EQ(mPath, std::deque({ - osg::Vec3f(-215, 215, 1.85963428020477294921875), - osg::Vec3f(-194.9653167724609375, 194.9653167724609375, -6.57602214813232421875), - osg::Vec3f(-174.930633544921875, 174.930633544921875, -15.01167774200439453125), - osg::Vec3f(-154.8959503173828125, 154.8959503173828125, -23.4473361968994140625), - osg::Vec3f(-134.86126708984375, 134.86126708984375, -31.8829936981201171875), - osg::Vec3f(-114.82657623291015625, 114.82657623291015625, -40.3186492919921875), - osg::Vec3f(-94.7918853759765625, 94.7918853759765625, -47.3990631103515625), - osg::Vec3f(-74.75719451904296875, 74.75719451904296875, -53.7258148193359375), - osg::Vec3f(-54.722499847412109375, 54.722499847412109375, -60.052555084228515625), - osg::Vec3f(-34.68780517578125, 34.68780517578125, -66.37931060791015625), - osg::Vec3f(-14.6531162261962890625, 14.6531162261962890625, -72.70604705810546875), - osg::Vec3f(5.3815765380859375, -5.3815765380859375, -75.35065460205078125), - osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), - osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375), - osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), - osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.8258514404296875), - osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), - osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625), - osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625), - osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625), - osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625), - osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125), - osg::Vec3f(215, -215, 1.87718021869659423828125), - })) << mPath; + EXPECT_THAT(mPath, ElementsAre( + Vec3fEq(-215, 215, 1.85963428020477294921875), + Vec3fEq(-194.9653167724609375, 194.9653167724609375, -6.57602214813232421875), + Vec3fEq(-174.930633544921875, 174.930633544921875, -15.01167774200439453125), + Vec3fEq(-154.8959503173828125, 154.8959503173828125, -23.4473361968994140625), + Vec3fEq(-134.86126708984375, 134.86126708984375, -31.8829936981201171875), + Vec3fEq(-114.82657623291015625, 114.82657623291015625, -40.3186492919921875), + Vec3fEq(-94.7918853759765625, 94.7918853759765625, -47.3990631103515625), + Vec3fEq(-74.75719451904296875, 74.75719451904296875, -53.7258148193359375), + Vec3fEq(-54.722499847412109375, 54.722499847412109375, -60.052555084228515625), + Vec3fEq(-34.68780517578125, 34.68780517578125, -66.37931060791015625), + Vec3fEq(-14.6531162261962890625, 14.6531162261962890625, -72.70604705810546875), + Vec3fEq(5.3815765380859375, -5.3815765380859375, -75.35065460205078125), + Vec3fEq(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), + Vec3fEq(45.450958251953125, -45.450958251953125, -60.5882568359375), + Vec3fEq(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), + Vec3fEq(85.5203399658203125, -85.5203399658203125, -45.8258514404296875), + Vec3fEq(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), + Vec3fEq(125.5897216796875, -125.5897216796875, -31.063449859619140625), + Vec3fEq(145.6244049072265625, -145.6244049072265625, -23.6822509765625), + Vec3fEq(165.659088134765625, -165.659088134765625, -16.3010501861572265625), + Vec3fEq(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625), + Vec3fEq(205.7284698486328125, -205.7284698486328125, -1.5386505126953125), + Vec3fEq(215, -215, 1.87718021869659423828125) + )); mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); @@ -197,31 +203,31 @@ namespace mOut = std::back_inserter(mPath); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); - EXPECT_EQ(mPath, std::deque({ - osg::Vec3f(-215, 215, 1.87826788425445556640625), - osg::Vec3f(-199.7968292236328125, 191.09100341796875, -3.54876613616943359375), - osg::Vec3f(-184.5936431884765625, 167.1819915771484375, -8.97847843170166015625), - osg::Vec3f(-169.3904571533203125, 143.2729949951171875, -14.408184051513671875), - osg::Vec3f(-154.1872711181640625, 119.36397552490234375, -19.837890625), - osg::Vec3f(-138.9840850830078125, 95.45496368408203125, -25.2675991058349609375), - osg::Vec3f(-123.78090667724609375, 71.54595184326171875, -30.6973056793212890625), - osg::Vec3f(-108.57772064208984375, 47.636936187744140625, -36.12701416015625), - osg::Vec3f(-93.3745269775390625, 23.7279262542724609375, -40.754688262939453125), - osg::Vec3f(-78.17134857177734375, -0.18108306825160980224609375, -37.128787994384765625), - osg::Vec3f(-62.968158721923828125, -24.0900936126708984375, -33.50289154052734375), - osg::Vec3f(-47.764972686767578125, -47.999103546142578125, -30.797946929931640625), - osg::Vec3f(-23.852447509765625, -63.196765899658203125, -33.97112274169921875), - osg::Vec3f(0.0600789971649646759033203125, -78.39443206787109375, -37.14543914794921875), - osg::Vec3f(23.97260284423828125, -93.5920867919921875, -40.774089813232421875), - osg::Vec3f(47.885128021240234375, -108.78974151611328125, -36.05129241943359375), - osg::Vec3f(71.7976531982421875, -123.98740386962890625, -30.6235561370849609375), - osg::Vec3f(95.71018218994140625, -139.18505859375, -25.1958255767822265625), - osg::Vec3f(119.6226959228515625, -154.382720947265625, -19.7680912017822265625), - osg::Vec3f(143.53521728515625, -169.58038330078125, -14.34035205841064453125), - osg::Vec3f(167.4477386474609375, -184.778045654296875, -8.9126186370849609375), - osg::Vec3f(191.360260009765625, -199.9757080078125, -3.4848802089691162109375), - osg::Vec3f(215, -215, 1.87826788425445556640625), - })) << mPath; + EXPECT_THAT(mPath, ElementsAre( + Vec3fEq(-215, 215, 1.87826788425445556640625), + Vec3fEq(-199.7968292236328125, 191.09100341796875, -3.54876613616943359375), + Vec3fEq(-184.5936431884765625, 167.1819915771484375, -8.97847843170166015625), + Vec3fEq(-169.3904571533203125, 143.2729949951171875, -14.408184051513671875), + Vec3fEq(-154.1872711181640625, 119.36397552490234375, -19.837890625), + Vec3fEq(-138.9840850830078125, 95.45496368408203125, -25.2675991058349609375), + Vec3fEq(-123.78090667724609375, 71.54595184326171875, -30.6973056793212890625), + Vec3fEq(-108.57772064208984375, 47.636936187744140625, -36.12701416015625), + Vec3fEq(-93.3745269775390625, 23.7279262542724609375, -40.754688262939453125), + Vec3fEq(-78.17134857177734375, -0.18108306825160980224609375, -37.128787994384765625), + Vec3fEq(-62.968158721923828125, -24.0900936126708984375, -33.50289154052734375), + Vec3fEq(-47.764972686767578125, -47.999103546142578125, -30.797946929931640625), + Vec3fEq(-23.852447509765625, -63.196765899658203125, -33.97112274169921875), + Vec3fEq(0.0600789971649646759033203125, -78.39443206787109375, -37.14543914794921875), + Vec3fEq(23.97260284423828125, -93.5920867919921875, -40.774089813232421875), + Vec3fEq(47.885128021240234375, -108.78974151611328125, -36.05129241943359375), + Vec3fEq(71.7976531982421875, -123.98740386962890625, -30.6235561370849609375), + Vec3fEq(95.71018218994140625, -139.18505859375, -25.1958255767822265625), + Vec3fEq(119.6226959228515625, -154.382720947265625, -19.7680912017822265625), + Vec3fEq(143.53521728515625, -169.58038330078125, -14.34035205841064453125), + Vec3fEq(167.4477386474609375, -184.778045654296875, -8.9126186370849609375), + Vec3fEq(191.360260009765625, -199.9757080078125, -3.4848802089691162109375), + Vec3fEq(215, -215, 1.87826788425445556640625) + )); } TEST_F(DetourNavigatorNavigatorTest, update_changed_object_should_change_navmesh) @@ -248,31 +254,31 @@ namespace EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); - EXPECT_EQ(mPath, std::deque({ - osg::Vec3f(-215, 215, 1.87826788425445556640625), - osg::Vec3f(-199.7968292236328125, 191.09100341796875, -3.54876613616943359375), - osg::Vec3f(-184.5936431884765625, 167.1819915771484375, -8.97847843170166015625), - osg::Vec3f(-169.3904571533203125, 143.2729949951171875, -14.408184051513671875), - osg::Vec3f(-154.1872711181640625, 119.36397552490234375, -19.837890625), - osg::Vec3f(-138.9840850830078125, 95.45496368408203125, -25.2675991058349609375), - osg::Vec3f(-123.78090667724609375, 71.54595184326171875, -30.6973056793212890625), - osg::Vec3f(-108.57772064208984375, 47.636936187744140625, -36.12701416015625), - osg::Vec3f(-93.3745269775390625, 23.7279262542724609375, -40.754688262939453125), - osg::Vec3f(-78.17134857177734375, -0.18108306825160980224609375, -37.128787994384765625), - osg::Vec3f(-62.968158721923828125, -24.0900936126708984375, -33.50289154052734375), - osg::Vec3f(-47.764972686767578125, -47.999103546142578125, -30.797946929931640625), - osg::Vec3f(-23.852447509765625, -63.196765899658203125, -33.97112274169921875), - osg::Vec3f(0.0600789971649646759033203125, -78.39443206787109375, -37.14543914794921875), - osg::Vec3f(23.97260284423828125, -93.5920867919921875, -40.774089813232421875), - osg::Vec3f(47.885128021240234375, -108.78974151611328125, -36.05129241943359375), - osg::Vec3f(71.7976531982421875, -123.98740386962890625, -30.6235561370849609375), - osg::Vec3f(95.71018218994140625, -139.18505859375, -25.1958255767822265625), - osg::Vec3f(119.6226959228515625, -154.382720947265625, -19.7680912017822265625), - osg::Vec3f(143.53521728515625, -169.58038330078125, -14.34035205841064453125), - osg::Vec3f(167.4477386474609375, -184.778045654296875, -8.9126186370849609375), - osg::Vec3f(191.360260009765625, -199.9757080078125, -3.4848802089691162109375), - osg::Vec3f(215, -215, 1.87826788425445556640625), - })) << mPath; + EXPECT_THAT(mPath, ElementsAre( + Vec3fEq(-215, 215, 1.87826788425445556640625), + Vec3fEq(-199.7968292236328125, 191.09100341796875, -3.54876613616943359375), + Vec3fEq(-184.5936431884765625, 167.1819915771484375, -8.97847843170166015625), + Vec3fEq(-169.3904571533203125, 143.2729949951171875, -14.408184051513671875), + Vec3fEq(-154.1872711181640625, 119.36397552490234375, -19.837890625), + Vec3fEq(-138.9840850830078125, 95.45496368408203125, -25.2675991058349609375), + Vec3fEq(-123.78090667724609375, 71.54595184326171875, -30.6973056793212890625), + Vec3fEq(-108.57772064208984375, 47.636936187744140625, -36.12701416015625), + Vec3fEq(-93.3745269775390625, 23.7279262542724609375, -40.754688262939453125), + Vec3fEq(-78.17134857177734375, -0.18108306825160980224609375, -37.128787994384765625), + Vec3fEq(-62.968158721923828125, -24.0900936126708984375, -33.50289154052734375), + Vec3fEq(-47.764972686767578125, -47.999103546142578125, -30.797946929931640625), + Vec3fEq(-23.852447509765625, -63.196765899658203125, -33.97112274169921875), + Vec3fEq(0.0600789971649646759033203125, -78.39443206787109375, -37.14543914794921875), + Vec3fEq(23.97260284423828125, -93.5920867919921875, -40.774089813232421875), + Vec3fEq(47.885128021240234375, -108.78974151611328125, -36.05129241943359375), + Vec3fEq(71.7976531982421875, -123.98740386962890625, -30.6235561370849609375), + Vec3fEq(95.71018218994140625, -139.18505859375, -25.1958255767822265625), + Vec3fEq(119.6226959228515625, -154.382720947265625, -19.7680912017822265625), + Vec3fEq(143.53521728515625, -169.58038330078125, -14.34035205841064453125), + Vec3fEq(167.4477386474609375, -184.778045654296875, -8.9126186370849609375), + Vec3fEq(191.360260009765625, -199.9757080078125, -3.4848802089691162109375), + Vec3fEq(215, -215, 1.87826788425445556640625) + )); compoundShape.updateChildTransform(0, btTransform(btMatrix3x3::getIdentity(), btVector3(1000, 0, 0))); @@ -284,31 +290,31 @@ namespace mOut = std::back_inserter(mPath); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); - EXPECT_EQ(mPath, std::deque({ - osg::Vec3f(-215, 215, 1.85963428020477294921875), - osg::Vec3f(-194.9653167724609375, 194.9653167724609375, -6.57602214813232421875), - osg::Vec3f(-174.930633544921875, 174.930633544921875, -15.01167774200439453125), - osg::Vec3f(-154.8959503173828125, 154.8959503173828125, -23.4473361968994140625), - osg::Vec3f(-134.86126708984375, 134.86126708984375, -31.8829936981201171875), - osg::Vec3f(-114.82657623291015625, 114.82657623291015625, -40.3186492919921875), - osg::Vec3f(-94.7918853759765625, 94.7918853759765625, -47.3990631103515625), - osg::Vec3f(-74.75719451904296875, 74.75719451904296875, -53.7258148193359375), - osg::Vec3f(-54.722499847412109375, 54.722499847412109375, -60.052555084228515625), - osg::Vec3f(-34.68780517578125, 34.68780517578125, -66.37931060791015625), - osg::Vec3f(-14.6531162261962890625, 14.6531162261962890625, -72.70604705810546875), - osg::Vec3f(5.3815765380859375, -5.3815765380859375, -75.35065460205078125), - osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), - osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375), - osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), - osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.8258514404296875), - osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), - osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625), - osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625), - osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625), - osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625), - osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125), - osg::Vec3f(215, -215, 1.87718021869659423828125), - })) << mPath; + EXPECT_THAT(mPath, ElementsAre( + Vec3fEq(-215, 215, 1.85963428020477294921875), + Vec3fEq(-194.9653167724609375, 194.9653167724609375, -6.57602214813232421875), + Vec3fEq(-174.930633544921875, 174.930633544921875, -15.01167774200439453125), + Vec3fEq(-154.8959503173828125, 154.8959503173828125, -23.4473361968994140625), + Vec3fEq(-134.86126708984375, 134.86126708984375, -31.8829936981201171875), + Vec3fEq(-114.82657623291015625, 114.82657623291015625, -40.3186492919921875), + Vec3fEq(-94.7918853759765625, 94.7918853759765625, -47.3990631103515625), + Vec3fEq(-74.75719451904296875, 74.75719451904296875, -53.7258148193359375), + Vec3fEq(-54.722499847412109375, 54.722499847412109375, -60.052555084228515625), + Vec3fEq(-34.68780517578125, 34.68780517578125, -66.37931060791015625), + Vec3fEq(-14.6531162261962890625, 14.6531162261962890625, -72.70604705810546875), + Vec3fEq(5.3815765380859375, -5.3815765380859375, -75.35065460205078125), + Vec3fEq(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), + Vec3fEq(45.450958251953125, -45.450958251953125, -60.5882568359375), + Vec3fEq(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), + Vec3fEq(85.5203399658203125, -85.5203399658203125, -45.8258514404296875), + Vec3fEq(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), + Vec3fEq(125.5897216796875, -125.5897216796875, -31.063449859619140625), + Vec3fEq(145.6244049072265625, -145.6244049072265625, -23.6822509765625), + Vec3fEq(165.659088134765625, -165.659088134765625, -16.3010501861572265625), + Vec3fEq(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625), + Vec3fEq(205.7284698486328125, -205.7284698486328125, -1.5386505126953125), + Vec3fEq(215, -215, 1.87718021869659423828125) + )); } TEST_F(DetourNavigatorNavigatorTest, for_overlapping_heightfields_should_use_higher) @@ -341,31 +347,31 @@ namespace EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); - EXPECT_EQ(mPath, std::deque({ - osg::Vec3f(-215, 215, 1.96328866481781005859375), - osg::Vec3f(-194.9653167724609375, 194.9653167724609375, -0.242215454578399658203125), - osg::Vec3f(-174.930633544921875, 174.930633544921875, -2.447719097137451171875), - osg::Vec3f(-154.8959503173828125, 154.8959503173828125, -4.65322399139404296875), - osg::Vec3f(-134.86126708984375, 134.86126708984375, -6.858726978302001953125), - osg::Vec3f(-114.82657623291015625, 114.82657623291015625, -9.06423282623291015625), - osg::Vec3f(-94.7918853759765625, 94.7918853759765625, -11.26973628997802734375), - osg::Vec3f(-74.75719451904296875, 74.75719451904296875, -13.26497173309326171875), - osg::Vec3f(-54.722499847412109375, 54.722499847412109375, -15.24860477447509765625), - osg::Vec3f(-34.68780517578125, 34.68780517578125, -17.23223876953125), - osg::Vec3f(-14.6531162261962890625, 14.6531162261962890625, -19.215869903564453125), - osg::Vec3f(5.3815765380859375, -5.3815765380859375, -20.1338443756103515625), - osg::Vec3f(25.41626739501953125, -25.41626739501953125, -18.1502132415771484375), - osg::Vec3f(45.450958251953125, -45.450958251953125, -16.1665802001953125), - osg::Vec3f(65.48564910888671875, -65.48564910888671875, -14.18294620513916015625), - osg::Vec3f(85.5203399658203125, -85.5203399658203125, -12.199314117431640625), - osg::Vec3f(105.55503082275390625, -105.55503082275390625, -10.08488368988037109375), - osg::Vec3f(125.5897216796875, -125.5897216796875, -7.87938022613525390625), - osg::Vec3f(145.6244049072265625, -145.6244049072265625, -5.673875331878662109375), - osg::Vec3f(165.659088134765625, -165.659088134765625, -3.468370914459228515625), - osg::Vec3f(185.6937713623046875, -185.6937713623046875, -1.26286637783050537109375), - osg::Vec3f(205.7284698486328125, -205.7284698486328125, 0.942641556262969970703125), - osg::Vec3f(215, -215, 1.96328866481781005859375), - })) << mPath; + EXPECT_THAT(mPath, ElementsAre( + Vec3fEq(-215, 215, 1.96328866481781005859375), + Vec3fEq(-194.9653167724609375, 194.9653167724609375, -0.242215454578399658203125), + Vec3fEq(-174.930633544921875, 174.930633544921875, -2.447719097137451171875), + Vec3fEq(-154.8959503173828125, 154.8959503173828125, -4.65322399139404296875), + Vec3fEq(-134.86126708984375, 134.86126708984375, -6.858726978302001953125), + Vec3fEq(-114.82657623291015625, 114.82657623291015625, -9.06423282623291015625), + Vec3fEq(-94.7918853759765625, 94.7918853759765625, -11.26973628997802734375), + Vec3fEq(-74.75719451904296875, 74.75719451904296875, -13.26497173309326171875), + Vec3fEq(-54.722499847412109375, 54.722499847412109375, -15.24860477447509765625), + Vec3fEq(-34.68780517578125, 34.68780517578125, -17.23223876953125), + Vec3fEq(-14.6531162261962890625, 14.6531162261962890625, -19.215869903564453125), + Vec3fEq(5.3815765380859375, -5.3815765380859375, -20.1338443756103515625), + Vec3fEq(25.41626739501953125, -25.41626739501953125, -18.1502132415771484375), + Vec3fEq(45.450958251953125, -45.450958251953125, -16.1665802001953125), + Vec3fEq(65.48564910888671875, -65.48564910888671875, -14.18294620513916015625), + Vec3fEq(85.5203399658203125, -85.5203399658203125, -12.199314117431640625), + Vec3fEq(105.55503082275390625, -105.55503082275390625, -10.08488368988037109375), + Vec3fEq(125.5897216796875, -125.5897216796875, -7.87938022613525390625), + Vec3fEq(145.6244049072265625, -145.6244049072265625, -5.673875331878662109375), + Vec3fEq(165.659088134765625, -165.659088134765625, -3.468370914459228515625), + Vec3fEq(185.6937713623046875, -185.6937713623046875, -1.26286637783050537109375), + Vec3fEq(205.7284698486328125, -205.7284698486328125, 0.942641556262969970703125), + Vec3fEq(215, -215, 1.96328866481781005859375) + )); } TEST_F(DetourNavigatorNavigatorTest, path_should_be_around_avoid_shape) @@ -397,32 +403,32 @@ namespace EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); - EXPECT_EQ(mPath, std::deque({ - osg::Vec3f(-215, 215, 1.9393787384033203125), - osg::Vec3f(-200.8159637451171875, 190.47265625, -0.639537751674652099609375), - osg::Vec3f(-186.6319427490234375, 165.9453125, -3.2184507846832275390625), - osg::Vec3f(-172.447906494140625, 141.41796875, -5.797363758087158203125), - osg::Vec3f(-158.263885498046875, 116.8906097412109375, -8.37627696990966796875), - osg::Vec3f(-144.079864501953125, 92.3632659912109375, -10.9551906585693359375), - osg::Vec3f(-129.89581298828125, 67.83591461181640625, -13.53410625457763671875), - osg::Vec3f(-115.7117919921875, 43.308563232421875, -16.1130199432373046875), - osg::Vec3f(-101.5277557373046875, 18.7812137603759765625, -18.6919345855712890625), - osg::Vec3f(-87.34372711181640625, -5.7461376190185546875, -20.4680538177490234375), - osg::Vec3f(-67.02922821044921875, -25.4970550537109375, -20.514247894287109375), - osg::Vec3f(-46.714717864990234375, -45.2479705810546875, -20.560443878173828125), - osg::Vec3f(-26.40021514892578125, -64.99889373779296875, -20.6066417694091796875), - osg::Vec3f(-6.085712432861328125, -84.74980926513671875, -20.652835845947265625), - osg::Vec3f(14.22879505157470703125, -104.50072479248046875, -18.151397705078125), - osg::Vec3f(39.05098724365234375, -118.16222381591796875, -15.66748714447021484375), - osg::Vec3f(63.87317657470703125, -131.82373046875, -13.18358135223388671875), - osg::Vec3f(88.69537353515625, -145.4852142333984375, -10.699672698974609375), - osg::Vec3f(113.51757049560546875, -159.146697998046875, -8.21576786041259765625), - osg::Vec3f(138.3397674560546875, -172.808197021484375, -5.731859683990478515625), - osg::Vec3f(163.1619720458984375, -186.469696044921875, -3.2479507923126220703125), - osg::Vec3f(187.984161376953125, -200.1311798095703125, -0.764044821262359619140625), - osg::Vec3f(212.8063507080078125, -213.7926788330078125, 1.719865322113037109375), - osg::Vec3f(215, -215, 1.9393787384033203125), - })) << mPath; + EXPECT_THAT(mPath, ElementsAre( + Vec3fEq(-215, 215, 1.9393787384033203125), + Vec3fEq(-200.8159637451171875, 190.47265625, -0.639537751674652099609375), + Vec3fEq(-186.6319427490234375, 165.9453125, -3.2184507846832275390625), + Vec3fEq(-172.447906494140625, 141.41796875, -5.797363758087158203125), + Vec3fEq(-158.263885498046875, 116.8906097412109375, -8.37627696990966796875), + Vec3fEq(-144.079864501953125, 92.3632659912109375, -10.9551906585693359375), + Vec3fEq(-129.89581298828125, 67.83591461181640625, -13.53410625457763671875), + Vec3fEq(-115.7117919921875, 43.308563232421875, -16.1130199432373046875), + Vec3fEq(-101.5277557373046875, 18.7812137603759765625, -18.6919345855712890625), + Vec3fEq(-87.34372711181640625, -5.7461376190185546875, -20.4680538177490234375), + Vec3fEq(-67.02922821044921875, -25.4970550537109375, -20.514247894287109375), + Vec3fEq(-46.714717864990234375, -45.2479705810546875, -20.560443878173828125), + Vec3fEq(-26.40021514892578125, -64.99889373779296875, -20.6066417694091796875), + Vec3fEq(-6.085712432861328125, -84.74980926513671875, -20.652835845947265625), + Vec3fEq(14.22879505157470703125, -104.50072479248046875, -18.151397705078125), + Vec3fEq(39.05098724365234375, -118.16222381591796875, -15.66748714447021484375), + Vec3fEq(63.87317657470703125, -131.82373046875, -13.18358135223388671875), + Vec3fEq(88.69537353515625, -145.4852142333984375, -10.699672698974609375), + Vec3fEq(113.51757049560546875, -159.146697998046875, -8.21576786041259765625), + Vec3fEq(138.3397674560546875, -172.808197021484375, -5.731859683990478515625), + Vec3fEq(163.1619720458984375, -186.469696044921875, -3.2479507923126220703125), + Vec3fEq(187.984161376953125, -200.1311798095703125, -0.764044821262359619140625), + Vec3fEq(212.8063507080078125, -213.7926788330078125, 1.719865322113037109375), + Vec3fEq(215, -215, 1.9393787384033203125) + )); } TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_ground_lower_than_water_with_only_swim_flag) @@ -590,26 +596,26 @@ namespace EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); - EXPECT_EQ(mPath, std::deque({ - osg::Vec3f(0, 215, -94.75363922119140625), - osg::Vec3f(9.8083515167236328125, 188.4185333251953125, -105.199951171875), - osg::Vec3f(19.6167049407958984375, 161.837066650390625, -114.25495147705078125), - osg::Vec3f(29.42505645751953125, 135.255615234375, -123.309967041015625), - osg::Vec3f(39.23340606689453125, 108.674163818359375, -132.3649749755859375), - osg::Vec3f(49.04175567626953125, 82.09270477294921875, -137.2874755859375), - osg::Vec3f(58.8501129150390625, 55.5112457275390625, -139.2451171875), - osg::Vec3f(68.6584625244140625, 28.9297885894775390625, -141.2027740478515625), - osg::Vec3f(78.4668121337890625, 2.3483295440673828125, -143.1604156494140625), - osg::Vec3f(88.27516937255859375, -24.233127593994140625, -141.3894805908203125), - osg::Vec3f(83.73651885986328125, -52.2005767822265625, -142.3761444091796875), - osg::Vec3f(79.19786834716796875, -80.16802978515625, -143.114837646484375), - osg::Vec3f(64.8477935791015625, -104.598602294921875, -137.840911865234375), - osg::Vec3f(50.497714996337890625, -129.0291748046875, -131.45831298828125), - osg::Vec3f(36.147632598876953125, -153.459747314453125, -121.42321014404296875), - osg::Vec3f(21.7975559234619140625, -177.8903350830078125, -111.38811492919921875), - osg::Vec3f(7.44747829437255859375, -202.3209075927734375, -101.19382476806640625), - osg::Vec3f(0, -215, -94.75363922119140625), - })) << mPath; + EXPECT_THAT(mPath, ElementsAre( + Vec3fEq(0, 215, -94.75363922119140625), + Vec3fEq(9.8083515167236328125, 188.4185333251953125, -105.199951171875), + Vec3fEq(19.6167049407958984375, 161.837066650390625, -114.25495147705078125), + Vec3fEq(29.42505645751953125, 135.255615234375, -123.309967041015625), + Vec3fEq(39.23340606689453125, 108.674163818359375, -132.3649749755859375), + Vec3fEq(49.04175567626953125, 82.09270477294921875, -137.2874755859375), + Vec3fEq(58.8501129150390625, 55.5112457275390625, -139.2451171875), + Vec3fEq(68.6584625244140625, 28.9297885894775390625, -141.2027740478515625), + Vec3fEq(78.4668121337890625, 2.3483295440673828125, -143.1604156494140625), + Vec3fEq(88.27516937255859375, -24.233127593994140625, -141.3894805908203125), + Vec3fEq(83.73651885986328125, -52.2005767822265625, -142.3761444091796875), + Vec3fEq(79.19786834716796875, -80.16802978515625, -143.114837646484375), + Vec3fEq(64.8477935791015625, -104.598602294921875, -137.840911865234375), + Vec3fEq(50.497714996337890625, -129.0291748046875, -131.45831298828125), + Vec3fEq(36.147632598876953125, -153.459747314453125, -121.42321014404296875), + Vec3fEq(21.7975559234619140625, -177.8903350830078125, -111.38811492919921875), + Vec3fEq(7.44747829437255859375, -202.3209075927734375, -101.19382476806640625), + Vec3fEq(0, -215, -94.75363922119140625) + )); } TEST_F(DetourNavigatorNavigatorTest, update_remove_and_update_then_find_path_should_return_path) @@ -639,31 +645,31 @@ namespace EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); - EXPECT_EQ(mPath, std::deque({ - osg::Vec3f(-215, 215, 1.85963428020477294921875), - osg::Vec3f(-194.9653167724609375, 194.9653167724609375, -6.57602214813232421875), - osg::Vec3f(-174.930633544921875, 174.930633544921875, -15.01167774200439453125), - osg::Vec3f(-154.8959503173828125, 154.8959503173828125, -23.4473361968994140625), - osg::Vec3f(-134.86126708984375, 134.86126708984375, -31.8829936981201171875), - osg::Vec3f(-114.82657623291015625, 114.82657623291015625, -40.3186492919921875), - osg::Vec3f(-94.7918853759765625, 94.7918853759765625, -47.3990631103515625), - osg::Vec3f(-74.75719451904296875, 74.75719451904296875, -53.7258148193359375), - osg::Vec3f(-54.722499847412109375, 54.722499847412109375, -60.052555084228515625), - osg::Vec3f(-34.68780517578125, 34.68780517578125, -66.37931060791015625), - osg::Vec3f(-14.6531162261962890625, 14.6531162261962890625, -72.70604705810546875), - osg::Vec3f(5.3815765380859375, -5.3815765380859375, -75.35065460205078125), - osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), - osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375), - osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), - osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.8258514404296875), - osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), - osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625), - osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625), - osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625), - osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625), - osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125), - osg::Vec3f(215, -215, 1.87718021869659423828125), - })) << mPath; + EXPECT_THAT(mPath, ElementsAre( + Vec3fEq(-215, 215, 1.85963428020477294921875), + Vec3fEq(-194.9653167724609375, 194.9653167724609375, -6.57602214813232421875), + Vec3fEq(-174.930633544921875, 174.930633544921875, -15.01167774200439453125), + Vec3fEq(-154.8959503173828125, 154.8959503173828125, -23.4473361968994140625), + Vec3fEq(-134.86126708984375, 134.86126708984375, -31.8829936981201171875), + Vec3fEq(-114.82657623291015625, 114.82657623291015625, -40.3186492919921875), + Vec3fEq(-94.7918853759765625, 94.7918853759765625, -47.3990631103515625), + Vec3fEq(-74.75719451904296875, 74.75719451904296875, -53.7258148193359375), + Vec3fEq(-54.722499847412109375, 54.722499847412109375, -60.052555084228515625), + Vec3fEq(-34.68780517578125, 34.68780517578125, -66.37931060791015625), + Vec3fEq(-14.6531162261962890625, 14.6531162261962890625, -72.70604705810546875), + Vec3fEq(5.3815765380859375, -5.3815765380859375, -75.35065460205078125), + Vec3fEq(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), + Vec3fEq(45.450958251953125, -45.450958251953125, -60.5882568359375), + Vec3fEq(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), + Vec3fEq(85.5203399658203125, -85.5203399658203125, -45.8258514404296875), + Vec3fEq(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), + Vec3fEq(125.5897216796875, -125.5897216796875, -31.063449859619140625), + Vec3fEq(145.6244049072265625, -145.6244049072265625, -23.6822509765625), + Vec3fEq(165.659088134765625, -165.659088134765625, -16.3010501861572265625), + Vec3fEq(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625), + Vec3fEq(205.7284698486328125, -205.7284698486328125, -1.5386505126953125), + Vec3fEq(215, -215, 1.87718021869659423828125) + )); } TEST_F(DetourNavigatorNavigatorTest, update_then_find_random_point_around_circle_should_return_position) @@ -687,10 +693,10 @@ namespace const auto result = mNavigator->findRandomPointAroundCircle(mAgentHalfExtents, mStart, 100.0, Flag_walk); - ASSERT_EQ(result, boost::optional(osg::Vec3f(-209.95985412597656, 129.89768981933594, -0.26253718137741089))); + ASSERT_THAT(result, Optional(Vec3fEq(-209.95985412597656, 129.89768981933594, -0.26253718137741089))); const auto distance = (*result - mStart).length(); - EXPECT_EQ(distance, 85.260780334472656) << distance; + EXPECT_FLOAT_EQ(distance, 85.260780334472656); } } diff --git a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp index af6797cf0..6c474765d 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp @@ -12,6 +12,7 @@ #include #include +#include namespace DetourNavigator { @@ -312,11 +313,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({ 0, -0.70710659027099609375, -3.535533905029296875, 0, 0.707107067108154296875, -3.535533905029296875, 0, 2.384185791015625e-07, -4.24264049530029296875, - })); + }))); EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2})); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector({AreaType_ground})); } @@ -337,11 +338,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({ -3.535533905029296875, -0.70710659027099609375, 0, -3.535533905029296875, 0.707107067108154296875, 0, -4.24264049530029296875, 2.384185791015625e-07, 0, - })); + }))); EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2})); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector({AreaType_ground})); } From 2b54e6216b4b8773d3b7481684540849ac5a5e82 Mon Sep 17 00:00:00 2001 From: p4r4digm Date: Sun, 19 Apr 2020 16:34:00 -0700 Subject: [PATCH 083/226] 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 084/226] 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 8a8107e837f215bbdca1d0ef33ac828d35a43267 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 20 Apr 2020 18:47:14 +0200 Subject: [PATCH 085/226] as it says; revert vismask and uncomplicate openmw --- apps/opencs/CMakeLists.txt | 4 ++ apps/opencs/view/render/brushdraw.cpp | 6 +-- apps/opencs/view/render/cell.cpp | 22 ++++---- apps/opencs/view/render/cellarrow.cpp | 7 +-- apps/opencs/view/render/cellborder.cpp | 5 +- apps/opencs/view/render/cellmarker.cpp | 4 +- apps/opencs/view/render/cellwater.cpp | 4 +- apps/opencs/view/render/instancemode.cpp | 50 +++++++++--------- .../view/render/instanceselectionmode.cpp | 8 ++- apps/opencs/view/render/mask.hpp | 35 +++++++++++++ apps/opencs/view/render/object.cpp | 8 +-- .../view/render/pagedworldspacewidget.cpp | 17 +++--- apps/opencs/view/render/pathgrid.cpp | 5 +- apps/opencs/view/render/pathgridmode.cpp | 22 ++++---- .../view/render/pathgridselectionmode.cpp | 6 +-- apps/opencs/view/render/scenewidget.cpp | 14 ++--- apps/opencs/view/render/selectionmode.hpp | 2 + apps/opencs/view/render/tagbase.cpp | 4 +- apps/opencs/view/render/tagbase.hpp | 8 +-- apps/opencs/view/render/terrainshapemode.cpp | 4 +- .../opencs/view/render/terraintexturemode.cpp | 4 +- .../view/render/unpagedworldspacewidget.cpp | 5 +- apps/opencs/view/render/worldspacewidget.cpp | 13 +++-- apps/opencs/view/render/worldspacewidget.hpp | 1 + apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 5 +- apps/openmw/mwclass/activator.cpp | 4 +- apps/openmw/mwclass/door.cpp | 4 +- apps/openmw/mwclass/static.cpp | 4 +- apps/openmw/mwgui/loadingscreen.cpp | 7 +-- apps/openmw/mwgui/windowmanagerimp.cpp | 7 +-- apps/openmw/mwmechanics/actors.cpp | 11 ++-- apps/openmw/mwrender/actoranimation.cpp | 7 +-- apps/openmw/mwrender/actorspaths.cpp | 4 +- apps/openmw/mwrender/animation.cpp | 10 ++-- apps/openmw/mwrender/bulletdebugdraw.cpp | 4 +- apps/openmw/mwrender/characterpreview.cpp | 12 ++--- apps/openmw/mwrender/effectmanager.cpp | 4 +- apps/openmw/mwrender/globalmap.cpp | 7 +-- apps/openmw/mwrender/localmap.cpp | 11 ++-- apps/openmw/mwrender/navmesh.cpp | 4 +- apps/openmw/mwrender/npcanimation.cpp | 4 +- apps/openmw/mwrender/objects.cpp | 9 ++-- apps/openmw/mwrender/pathgrid.cpp | 5 +- apps/openmw/mwrender/recastmesh.cpp | 7 +-- apps/openmw/mwrender/renderingmanager.cpp | 52 ++++++++++--------- apps/openmw/mwrender/ripplesimulation.cpp | 5 +- apps/openmw/mwrender/sky.cpp | 24 ++++----- .../openmw/mwrender}/vismask.hpp | 22 ++------ apps/openmw/mwrender/water.cpp | 28 +++++----- apps/openmw/mwworld/projectilemanager.cpp | 6 +-- apps/openmw/mwworld/worldimp.cpp | 4 +- components/CMakeLists.txt | 2 +- components/nifosg/controller.cpp | 3 +- components/nifosg/nifloader.cpp | 7 ++- components/resource/stats.cpp | 5 +- components/sceneutil/lightutil.cpp | 11 ++-- components/sceneutil/lightutil.hpp | 4 +- components/sceneutil/optimizer.hpp | 4 +- components/sceneutil/shadow.cpp | 3 +- components/terrain/cellborder.cpp | 9 ++-- components/terrain/cellborder.hpp | 3 +- components/terrain/quadtreeworld.cpp | 7 ++- components/terrain/quadtreeworld.hpp | 2 +- components/terrain/terraingrid.cpp | 4 +- components/terrain/terraingrid.hpp | 2 +- components/terrain/world.cpp | 11 ++-- components/terrain/world.hpp | 2 +- 68 files changed, 320 insertions(+), 290 deletions(-) create mode 100644 apps/opencs/view/render/mask.hpp rename {components/sceneutil => apps/openmw/mwrender}/vismask.hpp (82%) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 6d0f2ad9f..5dc2fb26f 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -97,6 +97,10 @@ opencs_units_noqt (view/render cellarrow cellmarker cellborder pathgrid ) +opencs_hdrs_noqt (view/render + mask + ) + opencs_units (view/tools reportsubview reporttable searchsubview searchbox merge diff --git a/apps/opencs/view/render/brushdraw.cpp b/apps/opencs/view/render/brushdraw.cpp index 992540851..255a13a12 100644 --- a/apps/opencs/view/render/brushdraw.cpp +++ b/apps/opencs/view/render/brushdraw.cpp @@ -8,10 +8,9 @@ #include -#include - #include "../../model/world/cellcoordinates.hpp" #include "../widget/brushshapes.hpp" +#include "mask.hpp" CSVRender::BrushDraw::BrushDraw(osg::ref_ptr parentNode, bool textureMode) : mParentNode(parentNode), mTextureMode(textureMode) @@ -46,7 +45,7 @@ float CSVRender::BrushDraw::getIntersectionHeight (const osg::Vec3d& point) intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); osgUtil::IntersectionVisitor visitor(intersector); - visitor.setTraversalMask(SceneUtil::Mask_Terrain); + visitor.setTraversalMask(Mask_Terrain); mParentNode->accept(visitor); @@ -258,7 +257,6 @@ void CSVRender::BrushDraw::update(osg::Vec3d point, int brushSize, CSVWidget::Br { if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); - mBrushDrawNode->setNodeMask (SceneUtil::Mask_GUI); float radius = (mLandSizeFactor * brushSize) / 2; osg::Vec3d snapToGridPoint = point; if (mTextureMode) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 48659865a..056c50e45 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include "../../model/world/idtable.hpp" @@ -22,6 +21,7 @@ #include "cellborder.hpp" #include "cellarrow.hpp" #include "cellmarker.hpp" +#include "mask.hpp" #include "pathgrid.hpp" #include "terrainstorage.hpp" #include "object.hpp" @@ -92,7 +92,7 @@ bool CSVRender::Cell::addObjects (int start, int end) std::unique_ptr object (new Object (mData, mCellNode, id, false)); - if (mSubModeElementMask & SceneUtil::Mask_EditorReference) + if (mSubModeElementMask & Mask_Reference) object->setSubMode (mSubMode); mObjects.insert (std::make_pair (id, object.release())); @@ -134,7 +134,7 @@ void CSVRender::Cell::updateLand() else { mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, - mData.getResourceSystem().get(), mTerrainStorage)); + mData.getResourceSystem().get(), mTerrainStorage, Mask_Terrain)); } mTerrain->loadCell(esmLand.mX, esmLand.mY); @@ -434,7 +434,7 @@ void CSVRender::Cell::reloadAssets() void CSVRender::Cell::setSelection (int elementMask, Selection mode) { - if (elementMask & SceneUtil::Mask_EditorReference) + if (elementMask & Mask_Reference) { for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) @@ -451,7 +451,7 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode) iter->second->setSelected (selected); } } - if (mPathgrid && elementMask & SceneUtil::Mask_Pathgrid) + if (mPathgrid && elementMask & Mask_Pathgrid) { // Only one pathgrid may be selected, so some operations will only have an effect // if the pathgrid is already focused @@ -546,12 +546,12 @@ std::vector > CSVRender::Cell::getSelection (un { std::vector > result; - if (elementMask & SceneUtil::Mask_EditorReference) + if (elementMask & Mask_Reference) for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) if (iter->second->getSelected()) result.push_back (iter->second->getTag()); - if (mPathgrid && elementMask & SceneUtil::Mask_Pathgrid) + if (mPathgrid && elementMask & Mask_Pathgrid) if (mPathgrid->isSelected()) result.push_back(mPathgrid->getTag()); @@ -562,7 +562,7 @@ std::vector > CSVRender::Cell::getEdited (unsig { std::vector > result; - if (elementMask & SceneUtil::Mask_EditorReference) + if (elementMask & Mask_Reference) for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) if (iter->second->isEdited()) @@ -576,7 +576,7 @@ void CSVRender::Cell::setSubMode (int subMode, unsigned int elementMask) mSubMode = subMode; mSubModeElementMask = elementMask; - if (elementMask & SceneUtil::Mask_EditorReference) + if (elementMask & Mask_Reference) for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) iter->second->setSubMode (subMode); @@ -584,10 +584,10 @@ void CSVRender::Cell::setSubMode (int subMode, unsigned int elementMask) void CSVRender::Cell::reset (unsigned int elementMask) { - if (elementMask & SceneUtil::Mask_EditorReference) + if (elementMask & Mask_Reference) for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) iter->second->reset(); - if (mPathgrid && elementMask & SceneUtil::Mask_Pathgrid) + if (mPathgrid && elementMask & Mask_Pathgrid) mPathgrid->resetIndicators(); } diff --git a/apps/opencs/view/render/cellarrow.cpp b/apps/opencs/view/render/cellarrow.cpp index 3fb4f711d..b6fee1545 100644 --- a/apps/opencs/view/render/cellarrow.cpp +++ b/apps/opencs/view/render/cellarrow.cpp @@ -11,10 +11,11 @@ #include "../../model/prefs/shortcutmanager.hpp" #include -#include + +#include "mask.hpp" CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow) -: TagBase (SceneUtil::Mask_EditorCellArrow), mArrow (arrow) +: TagBase (Mask_CellArrow), mArrow (arrow) {} CSVRender::CellArrow *CSVRender::CellArrowTag::getCellArrow() const @@ -174,7 +175,7 @@ CSVRender::CellArrow::CellArrow (osg::Group *cellNode, Direction direction, mParentNode->addChild (mBaseNode); - mBaseNode->setNodeMask (SceneUtil::Mask_EditorCellArrow); + mBaseNode->setNodeMask (Mask_CellArrow); adjustTransform(); buildShape(); diff --git a/apps/opencs/view/render/cellborder.cpp b/apps/opencs/view/render/cellborder.cpp index 0789ee22a..6073807ce 100644 --- a/apps/opencs/view/render/cellborder.cpp +++ b/apps/opencs/view/render/cellborder.cpp @@ -7,7 +7,8 @@ #include #include -#include + +#include "mask.hpp" #include "../../model/world/cellcoordinates.hpp" @@ -19,7 +20,7 @@ CSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoor : mParentNode(cellNode) { mBaseNode = new osg::PositionAttitudeTransform(); - mBaseNode->setNodeMask(SceneUtil::Mask_EditorCellBorder); + mBaseNode->setNodeMask(Mask_CellBorder); mBaseNode->setPosition(osg::Vec3f(coords.getX() * CellSize, coords.getY() * CellSize, 10)); mParentNode->addChild(mBaseNode); diff --git a/apps/opencs/view/render/cellmarker.cpp b/apps/opencs/view/render/cellmarker.cpp index e629aa827..3de96ab02 100644 --- a/apps/opencs/view/render/cellmarker.cpp +++ b/apps/opencs/view/render/cellmarker.cpp @@ -8,7 +8,7 @@ #include CSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker) -: TagBase(SceneUtil::Mask_EditorCellMarker), mMarker(marker) +: TagBase(Mask_CellMarker), mMarker(marker) {} CSVRender::CellMarker *CSVRender::CellMarkerTag::getCellMarker() const @@ -79,7 +79,7 @@ CSVRender::CellMarker::CellMarker( mMarkerNode->getOrCreateStateSet()->setAttribute(mat); mMarkerNode->setUserData(new CellMarkerTag(this)); - mMarkerNode->setNodeMask(SceneUtil::Mask_EditorCellMarker); + mMarkerNode->setNodeMask(Mask_CellMarker); mCellNode->addChild(mMarkerNode); diff --git a/apps/opencs/view/render/cellwater.cpp b/apps/opencs/view/render/cellwater.cpp index 8edbc1cd1..435178860 100644 --- a/apps/opencs/view/render/cellwater.cpp +++ b/apps/opencs/view/render/cellwater.cpp @@ -11,12 +11,12 @@ #include #include #include -#include #include "../../model/world/cell.hpp" #include "../../model/world/cellcoordinates.hpp" #include "../../model/world/data.hpp" +#include "mask.hpp" namespace CSVRender { @@ -38,7 +38,7 @@ namespace CSVRender mWaterTransform->setPosition(osg::Vec3f(cellCoords.getX() * CellSize + CellSize / 2.f, cellCoords.getY() * CellSize + CellSize / 2.f, 0)); - mWaterTransform->setNodeMask(SceneUtil::Mask_Water); + mWaterTransform->setNodeMask(Mask_Water); mParentNode->addChild(mWaterTransform); mWaterNode = new osg::Geode(); diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 0d221beb1..987dea437 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -21,7 +21,7 @@ #include "../widget/scenetoolbar.hpp" #include "../widget/scenetoolmode.hpp" -#include +#include "mask.hpp" #include "object.hpp" #include "worldspacewidget.hpp" @@ -97,7 +97,7 @@ osg::Vec3f CSVRender::InstanceMode::getScreenCoords(const osg::Vec3f& pos) } CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, osg::ref_ptr parentNode, QWidget *parent) -: EditMode (worldspaceWidget, QIcon (":scenetoolbar/editing-instance"), SceneUtil::Mask_EditorReference | SceneUtil::Mask_Terrain, "Instance editing", +: EditMode (worldspaceWidget, QIcon (":scenetoolbar/editing-instance"), Mask_Reference | Mask_Terrain, "Instance editing", parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None), mDragAxis (-1), mLocked (false), mUnitScaleDist(1), mParentNode (parentNode) { @@ -157,13 +157,13 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) std::string subMode = mSubMode->getCurrentId(); - getWorldspaceWidget().setSubMode (getSubModeFromId (subMode), SceneUtil::Mask_EditorReference); + getWorldspaceWidget().setSubMode (getSubModeFromId (subMode), Mask_Reference); } void CSVRender::InstanceMode::deactivate (CSVWidget::SceneToolbar *toolbar) { mDragMode = DragMode_None; - getWorldspaceWidget().reset (SceneUtil::Mask_EditorReference); + getWorldspaceWidget().reset (Mask_Reference); if (mSelectionMode) { @@ -216,7 +216,7 @@ void CSVRender::InstanceMode::secondaryEditPressed (const WorldspaceHitResult& h void CSVRender::InstanceMode::primarySelectPressed (const WorldspaceHitResult& hit) { - getWorldspaceWidget().clearSelection (SceneUtil::Mask_EditorReference); + getWorldspaceWidget().clearSelection (Mask_Reference); if (hit.tag) { @@ -251,13 +251,13 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); - std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Reference); if (selection.empty()) { // Only change selection at the start of drag if no object is already selected if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) { - getWorldspaceWidget().clearSelection (SceneUtil::Mask_EditorReference); + getWorldspaceWidget().clearSelection (Mask_Reference); if (CSVRender::ObjectTag *objectTag = dynamic_cast (hit.tag.get())) { CSVRender::Object* object = objectTag->mObject; @@ -265,7 +265,7 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) } } - selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); + selection = getWorldspaceWidget().getSelection (Mask_Reference); if (selection.empty()) return false; } @@ -291,7 +291,7 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) mDragMode = DragMode_Scale; // Calculate scale factor - std::vector > editedSelection = getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference); + std::vector > editedSelection = getWorldspaceWidget().getEdited (Mask_Reference); osg::Vec3f center = getScreenCoords(getSelectionCenter(editedSelection)); int widgetHeight = getWorldspaceWidget().height(); @@ -327,7 +327,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou osg::Vec3f offset; osg::Quat rotation; - std::vector > selection = getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference); + std::vector > selection = getWorldspaceWidget().getEdited (Mask_Reference); if (mDragMode == DragMode_Move) { @@ -484,7 +484,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou void CSVRender::InstanceMode::dragCompleted(const QPoint& pos) { std::vector > selection = - getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference); + getWorldspaceWidget().getEdited (Mask_Reference); QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); @@ -516,7 +516,7 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos) void CSVRender::InstanceMode::dragAborted() { - getWorldspaceWidget().reset (SceneUtil::Mask_EditorReference); + getWorldspaceWidget().reset (Mask_Reference); mDragMode = DragMode_None; } @@ -535,7 +535,7 @@ void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor) offset *= diff * speedFactor; std::vector > selection = - getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference); + getWorldspaceWidget().getEdited (Mask_Reference); for (std::vector >::iterator iter (selection.begin()); iter!=selection.end(); ++iter) @@ -677,12 +677,12 @@ void CSVRender::InstanceMode::subModeChanged (const std::string& id) { mSubModeId = id; getWorldspaceWidget().abortDrag(); - getWorldspaceWidget().setSubMode (getSubModeFromId (id), SceneUtil::Mask_EditorReference); + getWorldspaceWidget().setSubMode (getSubModeFromId (id), Mask_Reference); } void CSVRender::InstanceMode::deleteSelectedInstances(bool active) { - std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Reference); if (selection.empty()) return; CSMDoc::Document& document = getWorldspaceWidget().getDocument(); @@ -695,7 +695,7 @@ void CSVRender::InstanceMode::deleteSelectedInstances(bool active) if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) macro.push(new CSMWorld::DeleteCommand(referencesTable, objectTag->mObject->getReferenceId())); - getWorldspaceWidget().clearSelection (SceneUtil::Mask_EditorReference); + getWorldspaceWidget().clearSelection (Mask_Reference); } void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* object, float objectHeight) @@ -713,9 +713,9 @@ void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* osgUtil::IntersectionVisitor visitor(intersector); if (dropMode == TerrainSep) - visitor.setTraversalMask(SceneUtil::Mask_Terrain); + visitor.setTraversalMask(Mask_Terrain); if (dropMode == CollisionSep) - visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference); + visitor.setTraversalMask(Mask_Terrain | Mask_Reference); mParentNode->accept(visitor); @@ -745,9 +745,9 @@ float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Objec osgUtil::IntersectionVisitor visitor(intersector); if (dropMode == Terrain) - visitor.setTraversalMask(SceneUtil::Mask_Terrain); + visitor.setTraversalMask(Mask_Terrain); if (dropMode == Collision) - visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference); + visitor.setTraversalMask(Mask_Terrain | Mask_Reference); mParentNode->accept(visitor); @@ -784,7 +784,7 @@ void CSVRender::InstanceMode::dropSelectedInstancesToTerrainSeparately() void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString commandMsg) { - std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Reference); if (selection.empty()) return; @@ -841,7 +841,7 @@ void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString comman CSVRender::DropObjectDataHandler::DropObjectDataHandler(WorldspaceWidget* worldspacewidget) : mWorldspaceWidget(worldspacewidget) { - std::vector > selection = mWorldspaceWidget->getSelection (SceneUtil::Mask_EditorReference); + std::vector > selection = mWorldspaceWidget->getSelection (Mask_Reference); for(osg::ref_ptr tag: selection) { if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) @@ -850,7 +850,7 @@ CSVRender::DropObjectDataHandler::DropObjectDataHandler(WorldspaceWidget* worlds osg::ref_ptr objectNodeWithoutGUI = objectTag->mObject->getBaseNode(); osg::ComputeBoundsVisitor computeBounds; - computeBounds.setTraversalMask(SceneUtil::Mask_EditorReference); + computeBounds.setTraversalMask(Mask_Reference); objectNodeWithoutGUI->accept(computeBounds); osg::BoundingBox bounds = computeBounds.getBoundingBox(); float boundingBoxOffset = 0.0f; @@ -860,14 +860,14 @@ CSVRender::DropObjectDataHandler::DropObjectDataHandler(WorldspaceWidget* worlds mObjectHeights.emplace_back(boundingBoxOffset); mOldMasks.emplace_back(objectNodeWithGUI->getNodeMask()); - objectNodeWithGUI->setNodeMask(SceneUtil::Mask_Disabled); + objectNodeWithGUI->setNodeMask(0); } } } CSVRender::DropObjectDataHandler::~DropObjectDataHandler() { - std::vector > selection = mWorldspaceWidget->getSelection (SceneUtil::Mask_EditorReference); + std::vector > selection = mWorldspaceWidget->getSelection (Mask_Reference); int counter = 0; for(osg::ref_ptr tag: selection) { diff --git a/apps/opencs/view/render/instanceselectionmode.cpp b/apps/opencs/view/render/instanceselectionmode.cpp index 470f34e5d..bf8ede0eb 100644 --- a/apps/opencs/view/render/instanceselectionmode.cpp +++ b/apps/opencs/view/render/instanceselectionmode.cpp @@ -6,15 +6,13 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/commands.hpp" -#include - #include "worldspacewidget.hpp" #include "object.hpp" namespace CSVRender { InstanceSelectionMode::InstanceSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget) - : SelectionMode(parent, worldspaceWidget, SceneUtil::Mask_EditorReference) + : SelectionMode(parent, worldspaceWidget, Mask_Reference) { mSelectSame = new QAction("Extend selection to instances with same object ID", this); mDeleteSelection = new QAction("Delete selected instances", this); @@ -38,12 +36,12 @@ namespace CSVRender void InstanceSelectionMode::selectSame() { - getWorldspaceWidget().selectAllWithSameParentId(SceneUtil::Mask_EditorReference); + getWorldspaceWidget().selectAllWithSameParentId(Mask_Reference); } void InstanceSelectionMode::deleteSelection() { - std::vector > selection = getWorldspaceWidget().getSelection(SceneUtil::Mask_EditorReference); + std::vector > selection = getWorldspaceWidget().getSelection(Mask_Reference); CSMWorld::IdTable& referencesTable = dynamic_cast( *getWorldspaceWidget().getDocument().getData().getTableModel(CSMWorld::UniversalId::Type_References)); diff --git a/apps/opencs/view/render/mask.hpp b/apps/opencs/view/render/mask.hpp new file mode 100644 index 000000000..55b7c823f --- /dev/null +++ b/apps/opencs/view/render/mask.hpp @@ -0,0 +1,35 @@ +#ifndef CSV_RENDER_ELEMENTS_H +#define CSV_RENDER_ELEMENTS_H + +namespace CSVRender +{ + + /// Node masks used on the OSG scene graph in OpenMW-CS. + /// @note See the respective file in OpenMW (apps/openmw/mwrender/vismask.hpp) + /// for general usage hints about node masks. + /// @copydoc MWRender::VisMask + enum Mask + { + // internal use within NifLoader, do not change + Mask_UpdateVisitor = 0x1, + + // elements that are part of the actual scene + Mask_Reference = 0x2, + Mask_Pathgrid = 0x4, + Mask_Water = 0x8, + Mask_Fog = 0x10, + Mask_Terrain = 0x20, + + // used within models + Mask_ParticleSystem = 0x100, + + Mask_Lighting = 0x200, + + // control elements + Mask_CellMarker = 0x10000, + Mask_CellArrow = 0x20000, + Mask_CellBorder = 0x40000 + }; +} + +#endif diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 6b33cdeb2..f9d2c8872 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -29,9 +29,9 @@ #include #include #include -#include #include "actor.hpp" +#include "mask.hpp" const float CSVRender::Object::MarkerShaftWidth = 30; @@ -58,7 +58,7 @@ namespace CSVRender::ObjectTag::ObjectTag (Object* object) -: TagBase (SceneUtil::Mask_EditorReference), mObject (object) +: TagBase (Mask_Reference), mObject (object) {} QString CSVRender::ObjectTag::getToolTip (bool hideBasics) const @@ -140,7 +140,7 @@ void CSVRender::Object::update() if (light) { bool isExterior = false; // FIXME - SceneUtil::addLight(mBaseNode, light, isExterior); + SceneUtil::addLight(mBaseNode, light, Mask_ParticleSystem, Mask_Lighting, isExterior); } } @@ -429,7 +429,7 @@ CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode, parentNode->addChild (mRootNode); - mRootNode->setNodeMask(SceneUtil::Mask_EditorReference); + mRootNode->setNodeMask(Mask_Reference); if (referenceable) { diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 8fafa8459..b5d9234e4 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -21,6 +21,7 @@ #include "../widget/scenetooltoggle2.hpp" #include "editmode.hpp" +#include "mask.hpp" #include "cameracontroller.hpp" #include "cellarrow.hpp" #include "terraintexturemode.hpp" @@ -126,8 +127,8 @@ void CSVRender::PagedWorldspaceWidget::addVisibilitySelectorButtons ( CSVWidget::SceneToolToggle2 *tool) { WorldspaceWidget::addVisibilitySelectorButtons (tool); - tool->addButton (Button_Terrain, SceneUtil::Mask_Terrain, "Terrain"); - //tool->addButton (Button_Fog, Mask_Fog, "Fog", "", true); + tool->addButton (Button_Terrain, Mask_Terrain, "Terrain"); + tool->addButton (Button_Fog, Mask_Fog, "Fog", "", true); } void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( @@ -141,16 +142,16 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( tool->addButton ( new TerrainTextureMode (this, mRootNode, tool), "terrain-texture"); tool->addButton ( - new EditMode (this, QIcon (":placeholder"), SceneUtil::Mask_EditorReference, "Terrain vertex paint editing"), + new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), "terrain-vertex"); tool->addButton ( - new EditMode (this, QIcon (":placeholder"), SceneUtil::Mask_EditorReference, "Terrain movement"), + new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain movement"), "terrain-move"); } void CSVRender::PagedWorldspaceWidget::handleInteractionPress (const WorldspaceHitResult& hit, InteractionType type) { - if (hit.tag && hit.tag->getMask()==SceneUtil::Mask_EditorCellArrow) + if (hit.tag && hit.tag->getMask()==Mask_CellArrow) { if (CellArrowTag *cellArrowTag = dynamic_cast (hit.tag.get())) { @@ -873,9 +874,9 @@ CSVWidget::SceneToolToggle2 *CSVRender::PagedWorldspaceWidget::makeControlVisibi mControlElements = new CSVWidget::SceneToolToggle2 (parent, "Controls & Guides Visibility", ":scenetoolbar/scene-view-marker-c", ":scenetoolbar/scene-view-marker-"); - mControlElements->addButton (1, SceneUtil::Mask_EditorCellMarker, "Cell Marker"); - mControlElements->addButton (2, SceneUtil::Mask_EditorCellArrow, "Cell Arrows"); - mControlElements->addButton (4, SceneUtil::Mask_EditorCellBorder, "Cell Border"); + mControlElements->addButton (1, Mask_CellMarker, "Cell Marker"); + mControlElements->addButton (2, Mask_CellArrow, "Cell Arrows"); + mControlElements->addButton (4, Mask_CellBorder, "Cell Border"); mControlElements->setSelectionMask (0xffffffff); diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index b2714014b..d8acfe2e1 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -10,7 +10,6 @@ #include #include -#include #include "../../model/world/cell.hpp" #include "../../model/world/commands.hpp" @@ -32,7 +31,7 @@ namespace CSVRender }; PathgridTag::PathgridTag(Pathgrid* pathgrid) - : TagBase(SceneUtil::Mask_Pathgrid), mPathgrid(pathgrid) + : TagBase(Mask_Pathgrid), mPathgrid(pathgrid) { } @@ -71,7 +70,7 @@ namespace CSVRender mBaseNode->setPosition(osg::Vec3f(mCoords.getX() * CoordScalar, mCoords.getY() * CoordScalar, 0.f)); mBaseNode->setUserData(mTag); mBaseNode->setUpdateCallback(new PathgridNodeCallback()); - mBaseNode->setNodeMask(SceneUtil::Mask_Pathgrid); + mBaseNode->setNodeMask(Mask_Pathgrid); mParent->addChild(mBaseNode); mPathgridGeode = new osg::Geode(); diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index 33c1b8b42..8863ad235 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -4,7 +4,6 @@ #include #include -#include #include "../../model/prefs/state.hpp" @@ -16,6 +15,7 @@ #include "../widget/scenetoolbar.hpp" #include "cell.hpp" +#include "mask.hpp" #include "pathgrid.hpp" #include "pathgridselectionmode.hpp" #include "worldspacewidget.hpp" @@ -23,7 +23,7 @@ namespace CSVRender { PathgridMode::PathgridMode(WorldspaceWidget* worldspaceWidget, QWidget* parent) - : EditMode(worldspaceWidget, QIcon(":placeholder"), SceneUtil::Mask_Pathgrid | SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference, + : EditMode(worldspaceWidget, QIcon(":placeholder"), Mask_Pathgrid | Mask_Terrain | Mask_Reference, getTooltip(), parent) , mDragMode(DragMode_None) , mFromNode(0) @@ -110,7 +110,7 @@ namespace CSVRender void PathgridMode::primarySelectPressed(const WorldspaceHitResult& hit) { - getWorldspaceWidget().clearSelection(SceneUtil::Mask_Pathgrid); + getWorldspaceWidget().clearSelection(Mask_Pathgrid); if (hit.tag) { @@ -131,7 +131,7 @@ namespace CSVRender { if (tag->getPathgrid()->getId() != mLastId) { - getWorldspaceWidget().clearSelection(SceneUtil::Mask_Pathgrid); + getWorldspaceWidget().clearSelection(Mask_Pathgrid); mLastId = tag->getPathgrid()->getId(); } @@ -142,12 +142,12 @@ namespace CSVRender } } - getWorldspaceWidget().clearSelection(SceneUtil::Mask_Pathgrid); + getWorldspaceWidget().clearSelection(Mask_Pathgrid); } bool PathgridMode::primaryEditStartDrag(const QPoint& pos) { - std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid); + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) { @@ -156,7 +156,7 @@ namespace CSVRender if (dynamic_cast(hit.tag.get())) { primarySelectPressed(hit); - selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid); + selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); } } @@ -192,7 +192,7 @@ namespace CSVRender { if (mDragMode == DragMode_Move) { - std::vector > selection = getWorldspaceWidget().getSelection(SceneUtil::Mask_Pathgrid); + std::vector > selection = getWorldspaceWidget().getSelection(Mask_Pathgrid); for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) { @@ -233,7 +233,7 @@ namespace CSVRender { if (mDragMode == DragMode_Move) { - std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid); + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) { if (PathgridTag* tag = dynamic_cast(it->get())) @@ -272,11 +272,11 @@ namespace CSVRender } mDragMode = DragMode_None; - getWorldspaceWidget().reset(SceneUtil::Mask_Pathgrid); + getWorldspaceWidget().reset(Mask_Pathgrid); } void PathgridMode::dragAborted() { - getWorldspaceWidget().reset(SceneUtil::Mask_Pathgrid); + getWorldspaceWidget().reset(Mask_Pathgrid); } } diff --git a/apps/opencs/view/render/pathgridselectionmode.cpp b/apps/opencs/view/render/pathgridselectionmode.cpp index 43050d52a..db41faf50 100644 --- a/apps/opencs/view/render/pathgridselectionmode.cpp +++ b/apps/opencs/view/render/pathgridselectionmode.cpp @@ -13,7 +13,7 @@ namespace CSVRender { PathgridSelectionMode::PathgridSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget) - : SelectionMode(parent, worldspaceWidget, SceneUtil::Mask_Pathgrid) + : SelectionMode(parent, worldspaceWidget, Mask_Pathgrid) { mRemoveSelectedNodes = new QAction("Remove selected nodes", this); mRemoveSelectedEdges = new QAction("Remove edges between selected nodes", this); @@ -37,7 +37,7 @@ namespace CSVRender void PathgridSelectionMode::removeSelectedNodes() { - std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid); + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) { @@ -54,7 +54,7 @@ namespace CSVRender void PathgridSelectionMode::removeSelectedEdges() { - std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid); + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) { diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index cd671f875..ed232722d 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include "../widget/scenetoolmode.hpp" @@ -26,6 +25,7 @@ #include "../../model/prefs/shortcuteventhandler.hpp" #include "lighting.hpp" +#include "mask.hpp" #include "cameracontroller.hpp" namespace CSVRender @@ -71,7 +71,7 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f) SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager; lightMgr->setStartLight(1); - lightMgr->setLightingMask(SceneUtil::Mask_Lighting); + lightMgr->setLightingMask(Mask_Lighting); mRootNode = lightMgr; mView->getCamera()->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); @@ -88,7 +88,7 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f) // Add ability to signal osg to show its statistics for debugging purposes mView->addEventHandler(new osgViewer::StatsHandler); - mView->getCamera()->setCullMask(~(SceneUtil::Mask_UpdateVisitor)); + mView->getCamera()->setCullMask(~(Mask_UpdateVisitor)); viewer.addView(mView); viewer.setDone(false); @@ -122,7 +122,7 @@ void RenderWidget::flagAsModified() void RenderWidget::setVisibilityMask(int mask) { - mView->getCamera()->setCullMask(mask | SceneUtil::Mask_ParticleSystem | SceneUtil::Mask_Lighting); + mView->getCamera()->setCullMask(mask | Mask_ParticleSystem | Mask_Lighting); } osg::Camera *RenderWidget::getCamera() @@ -212,7 +212,7 @@ SceneWidget::SceneWidget(std::shared_ptr resourceSyste mOrbitCamControl = new OrbitCameraController(this); mCurrentCamControl = mFreeCamControl; - mOrbitCamControl->setPickingMask(SceneUtil::Mask_EditorReference | SceneUtil::Mask_Terrain); + mOrbitCamControl->setPickingMask(Mask_Reference | Mask_Terrain); mOrbitCamControl->setConstRoll( CSMPrefs::get()["3D Scene Input"]["navi-orbit-const-roll"].isTrue() ); @@ -221,7 +221,7 @@ SceneWidget::SceneWidget(std::shared_ptr resourceSyste setLighting(&mLightingDay); - mResourceSystem->getSceneManager()->setParticleSystemMask(SceneUtil::Mask_ParticleSystem); + mResourceSystem->getSceneManager()->setParticleSystemMask(Mask_ParticleSystem); // Recieve mouse move event even if mouse button is not pressed setMouseTracking(true); @@ -350,7 +350,7 @@ void SceneWidget::update(double dt) } else { - mCurrentCamControl->setup(mRootNode, SceneUtil::Mask_EditorReference | SceneUtil::Mask_Terrain, CameraController::WorldUp); + mCurrentCamControl->setup(mRootNode, Mask_Reference | Mask_Terrain, CameraController::WorldUp); mCamPositionSet = true; } } diff --git a/apps/opencs/view/render/selectionmode.hpp b/apps/opencs/view/render/selectionmode.hpp index 18c751290..f28888bfd 100644 --- a/apps/opencs/view/render/selectionmode.hpp +++ b/apps/opencs/view/render/selectionmode.hpp @@ -3,6 +3,8 @@ #include "../widget/scenetoolmode.hpp" +#include "mask.hpp" + class QAction; namespace CSVRender diff --git a/apps/opencs/view/render/tagbase.cpp b/apps/opencs/view/render/tagbase.cpp index bdd648102..3ddd68690 100644 --- a/apps/opencs/view/render/tagbase.cpp +++ b/apps/opencs/view/render/tagbase.cpp @@ -1,9 +1,9 @@ #include "tagbase.hpp" -CSVRender::TagBase::TagBase (SceneUtil::VisMask mask) : mMask (mask) {} +CSVRender::TagBase::TagBase (Mask mask) : mMask (mask) {} -SceneUtil::VisMask CSVRender::TagBase::getMask() const +CSVRender::Mask CSVRender::TagBase::getMask() const { return mMask; } diff --git a/apps/opencs/view/render/tagbase.hpp b/apps/opencs/view/render/tagbase.hpp index 6a0bc4aef..d1ecd2cfd 100644 --- a/apps/opencs/view/render/tagbase.hpp +++ b/apps/opencs/view/render/tagbase.hpp @@ -5,19 +5,19 @@ #include -#include +#include "mask.hpp" namespace CSVRender { class TagBase : public osg::Referenced { - SceneUtil::VisMask mMask; + Mask mMask; public: - TagBase (SceneUtil::VisMask mask); + TagBase (Mask mask); - SceneUtil::VisMask getMask() const; + Mask getMask() const; virtual QString getToolTip (bool hideBasics) const; diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 6df5ee836..5664378ca 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -17,7 +17,6 @@ #include #include -#include #include "../widget/brushshapes.hpp" #include "../widget/modebutton.hpp" @@ -40,12 +39,13 @@ #include "brushdraw.hpp" #include "editmode.hpp" #include "pagedworldspacewidget.hpp" +#include "mask.hpp" #include "tagbase.hpp" #include "terrainselection.hpp" #include "worldspacewidget.hpp" CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent) -: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference, "Terrain land editing", parent), +: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain | Mask_Reference, "Terrain land editing", parent), mParentNode(parentNode) { } diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index d4656b578..ae5711881 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -13,7 +13,6 @@ #include #include -#include #include "../widget/modebutton.hpp" #include "../widget/scenetoolbar.hpp" @@ -37,11 +36,12 @@ #include "brushdraw.hpp" #include "editmode.hpp" #include "pagedworldspacewidget.hpp" +#include "mask.hpp" #include "object.hpp" // Something small needed regarding pointers from here () #include "worldspacewidget.hpp" CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent) -: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference, "Terrain texture editing", parent), +: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent), mBrushTexture("L0#0"), mBrushSize(1), mBrushShape(CSVWidget::BrushShape_Point), diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 5f5441b83..b1088aa60 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -16,6 +16,7 @@ #include "../widget/scenetooltoggle2.hpp" #include "cameracontroller.hpp" +#include "mask.hpp" #include "tagbase.hpp" void CSVRender::UnpagedWorldspaceWidget::update() @@ -303,8 +304,8 @@ void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons ( CSVWidget::SceneToolToggle2 *tool) { WorldspaceWidget::addVisibilitySelectorButtons (tool); - tool->addButton (Button_Terrain, SceneUtil::Mask_Terrain, "Terrain", "", true); - //tool->addButton (Button_Fog, Mask_Fog, "Fog"); + tool->addButton (Button_Terrain, Mask_Terrain, "Terrain", "", true); + tool->addButton (Button_Fog, Mask_Fog, "Fog"); } std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction() diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 4755de97b..8e54b9a81 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -26,9 +26,8 @@ #include "../widget/scenetooltoggle2.hpp" #include "../widget/scenetoolrun.hpp" -#include - #include "object.hpp" +#include "mask.hpp" #include "instancemode.hpp" #include "pathgridmode.hpp" #include "cameracontroller.hpp" @@ -139,7 +138,7 @@ void CSVRender::WorldspaceWidget::settingChanged (const CSMPrefs::Setting *setti { float alpha = setting->toDouble(); // getSelection is virtual, thus this can not be called from the constructor - auto selection = getSelection(SceneUtil::Mask_EditorReference); + auto selection = getSelection(Mask_Reference); for (osg::ref_ptr tag : selection) { if (auto objTag = dynamic_cast(tag.get())) @@ -346,7 +345,7 @@ unsigned int CSVRender::WorldspaceWidget::getVisibilityMask() const void CSVRender::WorldspaceWidget::setInteractionMask (unsigned int mask) { - mInteractionMask = mask | SceneUtil::Mask_EditorCellMarker | SceneUtil::Mask_EditorCellArrow; + mInteractionMask = mask | Mask_CellMarker | Mask_CellArrow; } unsigned int CSVRender::WorldspaceWidget::getInteractionMask() const @@ -362,9 +361,9 @@ void CSVRender::WorldspaceWidget::setEditLock (bool locked) void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons ( CSVWidget::SceneToolToggle2 *tool) { - tool->addButton (Button_Reference, SceneUtil::Mask_EditorReference, "Instances"); - tool->addButton (Button_Water, SceneUtil::Mask_Water, "Water"); - tool->addButton (Button_Pathgrid, SceneUtil::Mask_Pathgrid, "Pathgrid"); + tool->addButton (Button_Reference, Mask_Reference, "Instances"); + tool->addButton (Button_Water, Mask_Water, "Water"); + tool->addButton (Button_Pathgrid, Mask_Pathgrid, "Pathgrid"); } void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool) diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 5ac63c673..a80032b82 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -8,6 +8,7 @@ #include "../../model/world/tablemimedata.hpp" #include "scenewidget.hpp" +#include "mask.hpp" namespace CSMPrefs { diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 4a375b8e9..57262f964 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -18,7 +18,7 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - actors objects renderingmanager animation rotatecontroller sky npcanimation + 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 diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index ec8c1e305..4a66f3a7a 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -26,7 +26,6 @@ #include -#include #include #include @@ -48,6 +47,8 @@ #include "mwworld/player.hpp" #include "mwworld/worldimp.hpp" +#include "mwrender/vismask.hpp" + #include "mwclass/classes.hpp" #include "mwdialogue/dialoguemanagerimp.hpp" @@ -537,7 +538,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) std::string myguiResources = (mResDir / "mygui").string(); osg::ref_ptr guiRoot = new osg::Group; guiRoot->setName("GUI Root"); - guiRoot->setNodeMask(SceneUtil::Mask_GUI); + guiRoot->setNodeMask(MWRender::Mask_GUI); rootNode->addChild(guiRoot); MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(), mCfgMgr.getLogPath().string() + std::string("/"), myguiResources, diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 3f4e13c1c..c54b1c369 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -20,6 +19,7 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" +#include "../mwrender/vismask.hpp" #include "../mwgui/tooltips.hpp" @@ -34,7 +34,7 @@ namespace MWClass if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model, true); - ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Static); + ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static); } } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 7d1c1d38a..56712a815 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -26,6 +25,7 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwrender/animation.hpp" +#include "../mwrender/vismask.hpp" #include "../mwmechanics/actorutil.hpp" @@ -58,7 +58,7 @@ namespace MWClass if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model, true); - ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Static); + ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static); } } diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 2cb00c497..5551b3d73 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "../mwworld/ptr.hpp" #include "../mwphysics/physicssystem.hpp" @@ -10,6 +9,7 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" +#include "../mwrender/vismask.hpp" namespace MWClass { @@ -19,7 +19,7 @@ namespace MWClass if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); - ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Static); + ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static); } } diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index ddaa9063a..54382ab4d 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -14,13 +14,14 @@ #include #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/inputmanager.hpp" +#include "../mwrender/vismask.hpp" + #include "backgroundimage.hpp" namespace MWGui @@ -334,8 +335,8 @@ namespace MWGui // Turn off rendering except the GUI int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); int oldCullMask = mViewer->getCamera()->getCullMask(); - mViewer->getUpdateVisitor()->setTraversalMask(SceneUtil::Mask_GUI|SceneUtil::Mask_PreCompile); - mViewer->getCamera()->setCullMask(SceneUtil::Mask_GUI|SceneUtil::Mask_PreCompile); + mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI|MWRender::Mask_PreCompile); + mViewer->getCamera()->setCullMask(MWRender::Mask_GUI|MWRender::Mask_PreCompile); MWBase::Environment::get().getInputManager()->update(0, true, true); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 4f36032e1..d10270d77 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include @@ -51,6 +50,8 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" +#include "../mwrender/vismask.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwworld/cellstore.hpp" @@ -1878,8 +1879,8 @@ namespace MWGui // Turn off all rendering except for the GUI int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); int oldCullMask = mViewer->getCamera()->getCullMask(); - mViewer->getUpdateVisitor()->setTraversalMask(SceneUtil::Mask_GUI); - mViewer->getCamera()->setCullMask(SceneUtil::Mask_GUI); + mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI); + mViewer->getCamera()->setCullMask(MWRender::Mask_GUI); MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize(); sizeVideo(screenSize.width, screenSize.height); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e81c9a119..726b2a31f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -25,6 +24,8 @@ #include "../mwmechanics/aibreathe.hpp" +#include "../mwrender/vismask.hpp" + #include "spellcasting.hpp" #include "steering.hpp" #include "npcstats.hpp" @@ -1422,11 +1423,11 @@ namespace MWMechanics const float dist = (player.getRefData().getPosition().asVec3() - ptr.getRefData().getPosition().asVec3()).length(); if (dist > mActorsProcessingRange) { - ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Disabled); + ptr.getRefData().getBaseNode()->setNodeMask(0); return; } else - ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor); + ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor); // Fade away actors on large distance (>90% of actor's processing distance) float visibilityRatio = 1.0; @@ -1750,12 +1751,12 @@ namespace MWMechanics if (!inRange) { - iter->first.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Disabled); + iter->first.getRefData().getBaseNode()->setNodeMask(0); world->setActorCollisionMode(iter->first, false, false); continue; } else if (!isPlayer) - iter->first.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor); + iter->first.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor); const bool isDead = iter->first.getClass().getCreatureStats(iter->first).isDead(); if (!isDead && iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) diff --git a/apps/openmw/mwrender/actoranimation.cpp b/apps/openmw/mwrender/actoranimation.cpp index 1c54f0684..fcffe220b 100644 --- a/apps/openmw/mwrender/actoranimation.cpp +++ b/apps/openmw/mwrender/actoranimation.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include @@ -32,6 +31,8 @@ #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/weapontype.hpp" +#include "vismask.hpp" + namespace MWRender { @@ -366,7 +367,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons) // Otherwise add the enchanted glow to it. if (!showHolsteredWeapons) { - weaponNode->setNodeMask(SceneUtil::Mask_Disabled); + weaponNode->setNodeMask(0); } else { @@ -540,7 +541,7 @@ void ActorAnimation::addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior(); osg::Vec4f ambient(1,1,1,1); - osg::ref_ptr lightSource = SceneUtil::createLightSource(esmLight, exterior, ambient); + osg::ref_ptr lightSource = SceneUtil::createLightSource(esmLight, Mask_Lighting, exterior, ambient); mInsert->addChild(lightSource); diff --git a/apps/openmw/mwrender/actorspaths.cpp b/apps/openmw/mwrender/actorspaths.cpp index ec90949bc..35b255355 100644 --- a/apps/openmw/mwrender/actorspaths.cpp +++ b/apps/openmw/mwrender/actorspaths.cpp @@ -1,7 +1,7 @@ #include "actorspaths.hpp" +#include "vismask.hpp" #include -#include #include @@ -43,7 +43,7 @@ namespace MWRender const auto newGroup = SceneUtil::createAgentPathGroup(path, halfExtents, start, end, settings); if (newGroup) { - newGroup->setNodeMask(SceneUtil::Mask_Debug); + newGroup->setNodeMask(Mask_Debug); mRootNode->addChild(newGroup); mGroups[actor] = newGroup; } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0f7548f05..3505ea261 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -45,6 +44,7 @@ #include "../mwmechanics/character.hpp" // FIXME: for MWMechanics::Priority +#include "vismask.hpp" #include "util.hpp" #include "rotatecontroller.hpp" @@ -578,7 +578,7 @@ namespace MWRender else { // Hide effect immediately - node->setNodeMask(SceneUtil::Mask_Disabled); + node->setNodeMask(0); mFinished = true; } } @@ -1595,7 +1595,7 @@ namespace MWRender { bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior(); - SceneUtil::addLight(parent, esmLight, exterior); + SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior); } void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture) @@ -1647,7 +1647,7 @@ namespace MWRender // FreezeOnCull doesn't work so well with effect particles, that tend to have moving emitters SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor; node->accept(disableFreezeOnCullVisitor); - node->setNodeMask(SceneUtil::Mask_Effect); + node->setNodeMask(Mask_Effect); params.mMaxControllerLength = findMaxLengthVisitor.getMaxLength(); params.mLoop = loop; @@ -1806,7 +1806,7 @@ namespace MWRender SceneUtil::configureLight(light, radius, isExterior); mGlowLight = new SceneUtil::LightSource; - mGlowLight->setNodeMask(SceneUtil::Mask_Lighting); + mGlowLight->setNodeMask(Mask_Lighting); mInsert->addChild(mGlowLight); mGlowLight->setLight(light); } diff --git a/apps/openmw/mwrender/bulletdebugdraw.cpp b/apps/openmw/mwrender/bulletdebugdraw.cpp index 9883d9fe3..4cf76e473 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -7,7 +7,7 @@ #include -#include +#include "vismask.hpp" namespace { @@ -34,7 +34,7 @@ void DebugDrawer::createGeometry() if (!mGeometry) { mGeometry = new osg::Geometry; - mGeometry->setNodeMask(SceneUtil::Mask_Debug); + mGeometry->setNodeMask(Mask_Debug); mVertices = new osg::Vec3Array; diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index c74c70b55..b2552e598 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -28,6 +27,7 @@ #include "../mwmechanics/weapontype.hpp" #include "npcanimation.hpp" +#include "vismask.hpp" namespace MWRender { @@ -61,7 +61,7 @@ namespace MWRender } else { - node->setNodeMask(SceneUtil::Mask_Disabled); + node->setNodeMask(0); } } @@ -138,9 +138,9 @@ namespace MWRender mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture); mCamera->setName("CharacterPreview"); mCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); - mCamera->setCullMask(~(SceneUtil::Mask_UpdateVisitor)); + mCamera->setCullMask(~(Mask_UpdateVisitor)); - mCamera->setNodeMask(SceneUtil::Mask_RenderToTexture); + mCamera->setNodeMask(Mask_RenderToTexture); osg::ref_ptr lightManager = new SceneUtil::LightManager; lightManager->setStartLight(1); @@ -255,7 +255,7 @@ namespace MWRender void CharacterPreview::redraw() { - mCamera->setNodeMask(SceneUtil::Mask_RenderToTexture); + mCamera->setNodeMask(Mask_RenderToTexture); mDrawOnceCallback->redrawNextFrame(); } @@ -364,7 +364,7 @@ namespace MWRender visitor.setTraversalNumber(mDrawOnceCallback->getLastRenderedFrame()); osg::Node::NodeMask nodeMask = mCamera->getNodeMask(); - mCamera->setNodeMask(SceneUtil::Mask_Default); + mCamera->setNodeMask(~0); mCamera->accept(visitor); mCamera->setNodeMask(nodeMask); diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index 450cb20f5..3e785a769 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -6,9 +6,9 @@ #include #include -#include #include "animation.hpp" +#include "vismask.hpp" #include "util.hpp" namespace MWRender @@ -29,7 +29,7 @@ void EffectManager::addEffect(const std::string &model, const std::string& textu { osg::ref_ptr node = mResourceSystem->getSceneManager()->getInstance(model); - node->setNodeMask(SceneUtil::Mask_Effect); + node->setNodeMask(Mask_Effect); Effect effect; effect.mAnimTime.reset(new EffectAnimationTime); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index d51791da6..afa83a1d7 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -16,7 +16,6 @@ #include #include -#include #include @@ -25,6 +24,8 @@ #include "../mwworld/esmstore.hpp" +#include "vismask.hpp" + namespace { @@ -75,7 +76,7 @@ namespace { if (mParent->copyResult(static_cast(node), nv->getTraversalNumber())) { - node->setNodeMask(SceneUtil::Mask_Disabled); + node->setNodeMask(0); mParent->markForRemoval(static_cast(node)); } return; @@ -287,7 +288,7 @@ namespace MWRender float srcLeft, float srcTop, float srcRight, float srcBottom) { osg::ref_ptr camera (new osg::Camera); - camera->setNodeMask(SceneUtil::Mask_RenderToTexture); + camera->setNodeMask(Mask_RenderToTexture); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->setViewMatrix(osg::Matrix::identity()); camera->setProjectionMatrix(osg::Matrix::identity()); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 7bd202e7e..f4a54eb98 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include "../mwbase/environment.hpp" @@ -26,6 +25,8 @@ #include "../mwworld/cellstore.hpp" +#include "vismask.hpp" + namespace { @@ -41,7 +42,7 @@ namespace virtual void operator()(osg::Node* node, osg::NodeVisitor*) { if (mRendered) - node->setNodeMask(SceneUtil::Mask_Disabled); + node->setNodeMask(0); if (!mRendered) { @@ -177,8 +178,8 @@ osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, f camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->setRenderOrder(osg::Camera::PRE_RENDER); - camera->setCullMask(SceneUtil::Mask_Scene | SceneUtil::Mask_SimpleWater | SceneUtil::Mask_Terrain | SceneUtil::Mask_Object | SceneUtil::Mask_Static); - camera->setNodeMask(SceneUtil::Mask_RenderToTexture); + camera->setCullMask(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object | Mask_Static); + camera->setNodeMask(Mask_RenderToTexture); osg::ref_ptr stateset = new osg::StateSet; stateset->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL), osg::StateAttribute::OVERRIDE); @@ -375,7 +376,7 @@ void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell) void LocalMap::requestInteriorMap(const MWWorld::CellStore* cell) { osg::ComputeBoundsVisitor computeBoundsVisitor; - computeBoundsVisitor.setTraversalMask(SceneUtil::Mask_Scene | SceneUtil::Mask_Terrain | SceneUtil::Mask_Object | SceneUtil::Mask_Static); + computeBoundsVisitor.setTraversalMask(Mask_Scene | Mask_Terrain | Mask_Object | Mask_Static); mSceneRoot->accept(computeBoundsVisitor); osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox(); diff --git a/apps/openmw/mwrender/navmesh.cpp b/apps/openmw/mwrender/navmesh.cpp index 7aade0c23..791c41a1a 100644 --- a/apps/openmw/mwrender/navmesh.cpp +++ b/apps/openmw/mwrender/navmesh.cpp @@ -1,7 +1,7 @@ #include "navmesh.hpp" +#include "vismask.hpp" #include -#include #include @@ -45,7 +45,7 @@ namespace MWRender mGroup = SceneUtil::createNavMeshGroup(navMesh, settings); if (mGroup) { - mGroup->setNodeMask(SceneUtil::Mask_Debug); + mGroup->setNodeMask(Mask_Debug); mRootNode->addChild(mGroup); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 261723db5..a797a9876 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -44,6 +43,7 @@ #include "camera.hpp" #include "rotatecontroller.hpp" #include "renderbin.hpp" +#include "vismask.hpp" namespace { @@ -538,7 +538,7 @@ void NpcAnimation::updateNpcBase() addAnimSource(smodel, smodel); - mObjectRoot->setNodeMask(SceneUtil::Mask_FirstPerson); + mObjectRoot->setNodeMask(Mask_FirstPerson); mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView)); } diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 83fd807dc..ec1c4397b 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -5,7 +5,6 @@ #include #include -#include #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" @@ -13,6 +12,8 @@ #include "animation.hpp" #include "npcanimation.hpp" #include "creatureanimation.hpp" +#include "vismask.hpp" + namespace MWRender { @@ -70,7 +71,7 @@ void Objects::insertBegin(const MWWorld::Ptr& ptr) void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool animated, bool allowLight) { insertBegin(ptr); - ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Object); + ptr.getRefData().getBaseNode()->setNodeMask(Mask_Object); osg::ref_ptr anim (new ObjectAnimation(ptr, mesh, mResourceSystem, animated, allowLight)); @@ -80,7 +81,7 @@ void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool void Objects::insertCreature(const MWWorld::Ptr &ptr, const std::string &mesh, bool weaponsShields) { insertBegin(ptr); - ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor); + ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor); // CreatureAnimation osg::ref_ptr anim; @@ -97,7 +98,7 @@ void Objects::insertCreature(const MWWorld::Ptr &ptr, const std::string &mesh, b void Objects::insertNPC(const MWWorld::Ptr &ptr) { insertBegin(ptr); - ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor); + ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor); osg::ref_ptr anim (new NpcAnimation(ptr, osg::ref_ptr(ptr.getRefData().getBaseNode()), mResourceSystem)); diff --git a/apps/openmw/mwrender/pathgrid.cpp b/apps/openmw/mwrender/pathgrid.cpp index a2c5a1f46..797794457 100644 --- a/apps/openmw/mwrender/pathgrid.cpp +++ b/apps/openmw/mwrender/pathgrid.cpp @@ -8,7 +8,6 @@ #include #include -#include #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" @@ -18,6 +17,8 @@ #include "../mwmechanics/pathfinding.hpp" #include "../mwmechanics/coordinateconverter.hpp" +#include "vismask.hpp" + namespace MWRender { @@ -72,7 +73,7 @@ void Pathgrid::togglePathgrid() { // add path grid meshes to already loaded cells mPathGridRoot = new osg::Group; - mPathGridRoot->setNodeMask(SceneUtil::Mask_Pathgrid); + mPathGridRoot->setNodeMask(Mask_Debug); mRootNode->addChild(mPathGridRoot); for(const MWWorld::CellStore* cell : mActiveCells) diff --git a/apps/openmw/mwrender/recastmesh.cpp b/apps/openmw/mwrender/recastmesh.cpp index 5aec174df..d07e7d37b 100644 --- a/apps/openmw/mwrender/recastmesh.cpp +++ b/apps/openmw/mwrender/recastmesh.cpp @@ -1,11 +1,12 @@ #include "recastmesh.hpp" -#include #include #include #include +#include "vismask.hpp" + namespace MWRender { RecastMesh::RecastMesh(const osg::ref_ptr& root, bool enabled) @@ -49,7 +50,7 @@ namespace MWRender || it->second.mRevision != tile->second->getRevision()) { const auto group = SceneUtil::createRecastMeshGroup(*tile->second, settings); - group->setNodeMask(SceneUtil::Mask_Debug); + group->setNodeMask(Mask_Debug); mRootNode->removeChild(it->second.mValue); mRootNode->addChild(group); it->second.mValue = group; @@ -66,7 +67,7 @@ namespace MWRender if (mGroups.count(tile.first)) continue; const auto group = SceneUtil::createRecastMeshGroup(*tile.second, settings); - group->setNodeMask(SceneUtil::Mask_Debug); + group->setNodeMask(Mask_Debug); mGroups.emplace(tile.first, Group {tile.second->getGeneration(), tile.second->getRevision(), group}); mRootNode->addChild(group); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 23a47438a..648660669 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -40,7 +40,6 @@ #include #include #include -#include #include #include @@ -59,6 +58,7 @@ #include "sky.hpp" #include "effectmanager.hpp" #include "npcanimation.hpp" +#include "vismask.hpp" #include "pathgrid.hpp" #include "camera.hpp" #include "water.hpp" @@ -216,7 +216,7 @@ namespace MWRender , mFieldOfViewOverride(0.f) , mBorders(false) { - resourceSystem->getSceneManager()->setParticleSystemMask(SceneUtil::Mask_ParticleSystem); + resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); // Shadows and radial fog have problems with fixed-function mode bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders") || Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows"); @@ -230,21 +230,21 @@ namespace MWRender resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders")); osg::ref_ptr sceneRoot = new SceneUtil::LightManager; - sceneRoot->setLightingMask(SceneUtil::Mask_Lighting); + sceneRoot->setLightingMask(Mask_Lighting); mSceneRoot = sceneRoot; sceneRoot->setStartLight(1); - int shadowCastingTraversalMask = SceneUtil::Mask_Scene; + int shadowCastingTraversalMask = Mask_Scene; if (Settings::Manager::getBool("actor shadows", "Shadows")) - shadowCastingTraversalMask |= SceneUtil::Mask_Actor; + shadowCastingTraversalMask |= Mask_Actor; if (Settings::Manager::getBool("player shadows", "Shadows")) - shadowCastingTraversalMask |= SceneUtil::Mask_Player; + shadowCastingTraversalMask |= Mask_Player; if (Settings::Manager::getBool("terrain shadows", "Shadows")) - shadowCastingTraversalMask |= SceneUtil::Mask_Terrain; + shadowCastingTraversalMask |= Mask_Terrain; int indoorShadowCastingTraversalMask = shadowCastingTraversalMask; if (Settings::Manager::getBool("object shadows", "Shadows")) - shadowCastingTraversalMask |= (SceneUtil::Mask_Object|SceneUtil::Mask_Static); + shadowCastingTraversalMask |= (Mask_Object|Mask_Static); mShadowManager.reset(new SceneUtil::ShadowManager(sceneRoot, mRootNode, shadowCastingTraversalMask, indoorShadowCastingTraversalMask, mResourceSystem->getSceneManager()->getShaderManager())); @@ -311,10 +311,11 @@ namespace MWRender float maxCompGeometrySize = Settings::Manager::getFloat("max composite geometry size", "Terrain"); maxCompGeometrySize = std::max(maxCompGeometrySize, 1.f); mTerrain.reset(new Terrain::QuadTreeWorld( - sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize)); + sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug, + compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize)); } else - mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage)); + mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug)); mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); mTerrain->setWorkQueue(mWorkQueue.get()); @@ -324,7 +325,7 @@ namespace MWRender mViewer->setLightingMode(osgViewer::View::NO_LIGHT); osg::ref_ptr source = new osg::LightSource; - source->setNodeMask(SceneUtil::Mask_Lighting); + source->setNodeMask(Mask_Lighting); mSunLight = new osg::Light; source->setLight(mSunLight); mSunLight->setDiffuse(osg::Vec4f(0,0,0,1)); @@ -343,7 +344,7 @@ namespace MWRender defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat); - sceneRoot->setNodeMask(SceneUtil::Mask_Scene); + sceneRoot->setNodeMask(Mask_Scene); sceneRoot->setName("Scene Root"); mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); @@ -371,7 +372,7 @@ namespace MWRender mViewer->getCamera()->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); mViewer->getCamera()->setCullingMode(cullingMode); - mViewer->getCamera()->setCullMask(~(SceneUtil::Mask_UpdateVisitor|SceneUtil::Mask_SimpleWater)); + mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater)); mNearClip = Settings::Manager::getFloat("near clip", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); @@ -573,12 +574,12 @@ namespace MWRender else if (mode == Render_Scene) { int mask = mViewer->getCamera()->getCullMask(); - bool enabled = mask & SceneUtil::Mask_Scene; + bool enabled = mask&Mask_Scene; enabled = !enabled; if (enabled) - mask |= SceneUtil::Mask_Scene; + mask |= Mask_Scene; else - mask &= ~SceneUtil::Mask_Scene; + mask &= ~Mask_Scene; mViewer->getCamera()->setCullMask(mask); return enabled; } @@ -851,7 +852,7 @@ namespace MWRender int maskBackup = mPlayerAnimation->getObjectRoot()->getNodeMask(); if (mCamera->isFirstPerson()) - mPlayerAnimation->getObjectRoot()->setNodeMask(SceneUtil::Mask_Disabled); + mPlayerAnimation->getObjectRoot()->setNodeMask(0); for (int i = 0; i < 6; ++i) // for each cubemap side { @@ -925,7 +926,7 @@ namespace MWRender void RenderingManager::renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h) { - camera->setNodeMask(SceneUtil::Mask_RenderToTexture); + camera->setNodeMask(Mask_RenderToTexture); camera->attach(osg::Camera::COLOR_BUFFER, image); camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); @@ -978,7 +979,7 @@ namespace MWRender rttCamera->addChild(mWater->getReflectionCamera()); rttCamera->addChild(mWater->getRefractionCamera()); - rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~SceneUtil::Mask_GUI)); + rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI)); rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -991,7 +992,7 @@ namespace MWRender return osg::Vec4f(); osg::ComputeBoundsVisitor computeBoundsVisitor; - computeBoundsVisitor.setTraversalMask(~(SceneUtil::Mask_ParticleSystem|SceneUtil::Mask_Effect)); + computeBoundsVisitor.setTraversalMask(~(Mask_ParticleSystem|Mask_Effect)); ptr.getRefData().getBaseNode()->accept(computeBoundsVisitor); osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix(); @@ -1063,11 +1064,12 @@ namespace MWRender mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber()); mIntersectionVisitor->setIntersector(intersector); - int mask = ~(SceneUtil::Mask_RenderToTexture|SceneUtil::Mask_Sky|SceneUtil::Mask_Pathgrid|SceneUtil::Mask_Debug|SceneUtil::Mask_Effect|SceneUtil::Mask_Water|SceneUtil::Mask_SimpleWater); + int mask = ~0; + mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water|Mask_SimpleWater); if (ignorePlayer) - mask &= ~(SceneUtil::Mask_Player); + mask &= ~(Mask_Player); if (ignoreActors) - mask &= ~(SceneUtil::Mask_Actor|SceneUtil::Mask_Player); + mask &= ~(Mask_Actor|Mask_Player); mIntersectionVisitor->setTraversalMask(mask); return mIntersectionVisitor; @@ -1148,7 +1150,7 @@ namespace MWRender if (!mPlayerNode) { mPlayerNode = new SceneUtil::PositionAttitudeTransform; - mPlayerNode->setNodeMask(SceneUtil::Mask_Player); + mPlayerNode->setNodeMask(Mask_Player); mPlayerNode->setName("Player Root"); mSceneRoot->addChild(mPlayerNode); } @@ -1392,7 +1394,7 @@ namespace MWRender osg::ref_ptr node = mResourceSystem->getSceneManager()->getTemplate(modelName); osg::ComputeBoundsVisitor computeBoundsVisitor; - computeBoundsVisitor.setTraversalMask(~(SceneUtil::Mask_ParticleSystem|SceneUtil::Mask_Effect)); + computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect)); const_cast(node.get())->accept(computeBoundsVisitor); osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox(); diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index 6597dde24..f7feb267a 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -16,7 +16,8 @@ #include #include #include -#include + +#include "vismask.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -103,7 +104,7 @@ RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem* mParticleNode->setName("Ripple Root"); mParticleNode->addChild(updater); mParticleNode->addChild(mParticleSystem); - mParticleNode->setNodeMask(SceneUtil::Mask_Water); + mParticleNode->setNodeMask(Mask_Water); createWaterRippleStateSet(resourceSystem, mParticleNode); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 592cd75bf..10fc630bd 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -43,12 +43,12 @@ #include #include #include -#include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "vismask.hpp" #include "renderbin.hpp" namespace @@ -454,7 +454,7 @@ public: void setVisible(bool visible) { - mTransform->setNodeMask(visible ? mVisibleMask : SceneUtil::Mask_Disabled); + mTransform->setNodeMask(visible ? mVisibleMask : 0); } protected: @@ -470,7 +470,7 @@ class Sun : public CelestialBody { public: Sun(osg::Group* parentNode, Resource::ImageManager& imageManager) - : CelestialBody(parentNode, 1.0f, 1, SceneUtil::Mask_Sun) + : CelestialBody(parentNode, 1.0f, 1, Mask_Sun) , mUpdater(new Updater) { mTransform->addUpdateCallback(mUpdater); @@ -655,7 +655,7 @@ private: camera->setProjectionMatrix(osg::Matrix::identity()); camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); // add to skyRoot instead? camera->setViewMatrix(osg::Matrix::identity()); - camera->setClearMask(SceneUtil::Mask_Disabled); + camera->setClearMask(0); camera->setRenderOrder(osg::Camera::NESTED_RENDER); camera->setAllowEventFocus(false); @@ -1145,7 +1145,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana skyroot->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::PROTECTED|osg::StateAttribute::ON); SceneUtil::ShadowManager::disableShadowsForStateSet(skyroot->getOrCreateStateSet()); - skyroot->setNodeMask(SceneUtil::Mask_Sky); + skyroot->setNodeMask(Mask_Sky); parentNode->addChild(skyroot); mRootNode = skyroot; @@ -1177,7 +1177,7 @@ void SkyManager::create() mAtmosphereDay->addUpdateCallback(mAtmosphereUpdater); mAtmosphereNightNode = new osg::PositionAttitudeTransform; - mAtmosphereNightNode->setNodeMask(SceneUtil::Mask_Disabled); + mAtmosphereNightNode->setNodeMask(0); mEarlyRenderBinRoot->addChild(mAtmosphereNightNode); osg::ref_ptr atmosphereNight; @@ -1210,7 +1210,7 @@ void SkyManager::create() mCloudUpdater2 = new CloudUpdater; mCloudUpdater2->setOpacity(0.f); mCloudMesh2->addUpdateCallback(mCloudUpdater2); - mCloudMesh2->setNodeMask(SceneUtil::Mask_Disabled); + mCloudMesh2->setNodeMask(0); osg::ref_ptr depth = new osg::Depth; depth->setWriteMask(false); @@ -1533,7 +1533,7 @@ void SkyManager::createRain() mRainFader = new RainFader(&mWeatherAlpha); mRainNode->addUpdateCallback(mRainFader); mRainNode->addCullCallback(mUnderwaterSwitch); - mRainNode->setNodeMask(SceneUtil::Mask_WeatherParticles); + mRainNode->setNodeMask(Mask_WeatherParticles); mRootNode->addChild(mRainNode); } @@ -1636,7 +1636,7 @@ void SkyManager::setEnabled(bool enabled) if (enabled && !mCreated) create(); - mRootNode->setNodeMask(enabled ? SceneUtil::Mask_Sky : SceneUtil::Mask_Disabled); + mRootNode->setNodeMask(enabled ? Mask_Sky : 0); mEnabled = enabled; } @@ -1729,7 +1729,7 @@ void SkyManager::setWeather(const WeatherResult& weather) { mParticleNode = new osg::PositionAttitudeTransform; mParticleNode->addCullCallback(mUnderwaterSwitch); - mParticleNode->setNodeMask(SceneUtil::Mask_WeatherParticles); + mParticleNode->setNodeMask(Mask_WeatherParticles); mRootNode->addChild(mParticleNode); } @@ -1799,7 +1799,7 @@ void SkyManager::setWeather(const WeatherResult& weather) mCloudUpdater->setOpacity((1.f-mCloudBlendFactor)); mCloudUpdater2->setOpacity(mCloudBlendFactor); - mCloudMesh2->setNodeMask(mCloudBlendFactor > 0.f ? SceneUtil::Mask_Default : SceneUtil::Mask_Disabled); + mCloudMesh2->setNodeMask(mCloudBlendFactor > 0.f ? ~0 : 0); } if (mCloudColour != weather.mFogColor) @@ -1844,7 +1844,7 @@ void SkyManager::setWeather(const WeatherResult& weather) mAtmosphereNightUpdater->setFade(mStarsOpacity); } - mAtmosphereNightNode->setNodeMask(weather.mNight ? SceneUtil::Mask_Default : SceneUtil::Mask_Disabled); + mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0 : 0); if (mRainFader) mRainFader->setAlpha(weather.mEffectFade * 0.6); // * Rain_Threshold? diff --git a/components/sceneutil/vismask.hpp b/apps/openmw/mwrender/vismask.hpp similarity index 82% rename from components/sceneutil/vismask.hpp rename to apps/openmw/mwrender/vismask.hpp index e9c35922c..f9f9dc74c 100644 --- a/components/sceneutil/vismask.hpp +++ b/apps/openmw/mwrender/vismask.hpp @@ -1,7 +1,7 @@ -#ifndef OPENMW_COMPONENTS_SCENEUTIL_VISMASK_H -#define OPENMW_COMPONENTS_SCENEUTIL_VISMASK_H +#ifndef OPENMW_MWRENDER_VISMASK_H +#define OPENMW_MWRENDER_VISMASK_H -namespace SceneUtil +namespace MWRender { /// Node masks used for controlling visibility of game objects. @@ -21,8 +21,6 @@ namespace SceneUtil /// compatibility if the enumeration values were to be changed. Feel free to change them when it makes sense. enum VisMask { - Mask_Disabled = 0, // For hidden nodes - Mask_UpdateVisitor = 0x1, // reserved for separating UpdateVisitors from CullVisitors // child of Scene @@ -55,19 +53,7 @@ namespace SceneUtil Mask_PreCompile = (1<<18), // Set on a camera's cull mask to enable the LightManager - Mask_Lighting = (1<<19), - - // For pathgrid nodes debugging - Mask_Pathgrid = (1<<20), - - // Editor control elements - Mask_EditorCellMarker = (1<<21), - Mask_EditorCellArrow = (1<<22), - Mask_EditorCellBorder = (1<<23), - Mask_EditorReference = (1<<24), - - // Default mask for OSG nodes - Mask_Default = 0xffffffff + Mask_Lighting = (1<<19) }; } diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 2b307055d..6d230d36e 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -27,7 +27,6 @@ #include #include -#include #include @@ -41,6 +40,7 @@ #include "../mwworld/cellstore.hpp" +#include "vismask.hpp" #include "ripplesimulation.hpp" #include "renderbin.hpp" #include "util.hpp" @@ -243,8 +243,8 @@ public: setName("RefractionCamera"); setCullCallback(new InheritViewPointCallback); - setCullMask(SceneUtil::Mask_Effect|SceneUtil::Mask_Scene|SceneUtil::Mask_Object|SceneUtil::Mask_Static|SceneUtil::Mask_Terrain|SceneUtil::Mask_Actor|SceneUtil::Mask_ParticleSystem|SceneUtil::Mask_Sky|SceneUtil::Mask_Sun|SceneUtil::Mask_Player|SceneUtil::Mask_Lighting); - setNodeMask(SceneUtil::Mask_RenderToTexture); + setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Static|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting); + setNodeMask(Mask_RenderToTexture); setViewport(0, 0, rttSize, rttSize); // No need for Update traversal since the scene is already updated as part of the main scene graph @@ -337,7 +337,7 @@ public: setCullCallback(new InheritViewPointCallback); setInterior(isInterior); - setNodeMask(SceneUtil::Mask_RenderToTexture); + setNodeMask(Mask_RenderToTexture); unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water"); setViewport(0, 0, rttSize, rttSize); @@ -372,11 +372,11 @@ public: int reflectionDetail = Settings::Manager::getInt("reflection detail", "Water"); reflectionDetail = std::min(4, std::max(isInterior ? 2 : 0, reflectionDetail)); unsigned int extraMask = 0; - if(reflectionDetail >= 1) extraMask |= SceneUtil::Mask_Terrain; - if(reflectionDetail >= 2) extraMask |= SceneUtil::Mask_Static; - if(reflectionDetail >= 3) extraMask |= SceneUtil::Mask_Effect|SceneUtil::Mask_ParticleSystem|SceneUtil::Mask_Object; - if(reflectionDetail >= 4) extraMask |= SceneUtil::Mask_Player|SceneUtil::Mask_Actor; - setCullMask(SceneUtil::Mask_Scene|SceneUtil::Mask_Sky|SceneUtil::Mask_Lighting|extraMask); + if(reflectionDetail >= 1) extraMask |= Mask_Terrain; + if(reflectionDetail >= 2) extraMask |= Mask_Static; + if(reflectionDetail >= 3) extraMask |= Mask_Effect|Mask_ParticleSystem|Mask_Object; + if(reflectionDetail >= 4) extraMask |= Mask_Player|Mask_Actor; + setCullMask(Mask_Scene|Mask_Sky|Mask_Lighting|extraMask); } void setWaterLevel(float waterLevel) @@ -441,7 +441,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900); mWaterGeom->setDrawCallback(new DepthClampCallback); - mWaterGeom->setNodeMask(SceneUtil::Mask_Water); + mWaterGeom->setNodeMask(Mask_Water); mWaterNode = new osg::PositionAttitudeTransform; mWaterNode->setName("Water Root"); @@ -451,7 +451,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem // simple water fallback for the local map osg::ref_ptr geom2 (osg::clone(mWaterGeom.get(), osg::CopyOp::DEEP_COPY_NODES)); createSimpleWaterStateSet(geom2, Fallback::Map::getFloat("Water_Map_Alpha")); - geom2->setNodeMask(SceneUtil::Mask_SimpleWater); + geom2->setNodeMask(Mask_SimpleWater); mWaterNode->addChild(geom2); mSceneRoot->addChild(mWaterNode); @@ -706,11 +706,11 @@ void Water::update(float dt) void Water::updateVisible() { bool visible = mEnabled && mToggled; - mWaterNode->setNodeMask(visible ? SceneUtil::Mask_Default : SceneUtil::Mask_Disabled); + mWaterNode->setNodeMask(visible ? ~0 : 0); if (mRefraction) - mRefraction->setNodeMask(visible ? SceneUtil::Mask_RenderToTexture : SceneUtil::Mask_Disabled); + mRefraction->setNodeMask(visible ? Mask_RenderToTexture : 0); if (mReflection) - mReflection->setNodeMask(visible ? SceneUtil::Mask_RenderToTexture : SceneUtil::Mask_Disabled); + mReflection->setNodeMask(visible ? Mask_RenderToTexture : 0); } bool Water::toggle() diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index c34c9c77f..38458fdb9 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include "../mwworld/manualref.hpp" @@ -36,6 +35,7 @@ #include "../mwmechanics/weapontype.hpp" #include "../mwrender/animation.hpp" +#include "../mwrender/vismask.hpp" #include "../mwrender/renderingmanager.hpp" #include "../mwrender/util.hpp" @@ -188,7 +188,7 @@ namespace MWWorld bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture) { state.mNode = new osg::PositionAttitudeTransform; - state.mNode->setNodeMask(SceneUtil::Mask_Effect); + state.mNode->setNodeMask(MWRender::Mask_Effect); state.mNode->setPosition(pos); state.mNode->setAttitude(orient); @@ -228,7 +228,7 @@ namespace MWWorld projectileLight->setPosition(osg::Vec4(pos, 1.0)); SceneUtil::LightSource* projectileLightSource = new SceneUtil::LightSource; - projectileLightSource->setNodeMask(SceneUtil::Mask_Lighting); + projectileLightSource->setNodeMask(MWRender::Mask_Lighting); projectileLightSource->setRadius(66.f); state.mNode->addChild(projectileLightSource); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8598b9b04..49b55f2a9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include @@ -47,6 +46,7 @@ #include "../mwrender/npcanimation.hpp" #include "../mwrender/renderingmanager.hpp" #include "../mwrender/camera.hpp" +#include "../mwrender/vismask.hpp" #include "../mwscript/globalscripts.hpp" @@ -2273,7 +2273,7 @@ namespace MWWorld { // Adjust position so the location we wanted ends up in the middle of the object bounding box osg::ComputeBoundsVisitor computeBounds; - computeBounds.setTraversalMask(~SceneUtil::Mask_ParticleSystem); + computeBounds.setTraversalMask(~MWRender::Mask_ParticleSystem); dropped.getRefData().getBaseNode()->accept(computeBounds); osg::BoundingBox bounds = computeBounds.getBoundingBox(); if (bounds.valid()) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 26cf38204..06c777c02 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -51,7 +51,7 @@ add_component_dir (shader add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer - actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique vismask recastmesh + actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh ) add_component_dir (nif diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 934e9b565..1842e0017 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -10,7 +10,6 @@ #include #include -#include #include "userdata.hpp" @@ -305,7 +304,7 @@ void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv) { bool vis = calculate(getInputValue(nv)); // Leave 0x1 enabled for UpdateVisitor, so we can make ourselves visible again in the future from this update callback - node->setNodeMask(vis ? SceneUtil::Mask_Default : SceneUtil::Mask_UpdateVisitor); + node->setNodeMask(vis ? ~0 : 0x1); } traverse(node, nv); } diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 74cdcefe3..dc624315c 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -16,7 +16,6 @@ #include #include #include -#include // particle #include @@ -178,7 +177,7 @@ namespace NifOsg void setEnabled(bool enabled) { - setNodeMask(enabled ? SceneUtil::Mask_Default : SceneUtil::Mask_Effect); + setNodeMask(enabled ? ~0 : 0); } }; @@ -573,7 +572,7 @@ namespace NifOsg { skipMeshes = true; // Leave mask for UpdateVisitor enabled - node->setNodeMask(SceneUtil::Mask_UpdateVisitor); + node->setNodeMask(0x1); } // We can skip creating meshes for hidden nodes if they don't have a VisController that @@ -588,7 +587,7 @@ namespace NifOsg skipMeshes = true; // skip child meshes, but still create the child node hierarchy for animating collision shapes // now hide this node, but leave the mask for UpdateVisitor enabled so that KeyframeController works - node->setNodeMask(SceneUtil::Mask_UpdateVisitor); + node->setNodeMask(0x1); } if ((skipMeshes || hasMarkers) && isAnimated) // make sure the empty node is not optimized away so the physicssystem can find it. diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 1535bdbf6..51497cd27 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -12,7 +12,6 @@ #include #include -#include namespace Resource { @@ -104,14 +103,14 @@ void StatsHandler::toggle(osgViewer::ViewerBase *viewer) if (!_statsType) { - _camera->setNodeMask(SceneUtil::Mask_Disabled); + _camera->setNodeMask(0); _switch->setAllChildrenOff(); viewer->getViewerStats()->collectStats("resource", false); } else { - _camera->setNodeMask(SceneUtil::Mask_Default); + _camera->setNodeMask(0xffffffff); _switch->setSingleChildOn(_resourceStatsChildNum); viewer->getViewerStats()->collectStats("resource", true); diff --git a/components/sceneutil/lightutil.cpp b/components/sceneutil/lightutil.cpp index c90fa8923..e9be05908 100644 --- a/components/sceneutil/lightutil.cpp +++ b/components/sceneutil/lightutil.cpp @@ -11,7 +11,6 @@ #include "lightcontroller.hpp" #include "util.hpp" #include "visitor.hpp" -#include "vismask.hpp" #include "positionattitudetransform.hpp" namespace SceneUtil @@ -59,7 +58,7 @@ namespace SceneUtil light->setQuadraticAttenuation(quadraticAttenuation); } - void addLight (osg::Group* node, const ESM::Light* esmLight, bool isExterior) + void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior) { SceneUtil::FindByNameVisitor visitor("AttachLight"); node->accept(visitor); @@ -72,7 +71,7 @@ namespace SceneUtil else { osg::ComputeBoundsVisitor computeBound; - computeBound.setTraversalMask(~SceneUtil::Mask_ParticleSystem); + computeBound.setTraversalMask(~partsysMask); // We want the bounds of all children of the node, ignoring the node's local transformation // So do a traverse(), not accept() computeBound.traverse(*node); @@ -86,15 +85,15 @@ namespace SceneUtil attachTo = trans; } - osg::ref_ptr lightSource = createLightSource(esmLight, isExterior); + osg::ref_ptr lightSource = createLightSource(esmLight, lightMask, isExterior); attachTo->addChild(lightSource); } - osg::ref_ptr createLightSource(const ESM::Light* esmLight, bool isExterior, const osg::Vec4f& ambient) + osg::ref_ptr createLightSource(const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient) { osg::ref_ptr lightSource (new SceneUtil::LightSource); osg::ref_ptr light (new osg::Light); - lightSource->setNodeMask(SceneUtil::Mask_Lighting); + lightSource->setNodeMask(lightMask); float radius = esmLight->mData.mRadius; lightSource->setRadius(radius); diff --git a/components/sceneutil/lightutil.hpp b/components/sceneutil/lightutil.hpp index f72cf9f19..7096c38b2 100644 --- a/components/sceneutil/lightutil.hpp +++ b/components/sceneutil/lightutil.hpp @@ -32,14 +32,14 @@ namespace SceneUtil /// @param partsysMask Node mask to ignore when computing the sub graph's bounding box. /// @param lightMask Mask to assign to the newly created LightSource. /// @param isExterior Is the light outside? May be used for deciding which attenuation settings to use. - void addLight (osg::Group* node, const ESM::Light* esmLight, bool isExterior); + void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior); /// @brief Convert an ESM::Light to a SceneUtil::LightSource, and return it. /// @param esmLight The light definition coming from the game files containing radius, color, flicker, etc. /// @param lightMask Mask to assign to the newly created LightSource. /// @param isExterior Is the light outside? May be used for deciding which attenuation settings to use. /// @param ambient Ambient component of the light. - osg::ref_ptr createLightSource (const ESM::Light* esmLight, bool isExterior, const osg::Vec4f& ambient=osg::Vec4f(0,0,0,1)); + osg::ref_ptr createLightSource (const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient=osg::Vec4f(0,0,0,1)); } diff --git a/components/sceneutil/optimizer.hpp b/components/sceneutil/optimizer.hpp index 9b3dc47f9..9974e7097 100644 --- a/components/sceneutil/optimizer.hpp +++ b/components/sceneutil/optimizer.hpp @@ -22,8 +22,6 @@ #include #include -#include - //#include #include @@ -44,7 +42,7 @@ class BaseOptimizerVisitor : public osg::NodeVisitor _optimizer(optimizer), _operationType(operation) { - setNodeMaskOverride(SceneUtil::Mask_Default); + setNodeMaskOverride(0xffffffff); } inline bool isOperationPermissibleForObject(const osg::StateSet* object) const; diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index 035c19a5f..6b88adaab 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -2,7 +2,6 @@ #include -#include #include namespace SceneUtil @@ -22,7 +21,7 @@ namespace SceneUtil mShadowTechnique->enableShadows(); mShadowSettings->setLightNum(0); - mShadowSettings->setReceivesShadowTraversalMask(SceneUtil::Mask_Default); + mShadowSettings->setReceivesShadowTraversalMask(~0u); int numberOfShadowMapsPerLight = Settings::Manager::getInt("number of shadow maps", "Shadows"); numberOfShadowMapsPerLight = std::max(1, std::min(numberOfShadowMapsPerLight, 8)); diff --git a/components/terrain/cellborder.cpp b/components/terrain/cellborder.cpp index d6ecd5b5a..6eabadf92 100644 --- a/components/terrain/cellborder.cpp +++ b/components/terrain/cellborder.cpp @@ -4,17 +4,16 @@ #include #include -#include - #include "world.hpp" #include "../esm/loadland.hpp" namespace Terrain { -CellBorder::CellBorder(Terrain::World *world, osg::Group *root): +CellBorder::CellBorder(Terrain::World *world, osg::Group *root, int borderMask): mWorld(world), - mRoot(root) + mRoot(root), + mBorderMask(borderMask) { } @@ -70,7 +69,7 @@ void CellBorder::createCellBorderGeometry(int x, int y) polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); stateSet->setAttributeAndModes(polygonmode,osg::StateAttribute::ON); - borderGeode->setNodeMask(SceneUtil::Mask_Debug); + borderGeode->setNodeMask(mBorderMask); mRoot->addChild(borderGeode); diff --git a/components/terrain/cellborder.hpp b/components/terrain/cellborder.hpp index 49759671f..908cdea09 100644 --- a/components/terrain/cellborder.hpp +++ b/components/terrain/cellborder.hpp @@ -16,7 +16,7 @@ namespace Terrain public: typedef std::map, osg::ref_ptr > CellGrid; - CellBorder(Terrain::World *world, osg::Group *root); + CellBorder(Terrain::World *world, osg::Group *root, int borderMask); void createCellBorderGeometry(int x, int y); void destroyCellBorderGeometry(int x, int y); @@ -31,6 +31,7 @@ namespace Terrain osg::Group *mRoot; CellGrid mCellBorderNodes; + int mBorderMask; }; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 05f33078f..396ac1138 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -6,7 +6,6 @@ #include #include -#include #include "quadtreenode.hpp" #include "storage.hpp" @@ -217,8 +216,8 @@ private: osg::ref_ptr mRootNode; }; -QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int compMapResolution, float compMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize) - : TerrainGrid(parent, compileRoot, resourceSystem, storage) +QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask, int borderMask, int compMapResolution, float compMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize) + : TerrainGrid(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask) , mViewDataMap(new ViewDataMap) , mQuadTreeBuilt(false) , mLodFactor(lodFactor) @@ -425,7 +424,7 @@ void QuadTreeWorld::enable(bool enabled) } if (mRootNode) - mRootNode->setNodeMask(enabled ? SceneUtil::Mask_Default : SceneUtil::Mask_Disabled); + mRootNode->setNodeMask(enabled ? ~0 : 0); } void QuadTreeWorld::cacheCell(View *view, int x, int y) diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 2bfd9f896..bcb671ee1 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -21,7 +21,7 @@ namespace Terrain class QuadTreeWorld : public TerrainGrid // note: derived from TerrainGrid is only to render default cells (see loadCell) { public: - QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int compMapResolution, float comMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize); + QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask, int compMapResolution, float comMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize); ~QuadTreeWorld(); diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index f7a7644fd..7310846c2 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -18,8 +18,8 @@ public: virtual void reset() {} }; -TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage) - : Terrain::World(parent, compileRoot, resourceSystem, storage) +TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask) + : Terrain::World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask) , mNumSplits(4) { } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 3764eb986..eb30fb97d 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -14,7 +14,7 @@ namespace Terrain class TerrainGrid : public Terrain::World { public: - TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage); + TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask=~0, int borderMask=0); ~TerrainGrid(); virtual void cacheCell(View* view, int x, int y); diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 010ae9568..da3bdb5c2 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -5,7 +5,6 @@ #include #include -#include #include "storage.hpp" #include "texturemanager.hpp" @@ -15,14 +14,14 @@ namespace Terrain { -World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage) +World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask) : mStorage(storage) , mParent(parent) , mResourceSystem(resourceSystem) , mBorderVisible(false) { mTerrainRoot = new osg::Group; - mTerrainRoot->setNodeMask(SceneUtil::Mask_Terrain); + mTerrainRoot->setNodeMask(nodeMask); mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); osg::ref_ptr material (new osg::Material); material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); @@ -35,8 +34,8 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst compositeCam->setProjectionMatrix(osg::Matrix::identity()); compositeCam->setViewMatrix(osg::Matrix::identity()); compositeCam->setReferenceFrame(osg::Camera::ABSOLUTE_RF); - compositeCam->setClearMask(SceneUtil::Mask_Disabled); - compositeCam->setNodeMask(SceneUtil::Mask_PreCompile); + compositeCam->setClearMask(0); + compositeCam->setNodeMask(preCompileMask); mCompositeMapCamera = compositeCam; compileRoot->addChild(compositeCam); @@ -48,7 +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)); - mCellBorder.reset(new CellBorder(this,mTerrainRoot.get())); + mCellBorder.reset(new CellBorder(this,mTerrainRoot.get(),borderMask)); mResourceSystem->addResourceManager(mChunkManager.get()); mResourceSystem->addResourceManager(mTextureManager.get()); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 8929e0f6b..0402b8197 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -63,7 +63,7 @@ namespace Terrain /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) /// @param nodeMask mask for the terrain root /// @param preCompileMask mask for pre compiling textures - World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage); + World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask); virtual ~World(); /// Set a WorkQueue to delete objects in the background thread. From dab09180ae2b3971d667f5ae92233592a8c9e19d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 20 Apr 2020 22:09:09 +0300 Subject: [PATCH 086/226] Make sure non-carriable light sources can't be picked up --- apps/openmw/mwgui/inventorywindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 6041838f5..d11ee4f0c 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -732,7 +732,8 @@ namespace MWGui && (type != typeid(ESM::Potion).name())) return; - if (object.getClass().getName(object) == "") // objects without name presented to user can never be picked up + // An object that can be picked up must have a tooltip. + if (!object.getClass().hasToolTip(object)) return; int count = object.getRefData().getCount(); From 886fd5efc94e92290cd1967082882fbb71e12d99 Mon Sep 17 00:00:00 2001 From: Julien Valentin Date: Thu, 16 Apr 2020 02:55:47 +0200 Subject: [PATCH 087/226] enable particle shading and force particle normal to (0.3,0.3,0.3) and update changelog --- CHANGELOG.md | 2 ++ components/nifosg/nifloader.cpp | 2 ++ components/nifosg/particle.cpp | 27 +++++++++++++++++++++++++++ components/nifosg/particle.hpp | 3 +++ 4 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eff7dee0..d975eb4cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ 0.47.0 ------ + Bug #3676: NiParticleColorModifier isn't applied properly + Bug #4949: Incorrect particle lighting when force shaders = true 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/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 07cbaee37..801427b92 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1078,6 +1078,8 @@ namespace NifOsg trans->addChild(toAttach); parentNode->addChild(trans); } + // create partsys stateset in order to pass in ShaderVisitor like all other Drawables + 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) diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 2a79af1c8..1b1e469bc 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -19,12 +20,19 @@ ParticleSystem::ParticleSystem() : osgParticle::ParticleSystem() , mQuota(std::numeric_limits::max()) { + mNormalArray = new osg::Vec3Array(1); + mNormalArray->setBinding(osg::Array::BIND_OVERALL); + (*mNormalArray.get())[0] = osg::Vec3(0.3, 0.3, 0.3); } ParticleSystem::ParticleSystem(const ParticleSystem ©, const osg::CopyOp ©op) : osgParticle::ParticleSystem(copy, copyop) , mQuota(copy.mQuota) { + mNormalArray = new osg::Vec3Array(1); + mNormalArray->setBinding(osg::Array::BIND_OVERALL); + (*mNormalArray.get())[0] = osg::Vec3(0.3, 0.3, 0.3); + // For some reason the osgParticle constructor doesn't copy the particles for (int i=0;iassignNormalArrayDispatcher(); + state.getCurrentVertexArrayState()->setNormalArray(state, mNormalArray); + } + else + { + state.getAttributeDispatchers().activateNormalArray(mNormalArray); + } +#else + state.Normal(0.3, 0.3, 0.3); +#endif + osgParticle::ParticleSystem::drawImplementation(renderInfo); +} + void InverseWorldMatrix::operator()(osg::Node *node, osg::NodeVisitor *nv) { if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) diff --git a/components/nifosg/particle.hpp b/components/nifosg/particle.hpp index e159914c7..d71cbb9f5 100644 --- a/components/nifosg/particle.hpp +++ b/components/nifosg/particle.hpp @@ -36,8 +36,11 @@ namespace NifOsg void setQuota(int quota); + virtual void drawImplementation(osg::RenderInfo& renderInfo) const; + private: int mQuota; + osg::ref_ptr mNormalArray; }; // HACK: Particle doesn't allow setting the initial age, but we need this for loading the particle system state From af335d71271891c1964c3ae6b6a3e263b9b274c5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 20 Apr 2020 23:51:00 +0300 Subject: [PATCH 088/226] Fix changelog particle lighting entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d975eb4cb..1043ca0cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ 0.47.0 ------ + Bug #1952: Incorrect particle lighting Bug #3676: NiParticleColorModifier isn't applied properly - Bug #4949: Incorrect particle lighting when force shaders = true 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 From 673184369158bb901c1572ac538bf51ff9a82216 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 21 Apr 2020 09:32:34 +0300 Subject: [PATCH 089/226] Consider the first person movement sneaking if it's actually sneaking --- apps/openmw/mwmechanics/character.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0c13c2740..b5addea70 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -615,7 +615,9 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup // The first person anims don't have any velocity to calculate a speed multiplier from. // We use the third person velocities instead. // FIXME: should be pulled from the actual animation, but it is not presently loaded. - mMovementAnimSpeed = (isSneaking() ? 33.5452f : (isRunning() ? 222.857f : 154.064f)); + bool sneaking = mMovementState == CharState_SneakForward || mMovementState == CharState_SneakBack + || mMovementState == CharState_SneakLeft || mMovementState == CharState_SneakRight; + mMovementAnimSpeed = (sneaking ? 33.5452f : (isRunning() ? 222.857f : 154.064f)); mMovementAnimationControlled = false; } } From 53b9b411591b2bc22e78ba2cc15fa422449d89a1 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 21 Apr 2020 18:18:55 +0100 Subject: [PATCH 090/226] 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 ade265ec6b8cef1d40a5a1d064f21d7d50356c6c Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 21 Apr 2020 23:44:55 +0200 Subject: [PATCH 091/226] give shared windows runners a try --- .gitlab-ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 79eb6795f..dfeb37a75 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -42,10 +42,7 @@ MacOS: Windows: tags: - - win10 - - msvc2017 - except: - - branches # because our CI VMs are not public, MRs can't use them and timeout + - windows stage: build allow_failure: true script: From 0021dabd8e4d62462c91052e001ad95135d89f6a Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 22 Apr 2020 00:22:48 +0200 Subject: [PATCH 092/226] powershell hell --- .gitlab-ci.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dfeb37a75..b466d011b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,8 +3,9 @@ stages: Debian: tags: - - docker - - linux +# - docker +# - linux + - none_remove_me image: gcc cache: key: apt-cache @@ -46,7 +47,14 @@ Windows: stage: build allow_failure: true script: - # - env # turn on for debugging + - Set-Variable -Name "time" -Value (date -Format "%H:%m") + - echo ${time} + - echo "started by ${GITLAB_USER_NAME}" + - (New-Object System.Net.WebClient).DownloadFile(http://repo.msys2.org/distrib/x86_64/msys2-x86_64-20190524.exe, $PSScriptRoot\msys2.exe) + - $result = Start-Process -Wait -FilePath '$PSScriptRoot\msys2.exe' -ArgumentList '/S' -PassThru + - echo ${result} + - bash + - env # turn on for debugging - sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V - SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe" - call %msBuildLocation% MSVC2017_64\OpenMW.sln /t:Build /p:Configuration=Release /m:%NUMBER_OF_PROCESSORS% From b38459e50558af6af0fc0335344e1d251bb8d7ad Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 22 Apr 2020 00:30:08 +0200 Subject: [PATCH 093/226] powershell hell 2 --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b466d011b..8a5740de8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,7 +50,7 @@ Windows: - Set-Variable -Name "time" -Value (date -Format "%H:%m") - echo ${time} - echo "started by ${GITLAB_USER_NAME}" - - (New-Object System.Net.WebClient).DownloadFile(http://repo.msys2.org/distrib/x86_64/msys2-x86_64-20190524.exe, $PSScriptRoot\msys2.exe) + - (New-Object System.Net.WebClient).DownloadFile('http://repo.msys2.org/distrib/x86_64/msys2-x86_64-20190524.exe', '$PSScriptRoot\msys2.exe') - $result = Start-Process -Wait -FilePath '$PSScriptRoot\msys2.exe' -ArgumentList '/S' -PassThru - echo ${result} - bash From 9106e3646cb49bdb3d285e563b687856171fecf1 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 22 Apr 2020 01:39:51 +0300 Subject: [PATCH 094/226] Fix outdated bug tracker links --- CONTRIBUTING.md | 2 +- apps/wizard/conclusionpage.cpp | 2 +- components/crashcatcher/crashcatcher.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 73ec986b5..264db49cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ Pull Request Guidelines To facilitate the review process, your pull request description should include the following, if applicable: -* A link back to the bug report or forum discussion that prompted the change. Note: when linking bugs, use the syntax ```[Bug #xyz](https://bugs.openmw.org/issues/#xyz)``` to create a clickable link. Writing only 'Bug #xyz' will unfortunately create a link to the Github pull request with that number instead. +* A link back to the bug report or forum discussion that prompted the change. Note: when linking bugs, use the syntax ```[Bug #xyz](https://gitlab.com/OpenMW/openmw/issues/#xyz)``` to create a clickable link. Writing only 'Bug #xyz' will unfortunately create a link to the Github pull request with that number instead. * Summary of the changes made * Reasoning / motivation behind the change * What testing you have carried out to verify the change diff --git a/apps/wizard/conclusionpage.cpp b/apps/wizard/conclusionpage.cpp index f6a6015b8..b1341374d 100644 --- a/apps/wizard/conclusionpage.cpp +++ b/apps/wizard/conclusionpage.cpp @@ -44,7 +44,7 @@ void Wizard::ConclusionPage::initializePage() } else { textLabel->setText(tr("

The OpenMW Wizard failed to install Morrowind on your computer.

\

Please report any bugs you might have encountered to our \ - bug tracker.
Make sure to include the installation log.


")); + bug tracker.
Make sure to include the installation log.


")); } } diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index 80d2102b8..006ab2d88 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -412,7 +412,7 @@ static void crash_handler(const char *logfile) if(logfile) { - std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(logfile) + "'.\n Please report this to https://bugs.openmw.org !"; + std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(logfile) + "'.\n Please report this to https://gitlab.com/OpenMW/openmw/issues !"; SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr); } From 6f1767c37408adc0ac4ceaeb3f7be8e489a8ffb5 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 22 Apr 2020 10:36:11 +0200 Subject: [PATCH 095/226] just get the thing working and leave it up to someone with more win10 knowledge than myself :) --- .gitlab-ci.yml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8a5740de8..514bac1ad 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,9 +3,8 @@ stages: Debian: tags: -# - docker -# - linux - - none_remove_me + - docker + - linux image: gcc cache: key: apt-cache @@ -24,6 +23,7 @@ Debian: artifacts: paths: - build/artifacts/ + MacOS: tags: - macos @@ -50,15 +50,14 @@ Windows: - Set-Variable -Name "time" -Value (date -Format "%H:%m") - echo ${time} - echo "started by ${GITLAB_USER_NAME}" - - (New-Object System.Net.WebClient).DownloadFile('http://repo.msys2.org/distrib/x86_64/msys2-x86_64-20190524.exe', '$PSScriptRoot\msys2.exe') - - $result = Start-Process -Wait -FilePath '$PSScriptRoot\msys2.exe' -ArgumentList '/S' -PassThru - - echo ${result} - - bash - - env # turn on for debugging - - sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V - - SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe" - - call %msBuildLocation% MSVC2017_64\OpenMW.sln /t:Build /p:Configuration=Release /m:%NUMBER_OF_PROCESSORS% - - 7z a OpenMW_MSVC2017_64_%CI_BUILD_REF_NAME%_%CI_BUILD_ID%.zip %CI_PROJECT_DIR%\MSVC2017_64\Release\ +# TODO: to anyone wanting to do further work here, we need to figure out how to get the below working +# TODO: on gitlab's new shared windows runners. They currently don't have bash or anything else installed +# TODO: it is currently just a bare windows 10 with powershell. +# - env # turn on for debugging +# - sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V +# - SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe" +# - call %msBuildLocation% MSVC2017_64\OpenMW.sln /t:Build /p:Configuration=Release /m:%NUMBER_OF_PROCESSORS% +# - 7z a OpenMW_MSVC2017_64_%CI_BUILD_REF_NAME%_%CI_BUILD_ID%.zip %CI_PROJECT_DIR%\MSVC2017_64\Release\ cache: paths: - deps From 68549828b49e7473a294df068dce94e3957977c8 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 22 Apr 2020 13:34:19 +0200 Subject: [PATCH 096/226] create static functions to set/get hidden node mask; compromise on magic numbers to make things easier to understand --- apps/opencs/view/render/mask.hpp | 3 --- apps/opencs/view/render/scenewidget.cpp | 2 -- apps/openmw/mwrender/renderingmanager.cpp | 3 +++ components/nifosg/controller.cpp | 8 +++++--- components/nifosg/controller.hpp | 3 ++- components/nifosg/nifloader.cpp | 19 ++++++++++++++----- components/nifosg/nifloader.hpp | 7 ++++++- 7 files changed, 30 insertions(+), 15 deletions(-) diff --git a/apps/opencs/view/render/mask.hpp b/apps/opencs/view/render/mask.hpp index 55b7c823f..deeab4996 100644 --- a/apps/opencs/view/render/mask.hpp +++ b/apps/opencs/view/render/mask.hpp @@ -10,9 +10,6 @@ namespace CSVRender /// @copydoc MWRender::VisMask enum Mask { - // internal use within NifLoader, do not change - Mask_UpdateVisitor = 0x1, - // elements that are part of the actual scene Mask_Reference = 0x2, Mask_Pathgrid = 0x4, diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index ed232722d..e50d7b2cd 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -88,8 +88,6 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f) // Add ability to signal osg to show its statistics for debugging purposes mView->addEventHandler(new osgViewer::StatsHandler); - mView->getCamera()->setCullMask(~(Mask_UpdateVisitor)); - viewer.addView(mView); viewer.setDone(false); viewer.realize(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 648660669..9a68b15fa 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -20,6 +20,8 @@ #include +#include + #include #include @@ -373,6 +375,7 @@ namespace MWRender mViewer->getCamera()->setCullingMode(cullingMode); mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater)); + NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor); mNearClip = Settings::Manager::getFloat("near clip", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 1842e0017..100aa234a 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -269,12 +269,14 @@ void UVController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv) } } -VisController::VisController(const Nif::NiVisData *data) +VisController::VisController(const Nif::NiVisData *data, unsigned int mask) : mData(data->mVis) + , mMask(mask) { } VisController::VisController() + : mMask(0) { } @@ -282,6 +284,7 @@ VisController::VisController(const VisController ©, const osg::CopyOp ©o : osg::NodeCallback(copy, copyop) , Controller(copy) , mData(copy.mData) + , mMask(copy.mMask) { } @@ -303,8 +306,7 @@ void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv) if (hasInput()) { bool vis = calculate(getInputValue(nv)); - // Leave 0x1 enabled for UpdateVisitor, so we can make ourselves visible again in the future from this update callback - node->setNodeMask(vis ? ~0 : 0x1); + node->setNodeMask(vis ? ~0 : mMask); } traverse(node, nv); } diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index c9bda2e62..ad6ee4d87 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -234,11 +234,12 @@ namespace NifOsg { private: std::vector mData; + unsigned int mMask; bool calculate(float time) const; public: - VisController(const Nif::NiVisData *data); + VisController(const Nif::NiVisData *data, unsigned int mask); VisController(); VisController(const VisController& copy, const osg::CopyOp& copyop); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index af8431da7..7d62d1ef1 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -193,6 +193,17 @@ namespace NifOsg return sShowMarkers; } + unsigned int Loader::sHiddenNodeMask = 0; + + void Loader::setHiddenNodeMask(unsigned int mask) + { + sHiddenNodeMask = mask; + } + unsigned int Loader::getHiddenNodeMask() + { + return sHiddenNodeMask; + } + class LoaderImpl { public: @@ -571,8 +582,7 @@ namespace NifOsg if (nifNode->recType == Nif::RC_RootCollisionNode) { skipMeshes = true; - // Leave mask for UpdateVisitor enabled - node->setNodeMask(0x1); + node->setNodeMask(Loader::getHiddenNodeMask()); } // We can skip creating meshes for hidden nodes if they don't have a VisController that @@ -586,8 +596,7 @@ namespace NifOsg if (!hasVisController) skipMeshes = true; // skip child meshes, but still create the child node hierarchy for animating collision shapes - // now hide this node, but leave the mask for UpdateVisitor enabled so that KeyframeController works - node->setNodeMask(0x1); + node->setNodeMask(Loader::getHiddenNodeMask()); } if ((skipMeshes || hasMarkers) && isAnimated) // make sure the empty node is not optimized away so the physicssystem can find it. @@ -753,7 +762,7 @@ namespace NifOsg { if (visctrl->data.empty()) return; - osg::ref_ptr callback(new VisController(visctrl->data.getPtr())); + osg::ref_ptr callback(new VisController(visctrl->data.getPtr(), Loader::getHiddenNodeMask())); setupController(visctrl, callback, animflags); node->addUpdateCallback(callback); } diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index d2d5e7b2d..6168bb474 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -74,8 +74,13 @@ namespace NifOsg static bool getShowMarkers(); - private: + /// Set the mask to use for hidden nodes. The default is 0, i.e. updates to those nodes can no longer happen. + /// If you need to run animations or physics for hidden nodes, you may want to set this to a non-zero mask and remove exactly that mask from the camera's cull mask. + static void setHiddenNodeMask(unsigned int mask); + static unsigned int getHiddenNodeMask(); + private: + static unsigned int sHiddenNodeMask; static bool sShowMarkers; }; From 40766d746c670a598bacbe26e149a105108f72d3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 22 Apr 2020 16:06:42 +0300 Subject: [PATCH 097/226] Pick the correct aggressive actor in fight distance bias calculation --- apps/openmw/mwmechanics/combat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 978ca61bd..9f5446c11 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -467,7 +467,7 @@ namespace MWMechanics osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3()); osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3()); - float d = getAggroDistance(actor2, pos1, pos2); + float d = getAggroDistance(actor1, pos1, pos2); static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find( "iFightDistanceBase")->mValue.getInteger(); From 6316227594ff8168fe033279d5b6b6aa91d93380 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 22 Apr 2020 15:57:24 +0200 Subject: [PATCH 098/226] hpp not cpp; review --- apps/openmw/mwrender/renderingmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9a68b15fa..481ea9982 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include From 1cd4b9ee6861a46d034d9bcd7a0ce6a4312c0d32 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 099/226] fixes shadows on savegame screenshot --- apps/openmw/mwrender/renderingmanager.cpp | 62 ++++++++++++++++++++--- apps/openmw/mwrender/renderingmanager.hpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 2 +- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 648660669..fac6fcff2 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -744,17 +744,19 @@ namespace MWRender class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback { public: - NotifyDrawCompletedCallback() - : mDone(false) + NotifyDrawCompletedCallback(unsigned int frame) + : mDone(false), mFrame(frame) { } virtual void operator () (osg::RenderInfo& renderInfo) const { - mMutex.lock(); - mDone = true; - mMutex.unlock(); - mCondition.signal(); + OpenThreads::ScopedLock lock(mMutex); + if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame) + { + mDone = true; + mCondition.signal(); + } } void waitTillDone() @@ -769,6 +771,7 @@ namespace MWRender mutable OpenThreads::Condition mCondition; mutable OpenThreads::Mutex mMutex; mutable bool mDone; + unsigned int mFrame; }; bool RenderingManager::screenshot360(osg::Image* image, std::string settingStr) @@ -948,7 +951,7 @@ namespace MWRender mRootNode->addChild(camera); // The draw needs to complete before we can copy back our image. - osg::ref_ptr callback (new NotifyDrawCompletedCallback); + osg::ref_ptr callback (new NotifyDrawCompletedCallback(0)); camera->setFinalDrawCallback(callback); MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOn(false); @@ -967,6 +970,51 @@ namespace MWRender mRootNode->removeChild(camera); } + class ReadImageFromFramebufferCallback : public osg::Drawable::DrawCallback + { + public: + ReadImageFromFramebufferCallback(osg::Image* image, int width, int height) + : mWidth(width), mHeight(height), mImage(image) + { + } + virtual void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* /*drawable*/) const + { + int screenW = renderInfo.getCurrentCamera()->getViewport()->width(); + int screenH = renderInfo.getCurrentCamera()->getViewport()->height(); + double imageaspect = (double)mWidth/(double)mHeight; + int leftPadding = std::max(0, static_cast(screenW - screenH * imageaspect) / 2); + int topPadding = std::max(0, static_cast(screenH - screenW / imageaspect) / 2); + int width = screenW - leftPadding*2; + int height = screenH - topPadding*2; + mImage->readPixels(leftPadding, topPadding, width, height, GL_RGB, GL_UNSIGNED_BYTE); + mImage->scaleImage(mWidth, mHeight, 1); + } + private: + int mWidth; + int mHeight; + osg::ref_ptr mImage; + }; + + void RenderingManager::screenshotScreen(osg::Image* image, int w, int h) + { + osg::Camera* camera = mViewer->getCamera(); + osg::ref_ptr tempDrw = new osg::Drawable; + tempDrw->setDrawCallback(new ReadImageFromFramebufferCallback(image, w, h)); + tempDrw->setCullingActive(false); + tempDrw->getOrCreateStateSet()->setRenderBinDetails(100, "RenderBin", osg::StateSet::USE_RENDERBIN_DETAILS); // so its after all scene bins but before POST_RENDER gui camera + camera->addChild(tempDrw); + osg::ref_ptr callback (new NotifyDrawCompletedCallback(mViewer->getFrameStamp()->getFrameNumber())); + camera->setFinalDrawCallback(callback); + mViewer->eventTraversal(); + mViewer->updateTraversal(); + mViewer->renderingTraversals(); + callback->waitTillDone(); + // now that we've "used up" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed + mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); + camera->removeChild(tempDrw); + camera->setFinalDrawCallback(nullptr); + } + void RenderingManager::screenshot(osg::Image *image, int w, int h, osg::Matrixd cameraTransform) { osg::ref_ptr rttCamera (new osg::Camera); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d4b0b1840..2efe10cf9 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -144,7 +144,8 @@ namespace MWRender void setWaterHeight(float level); /// Take a screenshot of w*h onto the given image, not including the GUI. - void screenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd()); + void screenshotScreen(osg::Image* image, int w, int h); // copie directly from framebuffer and scale to given size + void screenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd()); // make a new render at given size bool screenshot360(osg::Image* image, std::string settingStr); struct RayResult diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 49b55f2a9..73be0e6f4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2554,7 +2554,7 @@ namespace MWWorld void World::screenshot(osg::Image* image, int w, int h) { - mRendering->screenshot(image, w, h); + mRendering->screenshotScreen(image, w, h); } bool World::screenshot360(osg::Image* image, std::string settingStr) From ac256f05ff0f9aae575b175ba4e78ccbf6f8f292 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 22 Apr 2020 19:20:48 +0100 Subject: [PATCH 100/226] 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 c359406473bb45cf79c58f625cac5c0ff7a78418 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 22 Apr 2020 20:29:50 +0200 Subject: [PATCH 101/226] taking anyoldname3 review comments to heart; typo fixes plus rebase --- apps/openmw/mwrender/renderingmanager.cpp | 4 ++-- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index fac6fcff2..f5053cca9 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -995,7 +995,7 @@ namespace MWRender osg::ref_ptr mImage; }; - void RenderingManager::screenshotScreen(osg::Image* image, int w, int h) + void RenderingManager::screenshotFramebuffer(osg::Image* image, int w, int h) { osg::Camera* camera = mViewer->getCamera(); osg::ref_ptr tempDrw = new osg::Drawable; @@ -1009,7 +1009,7 @@ namespace MWRender mViewer->updateTraversal(); mViewer->renderingTraversals(); callback->waitTillDone(); - // now that we've "used up" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed + // now that we've "used up" the current frame, get a fresh frame number for the next frame() following after the screenshot is completed mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); camera->removeChild(tempDrw); camera->setFinalDrawCallback(nullptr); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 2efe10cf9..09cff26f1 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -144,8 +144,8 @@ namespace MWRender void setWaterHeight(float level); /// Take a screenshot of w*h onto the given image, not including the GUI. - void screenshotScreen(osg::Image* image, int w, int h); // copie directly from framebuffer and scale to given size void screenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd()); // make a new render at given size + void screenshotFramebuffer(osg::Image* image, int w, int h); // copy directly from framebuffer and scale to given size bool screenshot360(osg::Image* image, std::string settingStr); struct RayResult diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 73be0e6f4..ce06d887c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2554,7 +2554,7 @@ namespace MWWorld void World::screenshot(osg::Image* image, int w, int h) { - mRendering->screenshotScreen(image, w, h); + mRendering->screenshotFramebuffer(image, w, h); } bool World::screenshot360(osg::Image* image, std::string settingStr) From ed20d869b4e8a8283bd726b82da2ca6a074c8afc Mon Sep 17 00:00:00 2001 From: bzzt Date: Thu, 13 Jun 2019 13:37:00 +0000 Subject: [PATCH 102/226] waterculling for both terrain --- apps/openmw/mwrender/renderingmanager.cpp | 8 ++++-- apps/openmw/mwrender/water.cpp | 28 ++++++++++++++++++ apps/openmw/mwrender/water.hpp | 4 +++ components/terrain/quadtreeworld.cpp | 35 +++++++++++++++++++++++ components/terrain/terraingrid.cpp | 11 +++++++ components/terrain/terraingrid.hpp | 3 +- components/terrain/world.cpp | 8 ++++++ components/terrain/world.hpp | 35 +++++++++++++++++++++++ 8 files changed, 129 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 31af55a44..c966a5d67 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -282,8 +282,6 @@ namespace MWRender mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem)); - mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath)); - 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"); @@ -322,6 +320,9 @@ namespace MWRender mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); mTerrain->setWorkQueue(mWorkQueue.get()); + // water goes after terrain for correct waterculling order + mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath)); + mCamera.reset(new Camera(mViewer->getCamera())); mViewer->setLightingMode(osgViewer::View::NO_LIGHT); @@ -541,6 +542,8 @@ namespace MWRender void RenderingManager::enableTerrain(bool enable) { + if (!enable) + mWater->setCullCallback(nullptr); mTerrain->enable(enable); } @@ -740,6 +743,7 @@ namespace MWRender void RenderingManager::setWaterHeight(float height) { + mWater->setCullCallback(mTerrain->getHeightCullCallback(height, Mask_Water)); mWater->setHeight(height); mSky->setWaterHeight(height); } diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 6d230d36e..c9d16b728 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -436,6 +436,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem , mToggled(true) , mTop(0) , mInterior(false) + , mCullCallback(nullptr) { mSimulation.reset(new RippleSimulation(mSceneRoot, resourceSystem)); @@ -466,6 +467,29 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem ico->add(mWaterNode); } +void Water::setCullCallback(osg::Callback* callback) +{ + if (mCullCallback) + { + mWaterNode->removeCullCallback(mCullCallback); + if (mReflection) + mReflection->removeCullCallback(mCullCallback); + if (mRefraction) + mRefraction->removeCullCallback(mCullCallback); + } + + mCullCallback = callback; + + if (callback) + { + mWaterNode->addCullCallback(callback); + if (mReflection) + mReflection->addCullCallback(callback); + if (mRefraction) + mRefraction->addCullCallback(callback); + } +} + osg::Uniform *Water::getRainIntensityUniform() { return mRainIntensityUniform.get(); @@ -491,6 +515,8 @@ void Water::updateWaterMaterial() mReflection = new Reflection(mInterior); mReflection->setWaterLevel(mTop); mReflection->setScene(mSceneRoot); + if (mCullCallback) + mReflection->addCullCallback(mCullCallback); mParent->addChild(mReflection); if (Settings::Manager::getBool("refraction", "Water")) @@ -498,6 +524,8 @@ void Water::updateWaterMaterial() mRefraction = new Refraction; mRefraction->setWaterLevel(mTop); mRefraction->setScene(mSceneRoot); + if (mCullCallback) + mRefraction->addCullCallback(mCullCallback); mParent->addChild(mRefraction); } diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 5d99413c6..3787ef426 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -71,6 +71,8 @@ namespace MWRender float mTop; bool mInterior; + osg::Callback* mCullCallback; + osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY); void updateVisible(); @@ -88,6 +90,8 @@ namespace MWRender const std::string& resourcePath); ~Water(); + void setCullCallback(osg::Callback* callback); + void listAssetsToPreload(std::vector& textures); void setEnabled(bool enabled); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 396ac1138..1d968ddc4 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -307,6 +307,38 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, C entry.mRenderingNode = chunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags); } +void updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil::CullVisitor* cv, float cellworldsize, bool outofworld) +{ + if (!(cv->getTraversalMask() & callback->getCullMask())) + return; + float lowZ = FLT_MAX; + float highZ = callback->getHighZ(); + if (cv->getEyePoint().z() <= highZ || outofworld) + { + callback->setLowZ(-FLT_MAX); + return; + } + cv->pushCurrentMask(); + for (unsigned int i=0; igetNumEntries(); ++i) + { + ViewData::Entry& entry = vd->getEntry(i); + osg::BoundingBox bb = static_cast(entry.mRenderingNode->asGroup()->getChild(0))->getBoundingBox(); + float minZ = bb._min.z(); + if (minZ > highZ) + continue; + osg::Vec3f ofs (entry.mNode->getCenter().x()*cellworldsize, entry.mNode->getCenter().y()*cellworldsize, 0.f); + bb._min += ofs; bb._max += ofs; + bb._min.z() = highZ; + bb._max.z() = highZ; + if (cv->isCulled(bb)) + continue; + lowZ = minZ; + break; + } + callback->setLowZ(lowZ); + cv->popCurrentMask(); +} + void QuadTreeWorld::accept(osg::NodeVisitor &nv) { bool isCullVisitor = nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR; @@ -384,6 +416,9 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) entry.mRenderingNode->accept(nv); } + if (isCullVisitor) + updateWaterCullingView(mHeightCullCallback, vd, static_cast(&nv), mStorage->getCellWorldSize(), !mGrid.empty()); + 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. diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 7310846c2..a0e5e4718 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "chunkmanager.hpp" #include "compositemaprenderer.hpp" @@ -80,6 +81,7 @@ void TerrainGrid::loadCell(int x, int y) mTerrainRoot->addChild(terrainNode); mGrid[std::make_pair(x,y)] = terrainNode; + updateWaterCulling(); } void TerrainGrid::unloadCell(int x, int y) @@ -94,6 +96,15 @@ void TerrainGrid::unloadCell(int x, int y) mTerrainRoot->removeChild(terrainNode); mGrid.erase(it); + updateWaterCulling(); +} + +void TerrainGrid::updateWaterCulling() +{ + osg::ComputeBoundsVisitor computeBoundsVisitor; + mTerrainRoot->accept(computeBoundsVisitor); + float lowZ = computeBoundsVisitor.getBoundingBox()._min.z(); + mHeightCullCallback->setLowZ(lowZ); } View *TerrainGrid::createView() diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index eb30fb97d..e633a258f 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -27,8 +27,9 @@ namespace Terrain View* createView(); - private: + protected: osg::ref_ptr buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); + void updateWaterCulling(); // split each ESM::Cell into mNumSplits*mNumSplits terrain chunks unsigned int mNumSplits; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index da3bdb5c2..2d53f4090 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -19,6 +19,7 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst , mParent(parent) , mResourceSystem(resourceSystem) , mBorderVisible(false) + , mHeightCullCallback(new HeightCullCallback) { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); @@ -120,4 +121,11 @@ void World::clearAssociatedCaches() mChunkManager->clearCache(); } +osg::Callback* World::getHeightCullCallback(float highz, unsigned int mask) +{ + mHeightCullCallback->setHighZ(highz); + mHeightCullCallback->setCullMask(mask); + return mHeightCullCallback; +} + } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 0402b8197..92eb69b80 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,37 @@ namespace Terrain class ChunkManager; class CompositeMapRenderer; +class HeightCullCallback : public osg::NodeCallback +{ +public: + HeightCullCallback() : mLowZ(-FLT_MAX), mHighZ(FLT_MAX), mMask(~0) {} + + 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; + float mHighZ; + unsigned int mMask; +}; + /** * @brief A View is a collection of rendering objects that are visible from a given camera/intersection. * The base View class is part of the interface for usage in conjunction with preload feature. @@ -116,6 +148,8 @@ namespace Terrain Storage* getStorage() { return mStorage; } + osg::Callback* getHeightCullCallback(float highz, unsigned int mask); + protected: Storage* mStorage; @@ -135,6 +169,7 @@ namespace Terrain bool mBorderVisible; std::set> mLoadedCells; + osg::ref_ptr mHeightCullCallback; }; } From 407fbe320ef2ae7104e8c13a2156f055c41e23ed Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Thu, 23 Apr 2020 10:10:50 +0200 Subject: [PATCH 103/226] cleanup and use C++ version of FLT_MAX --- components/terrain/quadtreeworld.cpp | 5 +++-- components/terrain/world.hpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 1d968ddc4..56fa8b7f3 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -311,11 +312,11 @@ void updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil: { if (!(cv->getTraversalMask() & callback->getCullMask())) return; - float lowZ = FLT_MAX; + float lowZ = std::numeric_limits::max(); float highZ = callback->getHighZ(); if (cv->getEyePoint().z() <= highZ || outofworld) { - callback->setLowZ(-FLT_MAX); + callback->setLowZ(-std::numeric_limits::max()); return; } cv->pushCurrentMask(); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 92eb69b80..fb6c45967 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -7,9 +7,9 @@ #include #include +#include #include #include -#include #include "defs.hpp" #include "cellborder.hpp" @@ -43,7 +43,7 @@ namespace Terrain class HeightCullCallback : public osg::NodeCallback { public: - HeightCullCallback() : mLowZ(-FLT_MAX), mHighZ(FLT_MAX), mMask(~0) {} + HeightCullCallback() : mLowZ(-std::numeric_limits::max()), mHighZ(std::numeric_limits::max()), mMask(~0) {} void setLowZ(float z) { From 48713915cb21b86f515fd5d1e95808d8ad56bf17 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Thu, 23 Apr 2020 11:12:10 +0200 Subject: [PATCH 104/226] re-use bzzts waterplane overlay for debug; makes it easier to test. To use this, set the env OPENMW_WATER_CULLING_DEBUG=1; You will see blue rectangles corresponding to water outlines. Once there are no more outlines, water is culled. You can further see this by pressing F3 3 times to check the the amount of quads. Before culling it should be around 1600, after culling it should drop to 0. --- components/terrain/chunkmanager.cpp | 2 ++ components/terrain/quadtreeworld.cpp | 34 ++++++++++++++++++++++---- components/terrain/terraindrawable.cpp | 29 ++++++++++++++++++++++ components/terrain/terraindrawable.hpp | 8 ++++-- 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 3c76945d3..95c1ca491 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -227,6 +227,8 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve transform->addChild(geometry); transform->getBound(); + geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts); + if (mSceneManager->getIncrementalCompileOperation()) { mSceneManager->getIncrementalCompileOperation()->add(geometry); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 56fa8b7f3..ae3656fd2 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -1,6 +1,8 @@ #include "quadtreeworld.hpp" #include +#include +#include #include #include @@ -13,6 +15,7 @@ #include "viewdata.hpp" #include "chunkmanager.hpp" #include "compositemaprenderer.hpp" +#include "terraindrawable.hpp" namespace { @@ -323,9 +326,8 @@ void updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil: for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - osg::BoundingBox bb = static_cast(entry.mRenderingNode->asGroup()->getChild(0))->getBoundingBox(); - float minZ = bb._min.z(); - if (minZ > highZ) + osg::BoundingBox bb = static_cast(entry.mRenderingNode->asGroup()->getChild(0))->getWaterBoundingBox(); + if (!bb.valid()) continue; osg::Vec3f ofs (entry.mNode->getCenter().x()*cellworldsize, entry.mNode->getCenter().y()*cellworldsize, 0.f); bb._min += ofs; bb._max += ofs; @@ -333,8 +335,30 @@ void updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil: bb._max.z() = highZ; if (cv->isCulled(bb)) continue; - lowZ = minZ; - break; + lowZ = bb._min.z(); + + static bool debug = getenv("OPENMW_WATER_CULLING_DEBUG") != nullptr; + if (!debug) + break; + osg::Box* b = new osg::Box; + b->set(bb.center(), bb._max - bb.center()); + osg::ShapeDrawable* drw = new osg::ShapeDrawable(b); + static osg::ref_ptr stateset = nullptr; + if (!stateset) + { + stateset = new osg::StateSet; + stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + stateset->setAttributeAndModes(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE), osg::StateAttribute::ON); + osg::Material* m = new osg::Material; + m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,1,1)); + m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,1)); + m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,1)); + stateset->setAttributeAndModes(m, osg::StateAttribute::ON); + stateset->setRenderBinDetails(100,"RenderBin"); + } + drw->setStateSet(stateset); + drw->accept(*cv); } callback->setLowZ(lowZ); cv->popCurrentMask(); diff --git a/components/terrain/terraindrawable.cpp b/components/terrain/terraindrawable.cpp index c37074dac..9593687cf 100644 --- a/components/terrain/terraindrawable.cpp +++ b/components/terrain/terraindrawable.cpp @@ -10,6 +10,16 @@ namespace Terrain { +TerrainDrawable::TerrainDrawable() +{ + +} + +TerrainDrawable::~TerrainDrawable() +{ + +} + TerrainDrawable::TerrainDrawable(const TerrainDrawable ©, const osg::CopyOp ©op) : osg::Geometry(copy, copyop) , mPasses(copy.mPasses) @@ -118,6 +128,25 @@ void TerrainDrawable::setLightListCallback(SceneUtil::LightListCallback *lightLi mLightListCallback = lightListCallback; } +void TerrainDrawable::setupWaterBoundingBox(float waterheight, float margin) +{ + osg::Vec3Array* vertices = static_cast(getVertexArray()); + for (unsigned int i=0; isize(); ++i) + { + const osg::Vec3f& vertex = (*vertices)[i]; + if (vertex.z() <= waterheight) + mWaterBoundingBox.expandBy(vertex); + } + if (mWaterBoundingBox.valid()) + { + const osg::BoundingBox& bb = getBoundingBox(); + mWaterBoundingBox.xMin() = std::max(bb.xMin(), mWaterBoundingBox.xMin() - margin); + mWaterBoundingBox.yMin() = std::max(bb.yMin(), mWaterBoundingBox.yMin() - margin); + mWaterBoundingBox.xMax() = std::min(bb.xMax(), mWaterBoundingBox.xMax() + margin); + mWaterBoundingBox.xMax() = std::min(bb.xMax(), mWaterBoundingBox.xMax() + margin); + } +} + void TerrainDrawable::compileGLObjects(osg::RenderInfo &renderInfo) const { for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it) diff --git a/components/terrain/terraindrawable.hpp b/components/terrain/terraindrawable.hpp index 516b1abdb..a84200f66 100644 --- a/components/terrain/terraindrawable.hpp +++ b/components/terrain/terraindrawable.hpp @@ -36,8 +36,8 @@ namespace Terrain virtual const char* className() const { return "TerrainDrawable"; } virtual const char* libraryName() const { return "Terrain"; } - TerrainDrawable() = default; - ~TerrainDrawable() = default; + TerrainDrawable(); + ~TerrainDrawable(); // has to be defined in the cpp file because we only forward declared some members. TerrainDrawable(const TerrainDrawable& copy, const osg::CopyOp& copyop); virtual void accept(osg::NodeVisitor &nv); @@ -52,10 +52,14 @@ namespace Terrain virtual void compileGLObjects(osg::RenderInfo& renderInfo) const; + void setupWaterBoundingBox(float waterheight, float margin); + const osg::BoundingBox& getWaterBoundingBox() const { return mWaterBoundingBox; } + void setCompositeMap(CompositeMap* map) { mCompositeMap = map; } void setCompositeMapRenderer(CompositeMapRenderer* renderer) { mCompositeMapRenderer = renderer; } private: + osg::BoundingBox mWaterBoundingBox; PassVector mPasses; osg::ref_ptr mClusterCullingCallback; From 89ff1974ee180fb27ab61da4c9ea78bb082f6061 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 23 Apr 2020 13:14:48 +0300 Subject: [PATCH 105/226] Remove issue that hasn't been solved properly --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1043ca0cf..93681ef21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -165,7 +165,6 @@ Bug #5099: Non-swimming enemies will enter water if player is water walking Bug #5103: Sneaking state behavior is still inconsistent 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 #5110: ModRegion with a redundant numerical argument breaks script execution Bug #5112: Insufficient magicka for current spell not reflected on HUD icon From 756ec7117b4d6b8362ac22415c2e57ddc59c3e80 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Thu, 23 Apr 2020 17:40:10 +0200 Subject: [PATCH 106/226] make mGrid (and friends) private again, create accessor method to return bool of mGrid.empty() --- components/terrain/quadtreeworld.cpp | 2 +- components/terrain/terraingrid.hpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index ae3656fd2..0140ade49 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -442,7 +442,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) } if (isCullVisitor) - updateWaterCullingView(mHeightCullCallback, vd, static_cast(&nv), mStorage->getCellWorldSize(), !mGrid.empty()); + 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. diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index e633a258f..cb37ca296 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -28,6 +28,9 @@ namespace Terrain View* createView(); protected: + bool isGridEmpty() { return mGrid.empty(); } + + private: osg::ref_ptr buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); void updateWaterCulling(); From 7aaec5e989f58327e30741e016d0d7d9ad5fd0b8 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 23 Apr 2020 19:16:32 +0200 Subject: [PATCH 107/226] Log shader template name on parse error --- components/shader/shadermanager.cpp | 42 +++++++++++++++-------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 0a7345b97..c2126275f 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) + bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& shaderTemplate) { 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) << "Invalid #include"; + Log(Debug::Error) << "Shader " << shaderTemplate << " error: Invalid #include"; return false; } size_t end = source.find('"', start+1); if (end == std::string::npos) { - Log(Debug::Error) << "Invalid #include"; + Log(Debug::Error) << "Shader " << shaderTemplate << " 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) << "Failed to open " << includePath.string(); + Log(Debug::Error) << "Shader " << shaderTemplate << " error: Failed to open include " << includePath.string(); return false; } @@ -120,14 +120,14 @@ namespace Shader if (includedFiles.insert(includePath).second == false) { - Log(Debug::Error) << "Detected cyclic #includes"; + Log(Debug::Error) << "Shader " << shaderTemplate << " error: Detected cyclic #includes"; return false; } } return true; } - bool parseFors(std::string& source) + bool parseFors(std::string& source, const std::string& shaderTemplate) { 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) << "Unexpected EOF"; + Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unexpected EOF"; return false; } std::string command = source.substr(foundPos + 1, endPos - (foundPos + 1)); if (command != "foreach") { - Log(Debug::Error) << "Unknown shader directive: $" << command; + Log(Debug::Error) << "Shader " << shaderTemplate << " 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) << "Unexpected EOF"; + Log(Debug::Error) << "Shader " << shaderTemplate << " 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) << "Unexpected EOF"; + Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unexpected EOF"; return false; } std::string list = source.substr(listStart, listEnd - listStart); @@ -171,13 +171,13 @@ namespace Shader size_t contentEnd = source.find("$endforeach", contentStart); if (contentEnd == std::string::npos) { - Log(Debug::Error) << "Unexpected EOF"; + Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unexpected EOF"; return false; } std::string content = source.substr(contentStart, contentEnd - contentStart); size_t overallEnd = contentEnd + std::string("$endforeach").length(); - + size_t lineDirectivePosition = source.rfind("#line", overallEnd); int lineNumber; if (lineDirectivePosition != std::string::npos) @@ -210,7 +210,8 @@ namespace Shader return true; } - bool parseDefines(std::string& source, const ShaderManager::DefineMap& defines, const ShaderManager::DefineMap& globalDefines) + bool parseDefines(std::string& source, const ShaderManager::DefineMap& defines, + const ShaderManager::DefineMap& globalDefines, const std::string& shaderTemplate) { const char escapeCharacter = '@'; size_t foundPos = 0; @@ -220,7 +221,7 @@ namespace Shader size_t endPos = source.find_first_of(" \n\r()[].;,", foundPos); if (endPos == std::string::npos) { - Log(Debug::Error) << "Unexpected EOF"; + Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unexpected EOF"; return false; } std::string define = source.substr(foundPos+1, endPos - (foundPos+1)); @@ -233,7 +234,7 @@ namespace Shader size_t iterNameEnd = source.find_first_of(" \n\r()[].;,", iterNameStart); if (iterNameEnd == std::string::npos) { - Log(Debug::Error) << "Unexpected EOF"; + Log(Debug::Error) << "Shader " << shaderTemplate << " error: Unexpected EOF"; return false; } forIterators.push_back(source.substr(iterNameStart, iterNameEnd - iterNameStart)); @@ -243,7 +244,7 @@ namespace Shader source.replace(foundPos, 1, "$"); if (forIterators.empty()) { - Log(Debug::Error) << "endforeach without foreach"; + Log(Debug::Error) << "Shader " << shaderTemplate << " error: endforeach without foreach"; return false; } else @@ -263,7 +264,7 @@ namespace Shader } else { - Log(Debug::Error) << "Undefined " << define; + Log(Debug::Error) << "Shader " << shaderTemplate << " error: Undefined " << define; return false; } } @@ -291,7 +292,8 @@ namespace Shader // parse includes std::string source = buffer.str(); - if (!addLineDirectivesAfterConditionalBlocks(source) || !parseIncludes(boost::filesystem::path(mPath), source)) + if (!addLineDirectivesAfterConditionalBlocks(source) + || !parseIncludes(boost::filesystem::path(mPath), source, shaderTemplate)) return nullptr; templateIt = mShaderTemplates.insert(std::make_pair(shaderTemplate, source)).first; @@ -301,7 +303,7 @@ namespace Shader if (shaderIt == mShaders.end()) { std::string shaderSource = templateIt->second; - if (!parseDefines(shaderSource, defines, mGlobalDefines) || !parseFors(shaderSource)) + if (!parseDefines(shaderSource, defines, mGlobalDefines, shaderTemplate) || !parseFors(shaderSource, shaderTemplate)) { // 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)); @@ -350,7 +352,7 @@ namespace Shader // I'm not sure how to handle a shader that was already broken as there's no way to get a potential replacement to the nodes that need it. continue; std::string shaderSource = mShaderTemplates[templateId]; - if (!parseDefines(shaderSource, defines, mGlobalDefines) || !parseFors(shaderSource)) + if (!parseDefines(shaderSource, defines, mGlobalDefines, templateId) || !parseFors(shaderSource, templateId)) // We just broke the shader and there's no way to force existing objects back to fixed-function mode as we would when creating the shader. // If we put a nullptr in the shader map, we just lose the ability to put a working one in later. continue; From ed970f4d17d0803e31092e055d753dc2010ddbfe Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 24 Apr 2020 06:26:08 +0000 Subject: [PATCH 108/226] Apply suggestion to components/terrain/terraingrid.hpp --- components/terrain/terraingrid.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index cb37ca296..8b36448c1 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -28,7 +28,7 @@ namespace Terrain View* createView(); protected: - bool isGridEmpty() { return mGrid.empty(); } + bool isGridEmpty() const { return mGrid.empty(); } private: osg::ref_ptr buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); From 688e804548861d3b12009ab414da05e58438aae7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Apr 2020 13:21:49 +0300 Subject: [PATCH 109/226] 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 ee60d4bcea96ab5c8abcfc807bb242e29c68d5f4 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 7 Nov 2019 22:08:15 +0100 Subject: [PATCH 110/226] Remove unused ItemStack::stacks --- apps/openmw/mwgui/itemmodel.cpp | 20 -------------------- apps/openmw/mwgui/itemmodel.hpp | 1 - 2 files changed, 21 deletions(-) diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 5bbf74e26..cf88efaae 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -32,26 +32,6 @@ namespace MWGui { } - bool ItemStack::stacks(const ItemStack &other) - { - if(mBase == other.mBase) - return true; - - // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure - if (mBase.getContainerStore() && other.mBase.getContainerStore()) - return mBase.getContainerStore()->stacks(mBase, other.mBase) - && other.mBase.getContainerStore()->stacks(mBase, other.mBase); - - if (mBase.getContainerStore()) - return mBase.getContainerStore()->stacks(mBase, other.mBase); - if (other.mBase.getContainerStore()) - return other.mBase.getContainerStore()->stacks(mBase, other.mBase); - - MWWorld::ContainerStore store; - return store.stacks(mBase, other.mBase); - - } - bool operator == (const ItemStack& left, const ItemStack& right) { if (left.mType != right.mType) diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index 36432d479..4d923bae3 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -13,7 +13,6 @@ namespace MWGui { ItemStack (const MWWorld::Ptr& base, ItemModel* creator, size_t count); ItemStack(); - bool stacks (const ItemStack& other); ///< like operator==, only without checking mType enum Type From c59c8ae1f4282365fa07f2e5cbcdd8734f8d0d80 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 20 Apr 2020 18:36:51 +0200 Subject: [PATCH 111/226] Remove unused macro --- components/sceneutil/detourdebugdraw.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/components/sceneutil/detourdebugdraw.cpp b/components/sceneutil/detourdebugdraw.cpp index b9c2fecef..7ef329fc1 100644 --- a/components/sceneutil/detourdebugdraw.cpp +++ b/components/sceneutil/detourdebugdraw.cpp @@ -7,9 +7,6 @@ #include #include -#define OPENMW_TO_STRING(X) #X -#define OPENMW_LINE_STRING OPENMW_TO_STRING(__LINE__) - namespace { using DetourNavigator::operator<<; @@ -121,6 +118,3 @@ namespace SceneUtil mColors->push_back(value); } } - -#undef OPENMW_TO_STRING -#undef OPENMW_LINE_STRING From 847cd41b010af8b7c5e48301e46fb527b0a0b6f9 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 4 Apr 2020 01:16:55 +0200 Subject: [PATCH 112/226] Update recastnavigation to 57610fa6ef31b39020231906f8c5d40eaa8294ae --- extern/recastnavigation/.id | 2 +- extern/recastnavigation/Recast/Source/RecastContour.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/recastnavigation/.id b/extern/recastnavigation/.id index f15ce513c..81e564671 100644 --- a/extern/recastnavigation/.id +++ b/extern/recastnavigation/.id @@ -1 +1 @@ -c40188c796f089f89a42e0b939d934178dbcfc5c +57610fa6ef31b39020231906f8c5d40eaa8294ae diff --git a/extern/recastnavigation/Recast/Source/RecastContour.cpp b/extern/recastnavigation/Recast/Source/RecastContour.cpp index 277ab0150..6574c11b6 100644 --- a/extern/recastnavigation/Recast/Source/RecastContour.cpp +++ b/extern/recastnavigation/Recast/Source/RecastContour.cpp @@ -1009,7 +1009,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, if (cset.nconts > 0) { // Calculate winding of all polygons. - rcScopedDelete winding((char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP)); + rcScopedDelete winding((signed char*)rcAlloc(sizeof(signed char)*cset.nconts, RC_ALLOC_TEMP)); if (!winding) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts); From 975e4f99084a36d7d3c636cb5520c326b425cf0c Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 25 Apr 2020 01:26:06 +0200 Subject: [PATCH 113/226] Fix warning -Wrange-loop-construct apps/openmw/mwgui/loadingscreen.cpp:81:36: warning: loop variable 'extension' of type 'const std::__cxx11::basic_string' creates a copy from type 'const std::__cxx11::basic_string' [-Wrange-loop-construct] for(auto const extension: supported_extensions) ^ apps/openmw/mwgui/loadingscreen.cpp:81:25: note: use reference type 'const std::__cxx11::basic_string &' to prevent copying for(auto const extension: supported_extensions) ^~~~~~~~~~~~~~~~~~~~~ & --- apps/openmw/mwgui/loadingscreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 54382ab4d..7ab39915f 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -78,7 +78,7 @@ namespace MWGui size_t pos = name.find_last_of('.'); if (pos != std::string::npos) { - for(auto const extension: supported_extensions) + for(auto const& extension: supported_extensions) { if (name.compare(pos, name.size() - pos, extension) == 0) { From ffacc30597ee38d6f4fb00154b1f325400e30c84 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 25 Apr 2020 01:29:43 +0200 Subject: [PATCH 114/226] Use std::array for a list of extensions This list doesn't change and the size is known at compile time. --- apps/openmw/mwgui/loadingscreen.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 7ab39915f..093a2f34c 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -1,5 +1,7 @@ #include "loadingscreen.hpp" +#include + #include #include @@ -67,7 +69,7 @@ namespace MWGui mVFS->normalizeFilename(pattern); /* priority given to the left */ - std::list supported_extensions = {".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}; + const std::array supported_extensions {{".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}}; auto found = index.lower_bound(pattern); while (found != index.end()) From 066f0a744fa181c3d0d80335a4d118a0e408f59c Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 25 Apr 2020 00:26:42 +0200 Subject: [PATCH 115/226] 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 7502db1570c6773487dcc13ef35363cb926a532f Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 22 Feb 2020 12:13:08 -0800 Subject: [PATCH 116/226] Add number of thread jobs to update jobs stats --- .../detournavigator/asyncnavmeshupdater.cpp | 17 +++++++++++++---- .../detournavigator/asyncnavmeshupdater.hpp | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index e085aba16..da1ac19a7 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -7,6 +7,8 @@ #include +#include + namespace { using DetourNavigator::ChangeType; @@ -101,7 +103,7 @@ namespace DetourNavigator void AsyncNavMeshUpdater::wait() { std::unique_lock lock(mMutex); - mDone.wait(lock, [&] { return mJobs.empty(); }); + mDone.wait(lock, [&] { return mJobs.empty() && getTotalThreadJobsUnsafe() == 0; }); } void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const @@ -110,7 +112,7 @@ namespace DetourNavigator { const std::lock_guard lock(mMutex); - jobs = mJobs.size(); + jobs = mJobs.size() + getTotalThreadJobsUnsafe(); } stats.setAttribute(frameNumber, "NavMesh UpdateJobs", jobs); @@ -188,12 +190,13 @@ namespace DetourNavigator while (true) { - const auto hasJob = [&] { return !mJobs.empty() || !threadQueue.mPushed.empty(); }; + const auto hasJob = [&] { return !mJobs.empty() || !threadQueue.mJobs.empty(); }; if (!mHasJob.wait_for(lock, std::chrono::milliseconds(10), hasJob)) { mFirstStart.lock()->reset(); - mDone.notify_all(); + if (getTotalThreadJobsUnsafe() == 0) + mDone.notify_all(); return boost::none; } @@ -327,4 +330,10 @@ namespace DetourNavigator if (agent->second.empty()) locked->erase(agent); } + + std::size_t AsyncNavMeshUpdater::getTotalThreadJobsUnsafe() const + { + return std::accumulate(mThreadsQueues.begin(), mThreadsQueues.end(), std::size_t(0), + [] (auto r, const auto& v) { return r + v.second.mJobs.size(); }); + } } diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 3f17d5ca0..c833d617c 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -114,6 +114,8 @@ namespace DetourNavigator std::thread::id lockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile); void unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile); + + inline std::size_t getTotalThreadJobsUnsafe() const; }; } From df6e85b619390eb1753766e808aa01942889739f Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 24 Feb 2020 18:38:39 -0800 Subject: [PATCH 117/226] Use callback to handle changed tiles Instead of collecting changed tiles into a temporary vector. --- .../tilecachedrecastmeshmanager.cpp | 27 ++++++---- components/detournavigator/navmeshmanager.cpp | 8 +-- .../tilecachedrecastmeshmanager.cpp | 37 -------------- .../tilecachedrecastmeshmanager.hpp | 50 ++++++++++++++++++- 4 files changed, 67 insertions(+), 55 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index e44ae4878..eac3c024f 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -20,6 +20,7 @@ namespace struct DetourNavigatorTileCachedRecastMeshManagerTest : Test { Settings mSettings; + std::vector mChangedTiles; DetourNavigatorTileCachedRecastMeshManagerTest() { @@ -29,6 +30,11 @@ namespace mSettings.mTileSize = 64; mSettings.mTrianglesPerChunk = 256; } + + void onChangedTile(const TilePosition& tilePosition) + { + mChangedTiles.push_back(tilePosition); + } }; TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_empty_should_return_nullptr) @@ -78,8 +84,10 @@ namespace const btBoxShape boxShape(btVector3(20, 20, 100)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); manager.addObject(ObjectId(&boxShape), boxShape, transform, AreaType::AreaType_ground); + EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, + [&] (const auto& v) { onChangedTile(v); })); EXPECT_THAT( - manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground), + mChangedTiles, ElementsAre(TilePosition(-1, -1), TilePosition(-1, 0), TilePosition(0, -1), TilePosition(0, 0), TilePosition(1, -1), TilePosition(1, 0)) ); @@ -90,10 +98,9 @@ namespace TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); - EXPECT_EQ( - manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground), - std::vector() - ); + EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, + [&] (const auto& v) { onChangedTile(v); })); + EXPECT_EQ(mChangedTiles, std::vector()); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile) @@ -127,7 +134,7 @@ namespace EXPECT_NE(manager.getMesh(TilePosition(1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(1, -1)), nullptr); - manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); @@ -144,7 +151,7 @@ namespace EXPECT_EQ(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(-1, 0)), nullptr); - manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); EXPECT_EQ(manager.getMesh(TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(1, -1)), nullptr); } @@ -172,7 +179,7 @@ namespace EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr); - manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); @@ -205,7 +212,7 @@ namespace const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); manager.addObject(ObjectId(&boxShape), boxShape, transform, AreaType::AreaType_ground); const auto beforeUpdateRevision = manager.getRevision(); - manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1); } @@ -215,7 +222,7 @@ namespace const btBoxShape boxShape(btVector3(20, 20, 100)); manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const auto beforeUpdateRevision = manager.getRevision(); - manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision); } diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index a769981d3..b6c25bd93 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -56,12 +56,8 @@ namespace DetourNavigator bool NavMeshManager::updateObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType) { - const auto changedTiles = mRecastMeshManager.updateObject(id, shape, transform, areaType); - if (changedTiles.empty()) - return false; - for (const auto& tile : changedTiles) - addChangedTile(tile, ChangeType::update); - return true; + return mRecastMeshManager.updateObject(id, shape, transform, areaType, + [&] (const auto& tile) { addChangedTile(tile, ChangeType::update); }); } bool NavMeshManager::removeObject(const ObjectId id) diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index bbdbd410a..9debe5dea 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -31,43 +31,6 @@ namespace DetourNavigator return result; } - std::vector TileCachedRecastMeshManager::updateObject(const ObjectId id, const btCollisionShape& shape, - const btTransform& transform, const AreaType areaType) - { - const auto object = mObjectsTilesPositions.find(id); - if (object == mObjectsTilesPositions.end()) - return std::vector(); - auto& currentTiles = object->second; - const auto border = getBorderSize(mSettings); - std::vector changedTiles; - std::set newTiles; - { - auto tiles = mTiles.lock(); - const auto onTilePosition = [&] (const TilePosition& tilePosition) - { - if (currentTiles.count(tilePosition)) - { - newTiles.insert(tilePosition); - if (updateTile(id, transform, areaType, tilePosition, tiles.get())) - changedTiles.push_back(tilePosition); - } - else if (addTile(id, shape, transform, areaType, tilePosition, border, tiles.get())) - { - newTiles.insert(tilePosition); - changedTiles.push_back(tilePosition); - } - }; - getTilesPositions(shape, transform, mSettings, onTilePosition); - for (const auto& tile : currentTiles) - if (!newTiles.count(tile) && removeTile(id, tile, tiles.get())) - changedTiles.push_back(tile); - } - std::swap(currentTiles, newTiles); - if (!changedTiles.empty()) - ++mRevision; - return changedTiles; - } - boost::optional TileCachedRecastMeshManager::removeObject(const ObjectId id) { const auto object = mObjectsTilesPositions.find(id); diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index 4351c86bb..557cde1be 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -3,6 +3,8 @@ #include "cachedrecastmeshmanager.hpp" #include "tileposition.hpp" +#include "settingsutils.hpp" +#include "gettilespositions.hpp" #include @@ -20,8 +22,52 @@ namespace DetourNavigator bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType); - std::vector updateObject(const ObjectId id, const btCollisionShape& shape, - const btTransform& transform, const AreaType areaType); + template + bool updateObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, + const AreaType areaType, OnChangedTile&& onChangedTile) + { + const auto object = mObjectsTilesPositions.find(id); + if (object == mObjectsTilesPositions.end()) + return false; + auto& currentTiles = object->second; + const auto border = getBorderSize(mSettings); + bool changed = false; + std::set newTiles; + { + auto tiles = mTiles.lock(); + const auto onTilePosition = [&] (const TilePosition& tilePosition) + { + if (currentTiles.count(tilePosition)) + { + newTiles.insert(tilePosition); + if (updateTile(id, transform, areaType, tilePosition, tiles.get())) + { + onChangedTile(tilePosition); + changed = true; + } + } + else if (addTile(id, shape, transform, areaType, tilePosition, border, tiles.get())) + { + newTiles.insert(tilePosition); + onChangedTile(tilePosition); + changed = true; + } + }; + getTilesPositions(shape, transform, mSettings, onTilePosition); + for (const auto& tile : currentTiles) + { + if (!newTiles.count(tile) && removeTile(id, tile, tiles.get())) + { + onChangedTile(tile); + changed = true; + } + } + } + std::swap(currentTiles, newTiles); + if (changed) + ++mRevision; + return changed; + } boost::optional removeObject(const ObjectId id); From 4838cf7362ddf01c3302c1a30291d48a86ef1014 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Apr 2020 23:56:05 +0300 Subject: [PATCH 118/226] Clean up spellcasting --- apps/openmw/mwmechanics/spellcasting.cpp | 218 +++++++++-------------- apps/openmw/mwmechanics/spellcasting.hpp | 9 +- 2 files changed, 84 insertions(+), 143 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 3f32485de..d7645a698 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -32,41 +32,23 @@ namespace MWMechanics { ESM::Skill::SkillEnum spellSchoolToSkill(int school) { - static std::map schoolSkillMap; // maps spell school to skill id - if (schoolSkillMap.empty()) + static const std::map schoolSkillMap { - schoolSkillMap[0] = ESM::Skill::Alteration; - schoolSkillMap[1] = ESM::Skill::Conjuration; - schoolSkillMap[3] = ESM::Skill::Illusion; - schoolSkillMap[2] = ESM::Skill::Destruction; - schoolSkillMap[4] = ESM::Skill::Mysticism; - schoolSkillMap[5] = ESM::Skill::Restoration; - } - - assert(schoolSkillMap.find(school) != schoolSkillMap.end()); - return schoolSkillMap[school]; - } - - float calcEffectCost(const ESM::ENAMstruct& effect) - { - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); - return calcEffectCost(effect, magicEffect); + {0, ESM::Skill::Alteration}, {1, ESM::Skill::Conjuration}, {2, ESM::Skill::Destruction}, + {3, ESM::Skill::Illusion}, {4, ESM::Skill::Mysticism}, {5, ESM::Skill::Restoration} + }; + return schoolSkillMap.at(school); } float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect) { - int minMagn = 1; - int maxMagn = 1; - if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) - { - minMagn = effect.mMagnMin; - maxMagn = effect.mMagnMax; - } - - int duration = 1; - if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) - duration = effect.mDuration; - + 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(); @@ -84,19 +66,18 @@ namespace MWMechanics float y = std::numeric_limits::max(); float lowestSkill = 0; - for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it) + for (const ESM::ENAMstruct& effect : spell->mEffects.mList) { - float x = static_cast(it->mDuration); - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find( - it->mEffectID); + 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 * (it->mMagnMin + it->mMagnMax); - x *= it->mArea * 0.05f * magicEffect->mData.mBaseCost; - if (it->mRange == ESM::RT_Target) + 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(); @@ -149,24 +130,18 @@ namespace MWMechanics return 100; if (godmode) - { return 100; - } - if (!cap) - return std::max(0.f, castChance); - else - return std::max(0.f, std::min(100.f, castChance)); + 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) { - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - return getSpellSuccessChance(spell, actor, effectiveSchool, cap, 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; @@ -183,16 +158,13 @@ namespace MWMechanics bool spellIncreasesSkill(const ESM::Spell *spell) { - if (spell->mData.mType == ESM::Spell::ST_Spell && !(spell->mData.mFlags & ESM::Spell::F_Always)) - return true; - return false; + return spell->mData.mType == ESM::Spell::ST_Spell && !(spell->mData.mFlags & ESM::Spell::F_Always); } bool spellIncreasesSkill(const std::string &spellId) { - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - return spellIncreasesSkill(spell); + const auto spell = MWBase::Environment::get().getWorld()->getStore().get().search(spellId); + return spell && spellIncreasesSkill(spell); } float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects) @@ -314,10 +286,9 @@ namespace MWMechanics class GetAbsorptionProbability : public MWMechanics::EffectSourceVisitor { public: - float mProbability; + float mProbability{0.f}; - GetAbsorptionProbability(const MWWorld::Ptr& actor) - : mProbability(0.f){} + GetAbsorptionProbability() = default; virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, const std::string& sourceId, int casterActorId, @@ -342,9 +313,6 @@ namespace MWMechanics CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile, const bool manualSpell) : mCaster(caster) , mTarget(target) - , mStack(false) - , mHitPosition(0,0,0) - , mAlwaysSucceed(false) , mFromProjectile(fromProjectile) , mManualSpell(manualSpell) { @@ -375,10 +343,9 @@ namespace MWMechanics // If none of the effects need to apply, we can early-out bool found = false; - for (std::vector::const_iterator iter (effects.mList.begin()); - iter!=effects.mList.end(); ++iter) + for (const ESM::ENAMstruct& effect : effects.mList) { - if (iter->mRange == range) + if (effect.mRange == range) { found = true; break; @@ -441,8 +408,7 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCannotRecast}"); continue; } - else - canCastAnEffect = true; + canCastAnEffect = true; if (!checkEffectTarget(effectIt->mEffectID, target, caster, castByPlayer)) continue; @@ -466,7 +432,7 @@ namespace MWMechanics CreatureStats& stats = target.getClass().getCreatureStats(target); if (stats.getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude() > 0.f) { - GetAbsorptionProbability check(target); + GetAbsorptionProbability check; stats.getActiveSpells().visitEffectSources(check); stats.getSpells().visitEffectSources(check); if (target.getClass().hasInventoryStore(target)) @@ -578,11 +544,7 @@ namespace MWMechanics } else { - - if (!hasDuration) - effect.mDuration = 1.0f; - else - effect.mDuration = static_cast(effectIt->mDuration); + effect.mDuration = hasDuration ? static_cast(effectIt->mDuration) : 1.f; targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude)); @@ -658,13 +620,11 @@ namespace MWMechanics else castStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_DefaultHit"); - std::string texture = magicEffect->mParticle; - bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0; // Note: in case of non actor, a free effect should be fine as well MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); - if (anim) - anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "", texture); + if (anim && !castStatic->mModel.empty()) + anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "", magicEffect->mParticle); } } } @@ -985,7 +945,7 @@ namespace MWMechanics // A non-actor doesn't play its spell cast effects from a character controller, so play them here if (!mCaster.getClass().isActor()) - playSpellCastingEffects(mId, false); + playSpellCastingEffects(spell->mEffects.mList); inflict(mCaster, mCaster, spell->mEffects, ESM::RT_Self); @@ -1065,14 +1025,13 @@ namespace MWMechanics const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); if (enchantment) { - const ESM::Enchantment *spell = store.get().find(spellid); - playSpellCastingEffects(spell->mEffects.mList); - + if (const auto spell = store.get().search(spellid)) + playSpellCastingEffects(spell->mEffects.mList); } else { - const ESM::Spell *spell = store.get().find(spellid); - playSpellCastingEffects(spell->mEffects.mList); + if (const auto spell = store.get().search(spellid)) + playSpellCastingEffects(spell->mEffects.mList); } } @@ -1080,12 +1039,9 @@ namespace MWMechanics { const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); std::vector addedEffects; - for (std::vector::const_iterator iter = effects.begin(); iter != effects.end(); ++iter) + for (const ESM::ENAMstruct& effectData : effects) { - const ESM::MagicEffect *effect; - effect = store.get().find(iter->mEffectID); - - MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster); + const auto effect = store.get().find(effectData.mEffectID); const ESM::Static* castStatic; @@ -1098,13 +1054,10 @@ namespace MWMechanics if (std::find(addedEffects.begin(), addedEffects.end(), "meshes\\" + castStatic->mModel) != addedEffects.end()) continue; - std::string texture = effect->mParticle; - - osg::Vec3f pos (mCaster.getRefData().getPosition().asVec3()); - + MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster); if (animation) { - animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture); + animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", effect->mParticle); } else { @@ -1113,6 +1066,7 @@ namespace MWMechanics osg::Vec3f bounds (MWBase::Environment::get().getWorld()->getHalfExtents(mCaster) * 2.f / Constants::UnitsPerFoot); float scale = std::max({ bounds.x()/3.f, bounds.y()/3.f, bounds.z()/6.f }); float meshScale = !mCaster.getClass().isActor() ? mCaster.getCellRef().getScale() : 1.0f; + osg::Vec3f pos (mCaster.getRefData().getPosition().asVec3()); MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + castStatic->mModel, effect->mParticle, pos, scale * meshScale); } @@ -1135,10 +1089,7 @@ namespace MWMechanics bool CastSpell::spellIncreasesSkill() { - if (mManualSpell) - return false; - - return MWMechanics::spellIncreasesSkill(mId); + return !mManualSpell && MWMechanics::spellIncreasesSkill(mId); } int getEffectiveEnchantmentCastCost(float castCost, const MWWorld::Ptr &actor) @@ -1167,8 +1118,7 @@ namespace MWMechanics if (ptr.getClass().hasInventoryStore(ptr)) { MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); - MWWorld::ContainerStoreIterator item = - inv.getSlot(slot); + MWWorld::ContainerStoreIterator item = inv.getSlot(slot); if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor || item.getType() == MWWorld::ContainerStore::Type_Weapon)) { @@ -1183,9 +1133,7 @@ namespace MWMechanics item->getCellRef().applyChargeRemainderToBeSubtracted(disintegrate - std::floor(disintegrate)); charge = item->getClass().getItemHealth(*item); - charge -= - std::min(static_cast(disintegrate), - charge); + charge -= std::min(static_cast(disintegrate), charge); item->getCellRef().setCharge(charge); if (charge == 0) @@ -1366,58 +1314,52 @@ namespace MWMechanics std::string getSummonedCreature(int effectId) { - static std::map summonMap; - if (summonMap.empty()) + static const std::map summonMap { - summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID"; - summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID"; - summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID"; - summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID"; - summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID"; - summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID"; - summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID"; - summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID"; - summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID"; - summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID"; - summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID"; - summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID"; - summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID"; - summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID"; - summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID"; - summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID"; - summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID"; - summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID"; - summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID"; - summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID"; - summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID"; - summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID"; - } - - std::map::const_iterator it = summonMap.find(effectId); - if (it == summonMap.end()) - return std::string(); - else + {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*/) { - const ESM::MagicEffect *magicEffect = - MWBase::Environment::get().getWorld()->getStore().get().find(key.mId); - + 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"); - - std::string texture = magicEffect->mParticle; - - bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0; MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mActor); - if (anim && loop) - anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "", texture); + 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 5f0e4a1cf..85e732e03 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -25,8 +25,7 @@ namespace MWMechanics ESM::Skill::SkillEnum spellSchoolToSkill(int school); - float calcEffectCost(const ESM::ENAMstruct& effect); - float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect); + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect = nullptr); bool isSummoningEffect(int effectId); @@ -87,11 +86,11 @@ namespace MWMechanics void playSpellCastingEffects(const std::vector& effects); public: - bool mStack; + bool mStack{false}; std::string mId; // ID of spell, potion, item etc std::string mSourceName; // Display name for spell, potion, etc - osg::Vec3f mHitPosition; // Used for spawning area orb - bool mAlwaysSucceed; // Always succeed spells casted by NPCs/creatures regardless of their chance (default: false) + osg::Vec3f mHitPosition{0,0,0}; // Used for spawning area orb + bool mAlwaysSucceed{false}; // Always succeed spells casted by NPCs/creatures regardless of their chance (default: false) bool mFromProjectile; // True if spell is cast by enchantment of some projectile (arrow, bolt or thrown weapon) bool mManualSpell; // True if spell is casted from script and ignores some checks (mana level, success chance, etc.) From 916a9641fc16504189f82fde0fac7abfcf935403 Mon Sep 17 00:00:00 2001 From: fredzio Date: Sun, 29 Sep 2019 19:10:54 +0200 Subject: [PATCH 119/226] Delete support for 2-arguments opcodes - it was never used --- components/interpreter/interpreter.cpp | 52 -------------------------- components/interpreter/interpreter.hpp | 9 ----- components/interpreter/opcodes.hpp | 10 ----- 3 files changed, 71 deletions(-) diff --git a/components/interpreter/interpreter.cpp b/components/interpreter/interpreter.cpp index b64369e70..0b636092c 100644 --- a/components/interpreter/interpreter.cpp +++ b/components/interpreter/interpreter.cpp @@ -28,22 +28,6 @@ namespace Interpreter return; } - case 1: - { - int opcode = (code>>24) & 0x3f; - unsigned int arg0 = (code>>16) & 0xfff; - unsigned int arg1 = code & 0xfff; - - std::map::iterator iter = mSegment1.find (opcode); - - if (iter==mSegment1.end()) - abortUnknownCode (1, opcode); - - iter->second->execute (mRuntime, arg0, arg1); - - return; - } - case 2: { int opcode = (code>>20) & 0x3ff; @@ -79,22 +63,6 @@ namespace Interpreter return; } - case 0x31: - { - int opcode = (code>>16) & 0x3ff; - unsigned int arg0 = (code>>8) & 0xff; - unsigned int arg1 = code & 0xff; - - std::map::iterator iter = mSegment4.find (opcode); - - if (iter==mSegment4.end()) - abortUnknownCode (4, opcode); - - iter->second->execute (mRuntime, arg0, arg1); - - return; - } - case 0x32: { int opcode = code & 0x3ffffff; @@ -161,10 +129,6 @@ namespace Interpreter iter!=mSegment0.end(); ++iter) delete iter->second; - for (std::map::iterator iter (mSegment1.begin()); - iter!=mSegment1.end(); ++iter) - delete iter->second; - for (std::map::iterator iter (mSegment2.begin()); iter!=mSegment2.end(); ++iter) delete iter->second; @@ -173,10 +137,6 @@ namespace Interpreter iter!=mSegment3.end(); ++iter) delete iter->second; - for (std::map::iterator iter (mSegment4.begin()); - iter!=mSegment4.end(); ++iter) - delete iter->second; - for (std::map::iterator iter (mSegment5.begin()); iter!=mSegment5.end(); ++iter) delete iter->second; @@ -188,12 +148,6 @@ namespace Interpreter mSegment0.insert (std::make_pair (code, opcode)); } - void Interpreter::installSegment1 (int code, Opcode2 *opcode) - { - assert(mSegment1.find(code) == mSegment1.end()); - mSegment1.insert (std::make_pair (code, opcode)); - } - void Interpreter::installSegment2 (int code, Opcode1 *opcode) { assert(mSegment2.find(code) == mSegment2.end()); @@ -206,12 +160,6 @@ namespace Interpreter mSegment3.insert (std::make_pair (code, opcode)); } - void Interpreter::installSegment4 (int code, Opcode2 *opcode) - { - assert(mSegment4.find(code) == mSegment4.end()); - mSegment4.insert (std::make_pair (code, opcode)); - } - void Interpreter::installSegment5 (int code, Opcode0 *opcode) { assert(mSegment5.find(code) == mSegment5.end()); diff --git a/components/interpreter/interpreter.hpp b/components/interpreter/interpreter.hpp index dd341e709..ff3bcf7b7 100644 --- a/components/interpreter/interpreter.hpp +++ b/components/interpreter/interpreter.hpp @@ -11,7 +11,6 @@ namespace Interpreter { class Opcode0; class Opcode1; - class Opcode2; class Interpreter { @@ -19,10 +18,8 @@ namespace Interpreter bool mRunning; Runtime mRuntime; std::map mSegment0; - std::map mSegment1; std::map mSegment2; std::map mSegment3; - std::map mSegment4; std::map mSegment5; // not implemented @@ -48,18 +45,12 @@ namespace Interpreter void installSegment0 (int code, Opcode1 *opcode); ///< ownership of \a opcode is transferred to *this. - void installSegment1 (int code, Opcode2 *opcode); - ///< ownership of \a opcode is transferred to *this. - void installSegment2 (int code, Opcode1 *opcode); ///< ownership of \a opcode is transferred to *this. void installSegment3 (int code, Opcode1 *opcode); ///< ownership of \a opcode is transferred to *this. - void installSegment4 (int code, Opcode2 *opcode); - ///< ownership of \a opcode is transferred to *this. - void installSegment5 (int code, Opcode0 *opcode); ///< ownership of \a opcode is transferred to *this. diff --git a/components/interpreter/opcodes.hpp b/components/interpreter/opcodes.hpp index c447e1f10..da266e8ff 100644 --- a/components/interpreter/opcodes.hpp +++ b/components/interpreter/opcodes.hpp @@ -25,16 +25,6 @@ namespace Interpreter virtual ~Opcode1() {} }; - /// opcode for 2 arguments - class Opcode2 - { - public: - - virtual void execute (Runtime& runtime, unsigned int arg1, unsigned int arg2) = 0; - - virtual ~Opcode2() {} - }; - } #endif From a3cd3281fb76214b629f95fc4eb75c3a21329042 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Apr 2020 16:17:06 +0300 Subject: [PATCH 120/226] Use an array instead of a map in spellSchoolToSkill() --- apps/openmw/mwmechanics/spellcasting.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d7645a698..ed8972f05 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -32,12 +32,12 @@ namespace MWMechanics { ESM::Skill::SkillEnum spellSchoolToSkill(int school) { - static const std::map schoolSkillMap + static const std::array schoolSkillArray { - {0, ESM::Skill::Alteration}, {1, ESM::Skill::Conjuration}, {2, ESM::Skill::Destruction}, - {3, ESM::Skill::Illusion}, {4, ESM::Skill::Mysticism}, {5, ESM::Skill::Restoration} + ESM::Skill::Alteration, ESM::Skill::Conjuration, ESM::Skill::Destruction, + ESM::Skill::Illusion, ESM::Skill::Mysticism, ESM::Skill::Restoration }; - return schoolSkillMap.at(school); + return schoolSkillArray.at(school); } float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect) From 5535a7fb44956a7644d45b3ced4bf4ac186b04a5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 4 Apr 2020 16:27:00 +0300 Subject: [PATCH 121/226] 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 122/226] 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 123/226] 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 124/226] 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 125/226] 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 126/226] 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 6d3f9ce3072cf789165a5f28b073129937f47815 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 26 Apr 2020 22:20:57 +0200 Subject: [PATCH 127/226] puts an end of error spam when OSG is copmiled without Freetype support --- components/resource/stats.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 51497cd27..1d78d7a4c 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -8,6 +8,8 @@ #include +#include + #include #include @@ -32,12 +34,14 @@ StatsHandler::StatsHandler(): _resourceStatsChildNum = 0; - _font = osgMyGUI::DataManager::getInstance().getDataPath("DejaVuLGCSansMono.ttf"); + if (osgDB::Registry::instance()->getReaderWriterForExtension("ttf")) + _font = osgMyGUI::DataManager::getInstance().getDataPath("DejaVuLGCSansMono.ttf"); } Profiler::Profiler() { - _font = osgMyGUI::DataManager::getInstance().getDataPath("DejaVuLGCSansMono.ttf"); + if (osgDB::Registry::instance()->getReaderWriterForExtension("ttf")) + _font = osgMyGUI::DataManager::getInstance().getDataPath("DejaVuLGCSansMono.ttf"); setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3); } From bbd15cccd5c76c9245134c3a71553b400ce569ca Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 27 Apr 2020 00:21:34 +0300 Subject: [PATCH 128/226] Fix hidden node with NiVisController optimization --- components/nifosg/nifloader.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 7d62d1ef1..8b6d69f2d 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -591,7 +591,13 @@ namespace NifOsg { bool hasVisController = false; for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) - hasVisController = (ctrl->recType == Nif::RC_NiVisController); + { + if (ctrl->recType == Nif::RC_NiVisController) + { + hasVisController = true; + break; + } + } if (!hasVisController) skipMeshes = true; // skip child meshes, but still create the child node hierarchy for animating collision shapes From 1870b4b34586a052ee70b41a771d89c174558c31 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 27 Apr 2020 08:02:42 +0200 Subject: [PATCH 129/226] catch and set with no _found; leave empty as empty string --- components/resource/stats.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 1d78d7a4c..bc63ddf58 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -42,6 +42,8 @@ Profiler::Profiler() { if (osgDB::Registry::instance()->getReaderWriterForExtension("ttf")) _font = osgMyGUI::DataManager::getInstance().getDataPath("DejaVuLGCSansMono.ttf"); + else + _font = ""; setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3); } From dc33eeadf1b3e58ef76f85d339bbfcb13d1862dd Mon Sep 17 00:00:00 2001 From: bzzt Date: Fri, 21 Jun 2019 13:37:00 +0000 Subject: [PATCH 130/226] tightscenebound is uncessary after water bbfix --- apps/openmw/mwgui/loadingscreen.cpp | 10 ----- apps/openmw/mwgui/windowmanagerimp.cpp | 38 ++++++++++++++----- apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++ apps/openmw/mwrender/renderingmanager.cpp | 8 ++-- components/sceneutil/shadow.cpp | 3 ++ components/sceneutil/waterutil.cpp | 8 ++++ .../reference/modding/settings/shadows.rst | 3 +- files/settings-default.cfg | 4 +- 8 files changed, 49 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 093a2f34c..dcfe723f7 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -334,12 +334,6 @@ namespace MWGui setupCopyFramebufferToTextureCallback(); } - // Turn off rendering except the GUI - int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); - int oldCullMask = mViewer->getCamera()->getCullMask(); - mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI|MWRender::Mask_PreCompile); - mViewer->getCamera()->setCullMask(MWRender::Mask_GUI|MWRender::Mask_PreCompile); - MWBase::Environment::get().getInputManager()->update(0, true, true); //osg::Timer timer; @@ -355,10 +349,6 @@ namespace MWGui //if (mViewer->getIncrementalCompileOperation()) //std::cout << "num to compile " << mViewer->getIncrementalCompileOperation()->getToCompile().size() << std::endl; - // resume 3d rendering - mViewer->getUpdateVisitor()->setTraversalMask(oldUpdateMask); - mViewer->getCamera()->setCullMask(oldCullMask); - mLastRenderTime = mTimer.time_m(); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index d10270d77..28521ac6f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -130,7 +130,9 @@ namespace MWGui 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) - : mStore(nullptr) + : mOldUpdateMask(0) + , mOldCullMask(0) + , mStore(nullptr) , mResourceSystem(resourceSystem) , mWorkQueue(workQueue) , mViewer(viewer) @@ -676,13 +678,34 @@ namespace MWGui } } + void WindowManager::enableScene(bool enable) + { + unsigned int disablemask = MWRender::Mask_GUI|MWRender::Mask_PreCompile; + if (!enable && mViewer->getCamera()->getCullMask() != disablemask) + { + mOldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); + mOldCullMask = mViewer->getCamera()->getCullMask(); + mViewer->getUpdateVisitor()->setTraversalMask(disablemask); + mViewer->getCamera()->setCullMask(disablemask); + } + else if (enable && mViewer->getCamera()->getCullMask() == disablemask) + { + mViewer->getUpdateVisitor()->setTraversalMask(mOldUpdateMask); + mViewer->getCamera()->setCullMask(mOldCullMask); + } + } + void WindowManager::updateVisible() { + bool loading = (getMode() == GM_Loading || getMode() == GM_LoadingWallpaper); + + bool mainmenucover = containsMode(GM_MainMenu) && MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame; + + enableScene(!loading && !mainmenucover); + if (!mMap) return; // UI not created yet - bool loading = (getMode() == GM_Loading || getMode() == GM_LoadingWallpaper); - mHud->setVisible(mHudEnabled && !loading); mToolTips->setVisible(mHudEnabled && !loading); @@ -1876,11 +1899,7 @@ namespace MWGui mVideoBackground->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed); } - // Turn off all rendering except for the GUI - int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); - int oldCullMask = mViewer->getCamera()->getCullMask(); - mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI); - mViewer->getCamera()->setCullMask(MWRender::Mask_GUI); + enableScene(false); MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize(); sizeVideo(screenSize.width, screenSize.height); @@ -1936,8 +1955,7 @@ namespace MWGui setCursorVisible(cursorWasVisible); // Restore normal rendering - mViewer->getUpdateVisitor()->setTraversalMask(oldUpdateMask); - mViewer->getCamera()->setCullMask(oldCullMask); + updateVisible(); mVideoBackground->setVisible(false); } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 0b4307ec4..4e90f2e93 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -390,6 +390,8 @@ namespace MWGui virtual bool injectKeyRelease(MyGUI::KeyCode key); private: + unsigned int mOldUpdateMask; unsigned int mOldCullMask; + const MWWorld::ESMStore* mStore; Resource::ResourceSystem* mResourceSystem; osg::ref_ptr mWorkQueue; @@ -560,6 +562,8 @@ namespace MWGui void setMenuTransparency(float value); void updatePinnedWindows(); + + void enableScene(bool enable); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c966a5d67..e631a8fd8 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -235,6 +235,8 @@ namespace MWRender sceneRoot->setLightingMask(Mask_Lighting); mSceneRoot = sceneRoot; sceneRoot->setStartLight(1); + sceneRoot->setNodeMask(Mask_Scene); + sceneRoot->setName("Scene Root"); int shadowCastingTraversalMask = Mask_Scene; if (Settings::Manager::getBool("actor shadows", "Shadows")) @@ -347,9 +349,6 @@ namespace MWRender defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat); - sceneRoot->setNodeMask(Mask_Scene); - sceneRoot->setName("Scene Root"); - mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); mSky->setCamera(mViewer->getCamera()); @@ -768,11 +767,10 @@ namespace MWRender void waitTillDone() { - mMutex.lock(); + OpenThreads::ScopedLock lock(mMutex); if (mDone) return; mCondition.wait(&mMutex); - mMutex.unlock(); } mutable OpenThreads::Condition mCondition; diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index 6b88adaab..a1cd1d660 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -40,6 +40,8 @@ namespace SceneUtil mShadowSettings->setMinimumShadowMapNearFarRatio(Settings::Manager::getFloat("minimum lispsm near far ratio", "Shadows")); if (Settings::Manager::getBool("compute tight scene bounds", "Shadows")) mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES); + else + mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); int mapres = Settings::Manager::getInt("shadow map resolution", "Shadows"); mShadowSettings->setTextureSize(osg::Vec2s(mapres, mapres)); @@ -95,6 +97,7 @@ namespace SceneUtil mShadowedScene->addChild(sceneRoot); rootNode->addChild(mShadowedScene); + mShadowedScene->setNodeMask(sceneRoot->getNodeMask()); mShadowSettings = mShadowedScene->getShadowSettings(); setupShadowSettings(); diff --git a/components/sceneutil/waterutil.cpp b/components/sceneutil/waterutil.cpp index 562b0ee73..20a50a930 100644 --- a/components/sceneutil/waterutil.cpp +++ b/components/sceneutil/waterutil.cpp @@ -7,6 +7,12 @@ namespace SceneUtil { + // disable nonsense test against a worldsize bb what will always pass + class WaterBoundCallback : public osg::Drawable::ComputeBoundingBoxCallback + { + virtual osg::BoundingBox computeBound(const osg::Drawable&) const { return osg::BoundingBox(); } + }; + osg::ref_ptr createWaterGeometry(float size, int segments, float textureRepeats) { osg::ref_ptr verts (new osg::Vec3Array); @@ -51,6 +57,8 @@ namespace SceneUtil waterGeom->setNormalArray(normal, osg::Array::BIND_OVERALL); waterGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,verts->size())); + waterGeom->setComputeBoundingBoxCallback(new WaterBoundCallback); + waterGeom->setCullingActive(false); return waterGeom; } diff --git a/docs/source/reference/modding/settings/shadows.rst b/docs/source/reference/modding/settings/shadows.rst index 5854b6a91..638349ee6 100644 --- a/docs/source/reference/modding/settings/shadows.rst +++ b/docs/source/reference/modding/settings/shadows.rst @@ -85,10 +85,9 @@ compute tight scene bounds :Type: boolean :Range: True/False -:Default: True +:Default: False With this setting enabled, attempt to better use the shadow map(s) by making them cover a smaller area. -This can be especially helpful when looking downwards with a high viewing distance but will be less useful with the default value. May have a minor to major performance impact. shadow map resolution diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 06950e50d..056ee4a1e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -805,8 +805,8 @@ enable debug hud = false # Enable the debug overlay to see where each shadow map affects. enable debug overlay = false -# Attempt to better use the shadow map by making them cover a smaller area. Especially helpful when looking downwards. 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 minor to 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 3ba77b933a8aef090457e6f1bca1b1ac0efb7f43 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 27 Apr 2020 08:45:52 +0200 Subject: [PATCH 131/226] "compute tight scene bounds" reset back to true until it is decided how best to handle this in settings --- docs/source/reference/modding/settings/shadows.rst | 2 +- files/settings-default.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/reference/modding/settings/shadows.rst b/docs/source/reference/modding/settings/shadows.rst index 638349ee6..d0d92a6e2 100644 --- a/docs/source/reference/modding/settings/shadows.rst +++ b/docs/source/reference/modding/settings/shadows.rst @@ -85,7 +85,7 @@ compute tight scene bounds :Type: boolean :Range: True/False -:Default: False +:Default: True With this setting enabled, attempt to better use the shadow map(s) by making them cover a smaller area. May have a minor to major performance impact. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 056ee4a1e..6703e7732 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -806,7 +806,7 @@ enable debug hud = false 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 = false +compute tight scene bounds = true # 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 4c1c30db3350472d024e4391a9f85421985f82de Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 27 Apr 2020 12:03:20 +0300 Subject: [PATCH 132/226] 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 11:40:49 +0200 Subject: [PATCH 133/226] Something changed in OSG 3.6 that makes the command "showscenegraph" less useful; it writes out raw data of images to the debug output file openmw.ogst. This commit adds the hint and restores default behaviour found in OSG 3.4 --- components/sceneutil/writescene.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/sceneutil/writescene.cpp b/components/sceneutil/writescene.cpp index 19e8dd73b..6be963ef2 100644 --- a/components/sceneutil/writescene.cpp +++ b/components/sceneutil/writescene.cpp @@ -21,6 +21,7 @@ void SceneUtil::writeScene(osg::Node *node, const std::string& filename, const s osg::ref_ptr options = new osgDB::Options; options->setPluginStringData("fileType", format); + options->setPluginStringData("WriteImageHint", "UseExternal"); rw->writeNode(*node, stream, options); } From e7f91ff341358e4a8bb21992601f92b4d4b75cd5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 27 Apr 2020 12:43:34 +0300 Subject: [PATCH 134/226] 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 ca0adc25bb63bcdf100c18bc960cab0d0daf4ce3 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 27 Apr 2020 11:51:18 +0200 Subject: [PATCH 135/226] add two additional classes we do not need to serialize; less pam during `showscenegraph` debug dump. --- components/sceneutil/serialize.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/sceneutil/serialize.cpp b/components/sceneutil/serialize.cpp index ab0321532..8a2f9e816 100644 --- a/components/sceneutil/serialize.cpp +++ b/components/sceneutil/serialize.cpp @@ -115,6 +115,8 @@ void registerSerializers() "SceneUtil::UpdateRigGeometry", "SceneUtil::LightSource", "SceneUtil::StateSetUpdater", + "SceneUtil::DisableLight", + "SceneUtil::MWShadowTechnique", "NifOsg::NodeUserData", "NifOsg::FlipController", "NifOsg::KeyframeController", From 31a75a962aa51793a409c8764615c8814386a405 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 27 Apr 2020 11:56:10 +0200 Subject: [PATCH 136/226] Add 3 additional classes to be ignored who dumping "showscenegraph" debug output; less spammy --- components/sceneutil/serialize.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/sceneutil/serialize.cpp b/components/sceneutil/serialize.cpp index 8a2f9e816..f84a19876 100644 --- a/components/sceneutil/serialize.cpp +++ b/components/sceneutil/serialize.cpp @@ -109,6 +109,7 @@ void registerSerializers() const char* ignore[] = { "MWRender::PtrHolder", "Resource::TemplateRef", + "SceneUtil::CompositeStateSetUpdater", "SceneUtil::LightListCallback", "SceneUtil::LightManagerUpdateCallback", "SceneUtil::UpdateRigBounds", @@ -122,7 +123,9 @@ void registerSerializers() "NifOsg::KeyframeController", "NifOsg::TextKeyMapHolder", "NifOsg::Emitter", + "NifOsg::ParticleColorAffector", "NifOsg::ParticleSystem", + "NifOsg::GravityAffector", "NifOsg::GrowFadeAffector", "NifOsg::InverseWorldMatrix", "NifOsg::StaticBoundingBoxCallback", From 69cd53ef8abd370cf244d8deb305dc7775e100e5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 27 Apr 2020 14:06:50 +0300 Subject: [PATCH 137/226] 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 138/226] 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 3b7fb9ec094c9aefbfa3baa540e1b9bf76d4aa99 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 28 Apr 2020 10:00:46 +0300 Subject: [PATCH 139/226] Use pipe-equal operator --- components/nifosg/nifloader.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 8b6d69f2d..e77e6b34f 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -592,11 +592,8 @@ namespace NifOsg bool hasVisController = false; for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { - if (ctrl->recType == Nif::RC_NiVisController) - { - hasVisController = true; + if (hasVisController |= (ctrl->recType == Nif::RC_NiVisController)) break; - } } if (!hasVisController) From d77047e1cfe00092d2efb54893f766288a950f99 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 28 Apr 2020 13:16:37 +0200 Subject: [PATCH 140/226] tab2space --- components/resource/stats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index bc63ddf58..59d65e889 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -43,7 +43,7 @@ Profiler::Profiler() if (osgDB::Registry::instance()->getReaderWriterForExtension("ttf")) _font = osgMyGUI::DataManager::getInstance().getDataPath("DejaVuLGCSansMono.ttf"); else - _font = ""; + _font = ""; setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3); } From c08f9e13af2a9b5319eaab074465e7733bcad50d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 28 Apr 2020 23:53:00 +0300 Subject: [PATCH 141/226] 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 142/226] 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 143/226] 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 144/226] 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 145/226] 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 146/226] 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 147/226] 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 148/226] 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 149/226] 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 150/226] 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 151/226] 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 152/226] 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 153/226] 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 154/226] 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 155/226] 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 156/226] 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 157/226] 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 158/226] 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 159/226] 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 160/226] 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 161/226] 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 162/226] 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 163/226] 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 164/226] 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 165/226] 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 166/226] 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 167/226] 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 168/226] 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 169/226] 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 170/226] 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 171/226] 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 172/226] 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 173/226] 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 174/226] 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 175/226] 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 176/226] 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 177/226] 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 178/226] 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 179/226] 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 180/226] 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 181/226] 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 182/226] 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 183/226] 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 184/226] 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 185/226] 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 186/226] 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 187/226] 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 188/226] 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 189/226] 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 190/226] 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 191/226] 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 192/226] 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 193/226] 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 194/226] 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 195/226] 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 196/226] 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 197/226] 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 198/226] 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 199/226] 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 200/226] 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 201/226] 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 202/226] 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 203/226] 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 204/226] 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 205/226] 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 206/226] 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 207/226] 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 915ffe224169f76355ed0cf547f2ee193e006d4f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 14 May 2020 00:48:28 +0300 Subject: [PATCH 208/226] 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 209/226] 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 210/226] 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 211/226] 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 212/226] 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 213/226] 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 214/226] 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 215/226] 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 216/226] 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 71350c6dff5aa7b1f6ca2ef07530001e497f9680 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 15:52:33 +0200 Subject: [PATCH 217/226] 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 218/226] 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 219/226] 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 220/226] 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 221/226] 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 222/226] 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 223/226] 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 224/226] 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 225/226] 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 226/226] 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. "