From b61249841eaaaa2f193c598ad9b59116fb9e056d Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Wed, 19 May 2021 18:38:42 +0200 Subject: [PATCH 01/20] Update lookup only after sorting files --- components/bsa/bsa_file.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 3375882f5..4dfaa8658 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -182,8 +182,6 @@ void BSAFile::readHeader() if(fs.offset + fs.fileSize > fsize) fail("Archive contains offsets outside itself"); - // Add the file name to the lookup - mLookup[fs.name()] = i; } mStringBuf.resize(endOfNameBuffer); @@ -191,6 +189,13 @@ void BSAFile::readHeader() return left.offset < right.offset; }); + for (size_t i = 0; i < filenum; i++) + { + FileStruct& fs = mFiles[i]; + // Add the file name to the lookup + mLookup[fs.name()] = i; + } + mIsLoaded = true; } From 86d137363e86a087287305bfc3f8a281ee2645ef Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Thu, 20 May 2021 21:28:52 +0200 Subject: [PATCH 02/20] Check if the archive is opened before adding a file to it and clear state on close --- components/bsa/bsa_file.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 4dfaa8658..129c2bf45 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -252,6 +252,9 @@ int BSAFile::getIndex(const char *str) const /// Open an archive file. void BSAFile::open(const std::string &file) { + if (mIsLoaded) + close(); + mFilename = file; if(boost::filesystem::exists(file)) readHeader(); @@ -259,16 +262,20 @@ void BSAFile::open(const std::string &file) { { boost::filesystem::fstream(mFilename, std::ios::binary | std::ios::out); } writeHeader(); + mIsLoaded = true; } } /// Close the archive, write the updated headers to the file void Bsa::BSAFile::close() { - if (!mHasChanged) - return; + if (mHasChanged) + writeHeader(); - writeHeader(); + mFiles.clear(); + mStringBuf.clear(); + mLookup.clear(); + mIsLoaded = false; } Files::IStreamPtr BSAFile::getFile(const char *file) @@ -290,6 +297,8 @@ Files::IStreamPtr BSAFile::getFile(const FileStruct *file) void Bsa::BSAFile::addFile(const std::string& filename, std::istream& file) { + if (!mIsLoaded) + fail("Unable to add file " + filename + " the archive is not opened"); namespace bfs = boost::filesystem; auto newStartOfDataBuffer = 12 + (12 + 8) * (mFiles.size() + 1) + mStringBuf.size() + filename.size() + 1; From e51669c05daff9fce3c6af91d314efbdc6cec6eb Mon Sep 17 00:00:00 2001 From: jvoisin Date: Fri, 21 May 2021 18:15:31 +0200 Subject: [PATCH 03/20] Don't assign a variable passed by value --- components/misc/stringops.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index aa2ae105e..48deaa999 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -45,19 +45,19 @@ public: { // Russian alphabet if (ch >= 0x0410 && ch < 0x0430) - return ch += 0x20; + return ch + 0x20; // Cyrillic IO character if (ch == 0x0401) - return ch += 0x50; + return ch + 0x50; // Latin alphabet if (ch >= 0x41 && ch < 0x60) - return ch += 0x20; + return ch + 0x20; // Deutch characters if (ch == 0xc4 || ch == 0xd6 || ch == 0xdc) - return ch += 0x20; + return ch + 0x20; if (ch == 0x1e9e) return 0xdf; From 105cd5b06fe92160b4e59e63f8e88c59adf016b2 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 21 May 2021 19:30:29 +0200 Subject: [PATCH 04/20] Add more checks to NavMeshTilesCache tests --- apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp index a16c3af1d..447a5b44e 100644 --- a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp +++ b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp @@ -60,6 +60,7 @@ namespace EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData))); + EXPECT_NE(mNavMeshData.mValue, nullptr); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_return_cached_value) @@ -85,6 +86,7 @@ namespace NavMeshData anotherNavMeshData {anotherData, 1}; cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); + EXPECT_EQ(mNavMeshData.mValue, nullptr); const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(anotherNavMeshData)); ASSERT_TRUE(result); EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); From 375372981cbc5ee4c82217ec0bc84b35dba344bf Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 21 May 2021 23:15:14 +0200 Subject: [PATCH 05/20] Stop osg viewer threading before destructing engine To avoid rendering while engine parts are destructing. --- apps/openmw/engine.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b227ae04e..a06faa4d7 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -970,6 +970,8 @@ void OMW::Engine::go() // Save user settings settings.saveUser(settingspath); + mViewer->stopThreading(); + Log(Debug::Info) << "Quitting peacefully."; } From f12a52c603438e9cef6673b973e09bb38e34b691 Mon Sep 17 00:00:00 2001 From: Dobrohotov Alexei Date: Mon, 24 May 2021 01:57:08 +0300 Subject: [PATCH 06/20] Make the light distance slider de/increment by 128 --- files/mygui/openmw_settings_window.layout | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 92c185413..b602f3526 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -498,7 +498,7 @@ - + From d906ec773a5e8cfe57a0f9771fb42149deab5c1a Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Mon, 24 May 2021 17:57:39 +0000 Subject: [PATCH 07/20] Fix hangup on savegame after manual screenshots. --- apps/openmw/mwrender/screenshotmanager.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp index af2970fd8..f474a5a9f 100644 --- a/apps/openmw/mwrender/screenshotmanager.cpp +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -61,6 +61,7 @@ namespace MWRender void reset(unsigned int frame) { + std::lock_guard lock(mMutex); mDone = false; mFrame = frame; } @@ -104,11 +105,6 @@ namespace MWRender , mResourceSystem(resourceSystem) , mWater(water) { - // Note: This assumes no other final draw callbacks are set anywhere and that this callback will remain set until the application exits. - // This works around *DrawCallback manipulation being unsafe in OSG >= 3.5.10 for release 0.47 - // If you need to set other final draw callbacks, read the comments of issue 6013 for a suggestion - // Ref https://gitlab.com/OpenMW/openmw/-/issues/6013 - mViewer->getCamera()->setFinalDrawCallback(mDrawCompleteCallback); } ScreenshotManager::~ScreenshotManager() @@ -269,7 +265,10 @@ namespace MWRender void ScreenshotManager::traversalsAndWait(unsigned int frame) { + // Ref https://gitlab.com/OpenMW/openmw/-/issues/6013 mDrawCompleteCallback->reset(frame); + mViewer->getCamera()->setFinalDrawCallback(mDrawCompleteCallback); + mViewer->eventTraversal(); mViewer->updateTraversal(); mViewer->renderingTraversals(); From 3915e5d2cca844a2362e6df5efed2c5da71f9ead Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 26 May 2021 23:19:22 +0200 Subject: [PATCH 08/20] Always center loading screen progress bar by height when there is active message box To fix all possible situations when active message box overlaps with loading screen progress. The only used condition to center loading screen progress by height is number of message boxes > 0. No need to pass it through interface. LoadingScreen can check it inside setLabel function. --- apps/openmw/mwgui/loadingscreen.cpp | 4 ++-- apps/openmw/mwgui/loadingscreen.hpp | 2 +- apps/openmw/mwstate/statemanagerimp.cpp | 6 ++---- apps/openmw/mwworld/scene.cpp | 6 ++---- components/loadinglistener/loadinglistener.hpp | 2 +- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 65d49ffc9..61fcacca4 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -100,7 +100,7 @@ namespace MWGui Log(Debug::Warning) << "Warning: no splash screens found!"; } - void LoadingScreen::setLabel(const std::string &label, bool important, bool center) + void LoadingScreen::setLabel(const std::string &label, bool important) { mImportantLabel = important; @@ -110,7 +110,7 @@ namespace MWGui size.width = std::max(300, size.width); mLoadingBox->setSize(size); - if (center) + if (MWBase::Environment::get().getWindowManager()->getMessagesCount() > 0) mLoadingBox->setPosition(mMainWidget->getWidth()/2 - mLoadingBox->getWidth()/2, mMainWidget->getHeight()/2 - mLoadingBox->getHeight()/2); else mLoadingBox->setPosition(mMainWidget->getWidth()/2 - mLoadingBox->getWidth()/2, mMainWidget->getHeight() - mLoadingBox->getHeight() - 8); diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index ac911ab60..c64396534 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -37,7 +37,7 @@ namespace MWGui virtual ~LoadingScreen(); /// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details - void setLabel (const std::string& label, bool important, bool center) override; + void setLabel (const std::string& label, bool important) override; void loadingOn(bool visible=true) override; void loadingOff() override; void setProgressRange (size_t range) override; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index fb418b94a..ffef9d74a 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -260,10 +260,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.save (stream); Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); // Using only Cells for progress information, since they typically have the largest records by far listener.setProgressRange(MWBase::Environment::get().getWorld()->countSavedGameCells()); - listener.setLabel("#{sNotifyMessage4}", true, messagesCount > 0); + listener.setLabel("#{sNotifyMessage4}", true); Loading::ScopedLoad load(&listener); @@ -385,10 +384,9 @@ void MWState::StateManager::loadGame (const Character *character, const std::str std::map contentFileMap = buildContentFileIndexMap (reader); Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); listener.setProgressRange(100); - listener.setLabel("#{sLoadingMessage14}", false, messagesCount > 0); + listener.setLabel("#{sLoadingMessage14}"); Loading::ScopedLoad load(&listener); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 83e9805f6..1614e0705 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -566,9 +566,8 @@ namespace MWWorld Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); - int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); std::string loadingExteriorText = "#{sLoadingMessage3}"; - loadingListener->setLabel(loadingExteriorText, false, messagesCount > 0); + loadingListener->setLabel(loadingExteriorText); loadingListener->setProgressRange(refsToLoad); const auto getDistanceToPlayerCell = [&] (const std::pair& cellPosition) @@ -798,9 +797,8 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); std::string loadingInteriorText = "#{sLoadingMessage2}"; - loadingListener->setLabel(loadingInteriorText, false, messagesCount > 0); + loadingListener->setLabel(loadingInteriorText); Loading::ScopedLoad load(loadingListener); if(mCurrentCell != nullptr && *mCurrentCell == *cell) diff --git a/components/loadinglistener/loadinglistener.hpp b/components/loadinglistener/loadinglistener.hpp index 93467c141..14a1b96f9 100644 --- a/components/loadinglistener/loadinglistener.hpp +++ b/components/loadinglistener/loadinglistener.hpp @@ -14,7 +14,7 @@ namespace Loading /// @note "non-important" labels may not show on screen if the loading process went so fast /// that the implementation decided not to show a loading screen at all. "important" labels /// will show in a separate message-box if the loading screen was not shown. - virtual void setLabel (const std::string& label, bool important=false, bool center=false) {} + virtual void setLabel (const std::string& label, bool important=false) {} /// Start a loading sequence. Must call loadingOff() when done. /// @note To get the loading screen to actually update, you must call setProgress / increaseProgress periodically. From d122e184ccd6008069a5f544a9d2dfde7b9639a7 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 01:09:58 +0200 Subject: [PATCH 09/20] Report navmesh change for not posted tiles Corresponding recast mesh tiles can be updated but navmesh tiles may never appear for them. Report back zero navmesh version to allow oscillating recast objects detection to work. This version is always less than any generated navmesh tile version so any report for generated navmesh will override it. If zero navmesh version is reported after recast mesh tile got report about generated navmesh tile it is a no-op since generated version is always greater than zero. --- .../detournavigator/tilecachedrecastmeshmanager.cpp | 2 +- components/detournavigator/cachedrecastmeshmanager.cpp | 5 +++++ components/detournavigator/cachedrecastmeshmanager.hpp | 2 ++ components/detournavigator/navmeshmanager.cpp | 8 +++++--- components/detournavigator/recastmeshmanager.cpp | 5 +++++ components/detournavigator/recastmeshmanager.hpp | 2 ++ .../detournavigator/tilecachedrecastmeshmanager.hpp | 6 +++--- 7 files changed, 23 insertions(+), 7 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index 2d6d9f66b..83622c0b7 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -54,7 +54,7 @@ namespace { TileCachedRecastMeshManager manager(mSettings); std::size_t calls = 0; - manager.forEachTilePosition([&] (const TilePosition&) { ++calls; }); + manager.forEachTile([&] (const TilePosition&, const CachedRecastMeshManager&) { ++calls; }); EXPECT_EQ(calls, 0); } diff --git a/components/detournavigator/cachedrecastmeshmanager.cpp b/components/detournavigator/cachedrecastmeshmanager.cpp index 22b047fbd..2788b8046 100644 --- a/components/detournavigator/cachedrecastmeshmanager.cpp +++ b/components/detournavigator/cachedrecastmeshmanager.cpp @@ -66,4 +66,9 @@ namespace DetourNavigator { mImpl.reportNavMeshChange(recastMeshVersion, navMeshVersion); } + + Version CachedRecastMeshManager::getVersion() const + { + return mImpl.getVersion(); + } } diff --git a/components/detournavigator/cachedrecastmeshmanager.hpp b/components/detournavigator/cachedrecastmeshmanager.hpp index a19f017a4..ea55348f7 100644 --- a/components/detournavigator/cachedrecastmeshmanager.hpp +++ b/components/detournavigator/cachedrecastmeshmanager.hpp @@ -28,6 +28,8 @@ namespace DetourNavigator void reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion); + Version getVersion() const; + private: RecastMeshManager mImpl; std::shared_ptr mCached; diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 8f1aa86d4..de4c21c68 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -171,7 +171,7 @@ namespace DetourNavigator } } const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles); - mRecastMeshManager.forEachTilePosition([&] (const TilePosition& tile) + mRecastMeshManager.forEachTile([&] (const TilePosition& tile, CachedRecastMeshManager& recastMeshManager) { if (tilesToPost.count(tile)) return; @@ -181,6 +181,8 @@ namespace DetourNavigator tilesToPost.insert(std::make_pair(tile, ChangeType::add)); else if (!shouldAdd && presentInNavMesh) tilesToPost.insert(std::make_pair(tile, ChangeType::mixed)); + else + recastMeshManager.reportNavMeshChange(recastMeshManager.getVersion(), Version {0, 0}); }); } mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, tilesToPost); @@ -214,8 +216,8 @@ namespace DetourNavigator RecastMeshTiles NavMeshManager::getRecastMeshTiles() { std::vector tiles; - mRecastMeshManager.forEachTilePosition( - [&tiles] (const TilePosition& tile) { tiles.push_back(tile); }); + mRecastMeshManager.forEachTile( + [&tiles] (const TilePosition& tile, const CachedRecastMeshManager&) { tiles.push_back(tile); }); RecastMeshTiles result; std::transform(tiles.begin(), tiles.end(), std::inserter(result, result.end()), [this] (const TilePosition& tile) { return std::make_pair(tile, mRecastMeshManager.getMesh(tile)); }); diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index cdb9169d9..5fbcedff6 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -95,6 +95,11 @@ namespace DetourNavigator mLastNavMeshReportedChange = mLastNavMeshReport; } + Version RecastMeshManager::getVersion() const + { + return Version {mGeneration, mRevision}; + } + void RecastMeshManager::rebuild() { mMeshBuilder.reset(); diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index daa123fcb..ac17a347b 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -53,6 +53,8 @@ namespace DetourNavigator void reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion); + Version getVersion() const; + private: struct Report { diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index 68683f410..4c6f21c42 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -81,10 +81,10 @@ namespace DetourNavigator bool hasTile(const TilePosition& tilePosition); template - void forEachTilePosition(Function&& function) + void forEachTile(Function&& function) { - for (const auto& tile : *mTiles.lock()) - function(tile.first); + for (auto& [tilePosition, recastMeshManager] : *mTiles.lock()) + function(tilePosition, recastMeshManager); } std::size_t getRevision() const; From f4f9fa4701c8a41a13289c2b7a8f421f6a21be81 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 01:59:07 +0200 Subject: [PATCH 10/20] Limit oscillating recast mesh object AABB by tile bounds AABB change outside recast mesh tile should not affect navmesh for this tile. --- .../oscillatingrecastmeshobject.cpp | 17 ++++++++++++++++- .../oscillatingrecastmeshobject.hpp | 4 +++- .../detournavigator/recastmeshmanager.cpp | 3 ++- .../detournavigator/recastmeshmanager.hpp | 1 + 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/components/detournavigator/oscillatingrecastmeshobject.cpp b/components/detournavigator/oscillatingrecastmeshobject.cpp index 5b8423183..fbe4b77ff 100644 --- a/components/detournavigator/oscillatingrecastmeshobject.cpp +++ b/components/detournavigator/oscillatingrecastmeshobject.cpp @@ -1,9 +1,23 @@ #include "oscillatingrecastmeshobject.hpp" +#include "tilebounds.hpp" #include +#include + namespace DetourNavigator { + namespace + { + void limitBy(btAABB& aabb, const TileBounds& bounds) + { + aabb.m_min.setX(std::max(aabb.m_min.x(), static_cast(bounds.mMin.x()))); + aabb.m_min.setY(std::max(aabb.m_min.y(), static_cast(bounds.mMin.y()))); + aabb.m_max.setX(std::min(aabb.m_max.x(), static_cast(bounds.mMax.x()))); + aabb.m_max.setY(std::min(aabb.m_max.y(), static_cast(bounds.mMax.y()))); + } + } + OscillatingRecastMeshObject::OscillatingRecastMeshObject(RecastMeshObject&& impl, std::size_t lastChangeRevision) : mImpl(std::move(impl)) , mLastChangeRevision(lastChangeRevision) @@ -19,7 +33,7 @@ namespace DetourNavigator } bool OscillatingRecastMeshObject::update(const btTransform& transform, const AreaType areaType, - std::size_t lastChangeRevision) + std::size_t lastChangeRevision, const TileBounds& bounds) { const btTransform oldTransform = mImpl.getTransform(); if (!mImpl.update(transform, areaType)) @@ -37,6 +51,7 @@ namespace DetourNavigator } const btAABB currentAabb = mAabb; mAabb.merge(BulletHelpers::getAabb(mImpl.getShape(), transform)); + limitBy(mAabb, bounds); return currentAabb != mAabb; } } diff --git a/components/detournavigator/oscillatingrecastmeshobject.hpp b/components/detournavigator/oscillatingrecastmeshobject.hpp index 78a0c4b68..f8aabce62 100644 --- a/components/detournavigator/oscillatingrecastmeshobject.hpp +++ b/components/detournavigator/oscillatingrecastmeshobject.hpp @@ -3,6 +3,7 @@ #include "areatype.hpp" #include "recastmeshobject.hpp" +#include "tilebounds.hpp" #include #include @@ -15,7 +16,8 @@ namespace DetourNavigator explicit OscillatingRecastMeshObject(RecastMeshObject&& impl, std::size_t lastChangeRevision); explicit OscillatingRecastMeshObject(const RecastMeshObject& impl, std::size_t lastChangeRevision); - bool update(const btTransform& transform, const AreaType areaType, std::size_t lastChangeRevision); + bool update(const btTransform& transform, const AreaType areaType, std::size_t lastChangeRevision, + const TileBounds& bounds); const RecastMeshObject& getImpl() const { return mImpl; } diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index 5fbcedff6..e168a3304 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -5,6 +5,7 @@ namespace DetourNavigator RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation) : mGeneration(generation) , mMeshBuilder(settings, bounds) + , mTileBounds(bounds) { } @@ -29,7 +30,7 @@ namespace DetourNavigator return false; const std::size_t lastChangeRevision = mLastNavMeshReportedChange.has_value() ? mLastNavMeshReportedChange->mRevision : mRevision; - if (!object->second->update(transform, areaType, lastChangeRevision)) + if (!object->second->update(transform, areaType, lastChangeRevision, mTileBounds)) return false; ++mRevision; return true; diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index ac17a347b..e1606f0a9 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -65,6 +65,7 @@ namespace DetourNavigator std::size_t mRevision = 0; std::size_t mGeneration; RecastMeshBuilder mMeshBuilder; + TileBounds mTileBounds; std::list mObjectsOrder; std::unordered_map::iterator> mObjects; std::list mWaterOrder; From 22c2f106b755365ee0d85074a784a6c19fe5f0da Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 15:25:21 +0200 Subject: [PATCH 11/20] Store object tiles position as sorted vector instead of set --- .../tilecachedrecastmeshmanager.cpp | 20 ++++++++++--------- .../tilecachedrecastmeshmanager.hpp | 20 +++++++++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index 20ebc8fea..42ae93e80 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -3,6 +3,9 @@ #include "gettilespositions.hpp" #include "settingsutils.hpp" +#include +#include + namespace DetourNavigator { TileCachedRecastMeshManager::TileCachedRecastMeshManager(const Settings& settings) @@ -12,23 +15,22 @@ namespace DetourNavigator bool TileCachedRecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType) { - bool result = false; - auto& tilesPositions = mObjectsTilesPositions[id]; + std::vector tilesPositions; const auto border = getBorderSize(mSettings); { auto tiles = mTiles.lock(); getTilesPositions(shape, transform, mSettings, [&] (const TilePosition& tilePosition) { if (addTile(id, shape, transform, areaType, tilePosition, border, tiles.get())) - { - tilesPositions.insert(tilePosition); - result = true; - } + tilesPositions.push_back(tilePosition); }); } - if (result) - ++mRevision; - return result; + if (tilesPositions.empty()) + return false; + std::sort(tilesPositions.begin(), tilesPositions.end()); + mObjectsTilesPositions.insert_or_assign(id, std::move(tilesPositions)); + ++mRevision; + return true; } std::optional TileCachedRecastMeshManager::removeObject(const ObjectId id) diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index 4c6f21c42..23ecc7763 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -9,9 +9,10 @@ #include +#include #include #include -#include +#include namespace DetourNavigator { @@ -33,14 +34,14 @@ namespace DetourNavigator auto& currentTiles = object->second; const auto border = getBorderSize(mSettings); bool changed = false; - std::set newTiles; + std::vector newTiles; { auto tiles = mTiles.lock(); const auto onTilePosition = [&] (const TilePosition& tilePosition) { - if (currentTiles.count(tilePosition)) + if (std::binary_search(currentTiles.begin(), currentTiles.end(), tilePosition)) { - newTiles.insert(tilePosition); + newTiles.push_back(tilePosition); if (updateTile(id, transform, areaType, tilePosition, tiles.get())) { onChangedTile(tilePosition); @@ -49,24 +50,27 @@ namespace DetourNavigator } else if (addTile(id, shape, transform, areaType, tilePosition, border, tiles.get())) { - newTiles.insert(tilePosition); + newTiles.push_back(tilePosition); onChangedTile(tilePosition); changed = true; } }; getTilesPositions(shape, transform, mSettings, onTilePosition); + std::sort(newTiles.begin(), newTiles.end()); for (const auto& tile : currentTiles) { - if (!newTiles.count(tile) && removeTile(id, tile, tiles.get())) + if (!std::binary_search(newTiles.begin(), newTiles.end(), tile) && removeTile(id, tile, tiles.get())) { onChangedTile(tile); changed = true; } } } - std::swap(currentTiles, newTiles); if (changed) + { + currentTiles = std::move(newTiles); ++mRevision; + } return changed; } @@ -94,7 +98,7 @@ namespace DetourNavigator private: const Settings& mSettings; Misc::ScopeGuarded> mTiles; - std::unordered_map> mObjectsTilesPositions; + std::unordered_map> mObjectsTilesPositions; std::map> mWaterTilesPositions; std::size_t mRevision = 0; std::size_t mTilesGeneration = 0; From ed91cf9397cdf8d14c5a5823ee5155eca6cb5cd9 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 16:38:04 +0200 Subject: [PATCH 12/20] Replace unordered_map by map for storing objects For small amount of items it gives better performance for find by key for update. --- components/detournavigator/recastmeshmanager.cpp | 9 ++++----- components/detournavigator/recastmeshmanager.hpp | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index e168a3304..bfc0409a5 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -12,13 +12,12 @@ namespace DetourNavigator bool RecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType) { + const auto object = mObjects.lower_bound(id); + if (object != mObjects.end() && object->first == id) + return false; const auto iterator = mObjectsOrder.emplace(mObjectsOrder.end(), OscillatingRecastMeshObject(RecastMeshObject(shape, transform, areaType), mRevision + 1)); - if (!mObjects.emplace(id, iterator).second) - { - mObjectsOrder.erase(iterator); - return false; - } + mObjects.emplace_hint(object, id, iterator); ++mRevision; return true; } diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index e1606f0a9..5922821f2 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -13,7 +13,6 @@ #include #include #include -#include class btCollisionShape; @@ -67,7 +66,7 @@ namespace DetourNavigator RecastMeshBuilder mMeshBuilder; TileBounds mTileBounds; std::list mObjectsOrder; - std::unordered_map::iterator> mObjects; + std::map::iterator> mObjects; std::list mWaterOrder; std::map::iterator> mWater; std::optional mLastNavMeshReportedChange; From 4a6961b365e5621e6d39524c2b94c5084cd21b06 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 16:52:01 +0200 Subject: [PATCH 13/20] Trigger navmesh update on moved player only when player tile has been changed --- apps/openmw/mwworld/scene.cpp | 2 +- components/detournavigator/navigator.hpp | 8 +++++++- components/detournavigator/navigatorimpl.cpp | 9 +++++++++ components/detournavigator/navigatorimpl.hpp | 3 +++ components/detournavigator/navigatorstub.hpp | 2 ++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 83e9805f6..ac62aab19 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -500,7 +500,7 @@ namespace MWWorld { const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - navigator->update(player.getRefData().getPosition().asVec3()); + navigator->updatePlayerPosition(player.getRefData().getPosition().asVec3()); if (!mCurrentCell || !mCurrentCell->isExterior()) return; diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 8cf4cb80e..0dab7ba6b 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -155,11 +155,17 @@ namespace DetourNavigator virtual void removePathgrid(const ESM::Pathgrid& pathgrid) = 0; /** - * @brief update start background navmesh update using current scene state. + * @brief update starts background navmesh update using current scene state. * @param playerPosition setup initial point to order build tiles of navmesh. */ virtual void update(const osg::Vec3f& playerPosition) = 0; + /** + * @brief updatePlayerPosition starts background navmesh update using current scene state only when player position has been changed. + * @param playerPosition setup initial point to order build tiles of navmesh. + */ + virtual void updatePlayerPosition(const osg::Vec3f& playerPosition) = 0; + /** * @brief disable navigator updates */ diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 7522fe622..b7b3bbd58 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -146,6 +146,15 @@ namespace DetourNavigator mNavMeshManager.update(playerPosition, v.first); } + void NavigatorImpl::updatePlayerPosition(const osg::Vec3f& playerPosition) + { + const TilePosition tilePosition = getTilePosition(mSettings, toNavMeshCoordinates(mSettings, playerPosition)); + if (mLastPlayerPosition.has_value() && *mLastPlayerPosition == tilePosition) + return; + update(playerPosition); + mLastPlayerPosition = tilePosition; + } + void NavigatorImpl::setUpdatesEnabled(bool enabled) { mUpdatesEnabled = enabled; diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 324946261..80c6957d7 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -46,6 +46,8 @@ namespace DetourNavigator void update(const osg::Vec3f& playerPosition) override; + void updatePlayerPosition(const osg::Vec3f& playerPosition) override; + void setUpdatesEnabled(bool enabled) override; void wait(Loading::Listener& listener, WaitConditionType waitConditionType) override; @@ -66,6 +68,7 @@ namespace DetourNavigator Settings mSettings; NavMeshManager mNavMeshManager; bool mUpdatesEnabled; + std::optional mLastPlayerPosition; std::map mAgents; std::unordered_map mAvoidIds; std::unordered_map mWaterIds; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index 2c12c45eb..0a0881393 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -71,6 +71,8 @@ namespace DetourNavigator void update(const osg::Vec3f& /*playerPosition*/) override {} + void updatePlayerPosition(const osg::Vec3f& /*playerPosition*/) override {}; + void setUpdatesEnabled(bool /*enabled*/) override {} void wait(Loading::Listener& /*listener*/, WaitConditionType /*waitConditionType*/) override {} From 1a1085272a89ff813c7938b6c3008c1927d70a03 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 27 May 2021 17:13:04 +0200 Subject: [PATCH 14/20] Use ciEqual to determine actor-specific answers --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 34fd5828f..3c6349ad8 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -343,7 +343,7 @@ namespace MWDialogue if(!inJournal(topicId, answer->mId)) { // Does this dialogue contains some actor-specific answer? - if (answer->mActor == mActor.getCellRef().getRefId()) + if (Misc::StringUtils::ciEqual(answer->mActor, mActor.getCellRef().getRefId())) flag |= MWBase::DialogueManager::TopicType::Specific; } else From e9433a91fbd6f2816d8ec2a170cb922ace2d4631 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 20:18:52 +0200 Subject: [PATCH 15/20] Add more tests for TileCachedRecastMeshManager --- .../tilecachedrecastmeshmanager.cpp | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index 83622c0b7..fb0f97831 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -73,6 +73,16 @@ namespace EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); } + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const btBoxShape boxShape(btVector3(20, 20, 100)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); + for (int x = -1; x < 1; ++x) + for (int y = -1; y < 1; ++y) + ASSERT_TRUE(manager.hasTile(TilePosition(x, y))); + } + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_return_changed_tiles) { TileCachedRecastMeshManager manager(mSettings); @@ -238,4 +248,79 @@ namespace manager.removeObject(ObjectId(&manager)); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision); } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_new_water_should_return_true) + { + TileCachedRecastMeshManager manager(mSettings); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = 8192; + EXPECT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_not_max_int_should_add_new_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = 8192; + ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + for (int x = -6; x < 6; ++x) + for (int y = -6; y < 6; ++y) + ASSERT_TRUE(manager.hasTile(TilePosition(x, y))); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_max_int_should_not_add_new_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const btBoxShape boxShape(btVector3(20, 20, 100)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = std::numeric_limits::max(); + ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + for (int x = -6; x < 6; ++x) + for (int y = -6; y < 6; ++y) + ASSERT_EQ(manager.hasTile(TilePosition(x, y)), -1 <= x && x <= 0 && -1 <= y && y <= 0); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_absent_cell_should_return_nullopt) + { + TileCachedRecastMeshManager manager(mSettings); + EXPECT_EQ(manager.removeWater(osg::Vec2i(0, 0)), std::nullopt); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_return_removed_water) + { + TileCachedRecastMeshManager manager(mSettings); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = 8192; + ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + const auto result = manager.removeWater(cellPosition); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->mCellSize, cellSize); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_remove_empty_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = 8192; + ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + ASSERT_TRUE(manager.removeWater(cellPosition)); + for (int x = -6; x < 6; ++x) + for (int y = -6; y < 6; ++y) + ASSERT_FALSE(manager.hasTile(TilePosition(x, y))); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_leave_not_empty_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const btBoxShape boxShape(btVector3(20, 20, 100)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = 8192; + ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + ASSERT_TRUE(manager.removeWater(cellPosition)); + for (int x = -6; x < 6; ++x) + for (int y = -6; y < 6; ++y) + ASSERT_EQ(manager.hasTile(TilePosition(x, y)), -1 <= x && x <= 0 && -1 <= y && y <= 0); + } } From d4f28ac97967911dcd56f8a462a892d2918dccd1 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 28 May 2021 10:41:03 +0200 Subject: [PATCH 16/20] Avoid resetting buffer object when configuring light buffer layout Otherwise this casues RaceSelectionPreview to have no light until first change. --- components/sceneutil/lightmanager.cpp | 100 +++++++++++++++----------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index a69a64ba3..c4233059c 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -103,47 +103,12 @@ namespace SceneUtil : mData(new osg::FloatArray(3*4*count)) , mEndian(osg::getCpuByteOrder()) , mCount(count) - , mStride(12) , mCachedSunPos(osg::Vec4()) { - mOffsets[Diffuse] = 0; - mOffsets[Ambient] = 1; - mOffsets[Specular] = 2; - mOffsets[DiffuseSign] = 3; - mOffsets[Position] = 4; - mOffsets[AttenuationRadius] = 8; } LightBuffer(const LightBuffer&) = delete; - LightBuffer(const LightBuffer& other, int offsetColors, int offsetPosition, int offsetAttenuationRadius, int size, int stride) - : mData(new osg::FloatArray(size / sizeof(GL_FLOAT))) - , mEndian(other.mEndian) - , mCount(other.mCount) - , mStride((offsetAttenuationRadius + sizeof(GL_FLOAT) * osg::Vec4::num_components + stride) / 4) - , mCachedSunPos(other.mCachedSunPos) - { - mData->setBufferObject(other.mData->getBufferObject()); - - constexpr auto sizeofFloat = sizeof(GL_FLOAT); - const auto diffuseOffset = offsetColors / sizeofFloat; - - mOffsets[Diffuse] = diffuseOffset; - mOffsets[Ambient] = diffuseOffset + 1; - mOffsets[Specular] = diffuseOffset + 2; - mOffsets[DiffuseSign] = diffuseOffset + 3; - mOffsets[Position] = offsetPosition / sizeofFloat; - mOffsets[AttenuationRadius] = offsetAttenuationRadius / sizeofFloat; - - // Copy over previous buffers light data. Buffers populate before we know the layout. - for (int i = 0; i < other.mCount; ++i) - { - std::memcpy(&(*mData)[getOffset(i, Diffuse)], &(*other.mData)[other.getOffset(i, Diffuse)], sizeof(osg::Vec4f)); - std::memcpy(&(*mData)[getOffset(i, Position)], &(*other.mData)[other.getOffset(i, Position)], sizeof(osg::Vec4f)); - std::memcpy(&(*mData)[getOffset(i, AttenuationRadius)], &(*other.mData)[other.getOffset(i, AttenuationRadius)], sizeof(osg::Vec4f)); - } - } - void setDiffuse(int index, const osg::Vec4& value) { // Deal with negative lights (negative diffuse) by passing a sign bit in the unused alpha component @@ -214,15 +179,69 @@ namespace SceneUtil int getOffset(int index, LayoutOffset slot) const { - return mStride * index + mOffsets[slot]; + return mOffsets.get(index, slot); + } + + void configureLayout(int offsetColors, int offsetPosition, int offsetAttenuationRadius, int size, int stride) + { + const Offsets offsets(offsetColors, offsetPosition, offsetAttenuationRadius, stride); + + // Copy cloned data using current layout into current data using new layout. + // This allows to preserve osg::FloatArray buffer object in mData. + const auto data = mData->asVector(); + mData->resizeArray(static_cast(size)); + for (int i = 0; i < mCount; ++i) + { + std::memcpy(&(*mData)[offsets.get(i, Diffuse)], data.data() + getOffset(i, Diffuse), sizeof(osg::Vec4f)); + std::memcpy(&(*mData)[offsets.get(i, Position)], data.data() + getOffset(i, Position), sizeof(osg::Vec4f)); + std::memcpy(&(*mData)[offsets.get(i, AttenuationRadius)], data.data() + getOffset(i, AttenuationRadius), sizeof(osg::Vec4f)); + } + mOffsets = offsets; } private: + class Offsets + { + public: + Offsets() + : mStride(12) + { + mValues[Diffuse] = 0; + mValues[Ambient] = 1; + mValues[Specular] = 2; + mValues[DiffuseSign] = 3; + mValues[Position] = 4; + mValues[AttenuationRadius] = 8; + } + + Offsets(int offsetColors, int offsetPosition, int offsetAttenuationRadius, int stride) + : mStride((offsetAttenuationRadius + sizeof(GL_FLOAT) * osg::Vec4::num_components + stride) / 4) + { + constexpr auto sizeofFloat = sizeof(GL_FLOAT); + const auto diffuseOffset = offsetColors / sizeofFloat; + + mValues[Diffuse] = diffuseOffset; + mValues[Ambient] = diffuseOffset + 1; + mValues[Specular] = diffuseOffset + 2; + mValues[DiffuseSign] = diffuseOffset + 3; + mValues[Position] = offsetPosition / sizeofFloat; + mValues[AttenuationRadius] = offsetAttenuationRadius / sizeofFloat; + } + + int get(int index, LayoutOffset slot) const + { + return mStride * index + mValues[slot]; + } + + private: + int mStride; + std::array mValues; + }; + osg::ref_ptr mData; osg::Endian mEndian; int mCount; - int mStride; - std::array mOffsets; + Offsets mOffsets; osg::Vec4 mCachedSunPos; }; @@ -751,10 +770,7 @@ namespace SceneUtil ext->glGetActiveUniformsiv(handle, indices.size(), indices.data(), GL_UNIFORM_OFFSET, offsets.data()); for (int i = 0; i < 2; ++i) - { - auto& buf = mLightManager->getLightBuffer(i); - buf = new LightBuffer(*buf, offsets[0], offsets[1], offsets[2], totalBlockSize, stride); - } + mLightManager->getLightBuffer(i)->configureLayout(offsets[0], offsets[1], offsets[2], totalBlockSize, stride); } void apply(osg::State& state) const override From b8472e13032108b75980b65877380cb214edd772 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Fri, 28 May 2021 16:55:54 +0200 Subject: [PATCH 17/20] Use modified paralyze magnitude to fall and float --- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwphysics/physicssystem.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f7c50c5b6..54c951187 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2458,7 +2458,7 @@ void CharacterController::update(float duration) if (mFloatToSurface && cls.isActor()) { if (cls.getCreatureStats(mPtr).isDead() - || (!godmode && cls.getCreatureStats(mPtr).isParalyzed())) + || (!godmode && cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Paralyze).getModifier() > 0)) { moved.z() = 1.0; } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 705e15187..833bb9a16 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -932,7 +932,7 @@ namespace MWPhysics mWantJump = ptr.getClass().getMovementSettings(ptr).mPosition[2] != 0; auto& stats = ptr.getClass().getCreatureStats(ptr); const bool godmode = ptr == world->getPlayerConstPtr() && world->getGodModeState(); - mFloatToSurface = stats.isDead() || (!godmode && stats.isParalyzed()); + mFloatToSurface = stats.isDead() || (!godmode && stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getModifier() > 0); mWasOnGround = actor->getOnGround(); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e406992e2..45a7dd8a7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2323,7 +2323,7 @@ namespace MWWorld return false; const bool isPlayer = ptr == getPlayerConstPtr(); - if (!(isPlayer && mGodMode) && stats.isParalyzed()) + if (!(isPlayer && mGodMode) && stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getModifier() > 0) return false; if (ptr.getClass().canFly(ptr)) From a487295d39544455d754d107675792cf2074c613 Mon Sep 17 00:00:00 2001 From: psi29a Date: Sat, 29 May 2021 20:29:53 +0000 Subject: [PATCH 18/20] Merge branch 'fix_new_game_guard' into 'master' Consider time to destination when try to avoid collision See merge request OpenMW/openmw!914 --- apps/openmw/mwmechanics/actors.cpp | 24 ++++++++++++++++++++---- apps/openmw/mwmechanics/actors.hpp | 2 +- apps/openmw/mwmechanics/aipackage.cpp | 26 ++++++++++++++++++++++---- apps/openmw/mwmechanics/aipackage.hpp | 5 +++++ 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4629ad2d3..0ffcef2f8 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1,5 +1,7 @@ #include "actors.hpp" +#include + #include #include @@ -173,6 +175,15 @@ namespace MWMechanics static const int GREETING_COOLDOWN = 40; // how many updates should pass before NPC can continue movement static const float DECELERATE_DISTANCE = 512.f; + namespace + { + float getTimeToDestination(const AiPackage& package, const osg::Vec3f& position, float speed, float duration, const osg::Vec3f& halfExtents) + { + const auto distanceToNextPathPoint = (package.getNextPathPoint(package.getDestination()) - position).length(); + return (distanceToNextPathPoint - package.getNextPathPointTolerance(speed, duration, halfExtents)) / speed; + } + } + class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor { public: @@ -1764,7 +1775,7 @@ namespace MWMechanics } - void Actors::predictAndAvoidCollisions() + void Actors::predictAndAvoidCollisions(float duration) { if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) return; @@ -1799,7 +1810,8 @@ namespace MWMechanics bool shouldAvoidCollision = isMoving; bool shouldTurnToApproachingActor = !isMoving; MWWorld::Ptr currentTarget; // Combat or pursue target (NPCs should not avoid collision with their targets). - for (const auto& package : ptr.getClass().getCreatureStats(ptr).getAiSequence()) + const auto& aiSequence = ptr.getClass().getCreatureStats(ptr).getAiSequence(); + for (const auto& package : aiSequence) { if (package->getTypeId() == AiPackageTypeId::Follow) shouldAvoidCollision = true; @@ -1829,6 +1841,10 @@ namespace MWMechanics osg::Vec2f movementCorrection(0, 0); float angleToApproachingActor = 0; + const float timeToDestination = aiSequence.isEmpty() + ? std::numeric_limits::max() + : getTimeToDestination(**aiSequence.begin(), basePos, maxSpeed, duration, halfExtents); + // Iterate through all other actors and predict collisions. for(PtrActorMap::iterator otherIter(mActors.begin()); otherIter != mActors.end(); ++otherIter) { @@ -1865,7 +1881,7 @@ namespace MWMechanics continue; // No solution; distance is always >= collisionDist. float t = (-vr - std::sqrt(Dh)) / v2; - if (t < 0 || t > timeToCollision) + if (t < 0 || t > timeToCollision || t > timeToDestination) continue; // Check visibility and awareness last as it's expensive. @@ -2075,7 +2091,7 @@ namespace MWMechanics static const bool avoidCollisions = Settings::Manager::getBool("NPCs avoid collisions", "Game"); if (avoidCollisions) - predictAndAvoidCollisions(); + predictAndAvoidCollisions(duration); timerUpdateHeadTrack += duration; timerUpdateEquippedLight += duration; diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 2de0728d5..0ae968757 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -63,7 +63,7 @@ namespace MWMechanics void purgeSpellEffects (int casterActorId); - void predictAndAvoidCollisions(); + void predictAndAvoidCollisions(float duration); public: diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index a7e8e74b0..0701f42cf 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -28,6 +28,12 @@ namespace { return divisor == 0 ? std::numeric_limits::max() * std::numeric_limits::epsilon() : dividend / divisor; } + + float getPointTolerance(float speed, float duration, const osg::Vec3f& halfExtents) + { + const float actorTolerance = 2 * speed * duration + 1.2 * std::max(halfExtents.x(), halfExtents.y()); + return std::max(MWMechanics::MIN_TOLERANCE, actorTolerance); + } } MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) : @@ -98,6 +104,8 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& return false; } + mLastDestinationTolerance = destTolerance; + const float distToTarget = distance(position, dest); const bool isDestReached = (distToTarget <= destTolerance); const bool actorCanMoveByZ = canActorMoveByZAxis(actor); @@ -148,9 +156,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& } } - const float actorTolerance = 2 * actor.getClass().getMaxSpeed(actor) * duration - + 1.2 * std::max(halfExtents.x(), halfExtents.y()); - const float pointTolerance = std::max(MIN_TOLERANCE, actorTolerance); + const float pointTolerance = getPointTolerance(actor.getClass().getMaxSpeed(actor), duration, halfExtents); static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game"); mPathFinder.update(position, pointTolerance, DEFAULT_TOLERANCE, @@ -181,7 +187,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& zTurn(actor, zAngleToNext); smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0); - const auto destination = mPathFinder.getPath().empty() ? dest : mPathFinder.getPath().front(); + const auto destination = getNextPathPoint(dest); mObstacleCheck.update(actor, destination, duration); if (smoothMovement) @@ -461,3 +467,15 @@ DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::P return costs; } + +osg::Vec3f MWMechanics::AiPackage::getNextPathPoint(const osg::Vec3f& destination) const +{ + return mPathFinder.getPath().empty() ? destination : mPathFinder.getPath().front(); +} + +float MWMechanics::AiPackage::getNextPathPointTolerance(float speed, float duration, const osg::Vec3f& halfExtents) const +{ + if (mPathFinder.getPathSize() <= 1) + return mLastDestinationTolerance; + return getPointTolerance(speed, duration, halfExtents); +} diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 5ad73c2da..6d8af0d92 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -122,6 +122,10 @@ namespace MWMechanics /// Return if actor's rotation speed is sufficient to rotate to the destination pathpoint on the run. Otherwise actor should rotate while standing. static bool isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const osg::Vec3f& dest); + osg::Vec3f getNextPathPoint(const osg::Vec3f& destination) const; + + float getNextPathPointTolerance(float speed, float duration, const osg::Vec3f& halfExtents) const; + protected: /// Handles path building and shortcutting with obstacles avoiding /** \return If the actor has arrived at his destination **/ @@ -166,6 +170,7 @@ namespace MWMechanics bool mIsShortcutting; // if shortcutting at the moment bool mShortcutProhibited; // shortcutting may be prohibited after unsuccessful attempt osg::Vec3f mShortcutFailPos; // position of last shortcut fail + float mLastDestinationTolerance = 0; private: bool isNearInactiveCell(osg::Vec3f position); From 9058fa9f60bdd81f15768d3264f0ff1996291e5e Mon Sep 17 00:00:00 2001 From: psi29a Date: Sat, 29 May 2021 20:32:00 +0000 Subject: [PATCH 19/20] Merge branch 'fix_launcher_ui' into 'master' Make launcher Advanced/Visuals tab look more like other See merge request OpenMW/openmw!908 --- files/ui/advancedpage.ui | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 5859f1f38..b9d5c7ef5 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -507,6 +507,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + From 3e4340338f0fd3a994cc4dcf69b2fe0e919367a3 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Mon, 31 May 2021 21:11:43 +0100 Subject: [PATCH 20/20] Update IRC links to libera In README, in OpenCS about dialog, in Travis notification --- .travis.yml | 2 +- README.md | 2 +- apps/opencs/view/doc/view.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d019b5526..1322dfca1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -74,7 +74,7 @@ notifications: irc: if: repository_slug = OpenMW/openmw AND branch = master channels: - - "chat.freenode.net#openmw" + - "irc.libera.chat#openmw" on_success: change on_failure: always use_notice: true diff --git a/README.md b/README.md index 6aa0a8515..54fd59f00 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set. * 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 +* IRC: #openmw on irc.libera.chat Font Licenses: * DejaVuLGCSansMono.ttf: custom (see [files/mygui/DejaVuFontLicense.txt](https://github.com/OpenMW/openmw/blob/master/files/mygui/DejaVuFontLicense.txt) for more information) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 6fe01dc27..4514cfb58 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -749,7 +749,7 @@ void CSVDoc::View::infoAbout() "%4https://openmw.org" "%5https://forum.openmw.org" "%6https://gitlab.com/OpenMW/openmw/issues" - "%7irc://irc.freenode.net/#openmw" + "%7ircs://irc.libera.chat/#openmw" "" "

") .arg(versionInfo