From f169f8e6f0325bfb1f9e2c1cb337cde53abd0ce6 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 5 May 2021 18:13:17 +0200 Subject: [PATCH 1/2] Wait until navmesh is generated for interior cells Add special loading progress bar. It should be fast enough to not keep loading screen for noticably long but will provide better pathfinding for actors inside interior cells. --- apps/openmw/mwworld/scene.cpp | 2 + .../detournavigator/navigator.cpp | 46 +++++++++--------- .../detournavigator/asyncnavmeshupdater.cpp | 48 +++++++++++++++++-- .../detournavigator/asyncnavmeshupdater.hpp | 11 ++++- components/detournavigator/navigator.hpp | 7 ++- components/detournavigator/navigatorimpl.cpp | 4 +- components/detournavigator/navigatorimpl.hpp | 2 +- components/detournavigator/navigatorstub.hpp | 7 ++- components/detournavigator/navmeshmanager.cpp | 4 +- components/detournavigator/navmeshmanager.hpp | 2 +- components/misc/guarded.hpp | 4 +- 11 files changed, 99 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index dae703c8c..0cfca3be2 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -847,6 +847,8 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); + + mNavigator.wait(*loadingListener); } void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 619276588..8d92b48e0 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,7 @@ namespace std::back_insert_iterator> mOut; float mStepSize; AreaCosts mAreaCosts; + Loading::Listener mListener; DetourNavigatorNavigatorTest() : mPlayerPosition(0, 0, 0) @@ -124,7 +126,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -174,7 +176,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -206,7 +208,7 @@ namespace mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mPath.clear(); mOut = std::back_inserter(mPath); @@ -259,7 +261,7 @@ namespace mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -293,7 +295,7 @@ namespace mNavigator->updateObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mPath.clear(); mOut = std::back_inserter(mPath); @@ -352,7 +354,7 @@ namespace mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape2), shape2, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -408,7 +410,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), ObjectShapes {shape, &shapeAvoid}, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -456,7 +458,7 @@ namespace mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, 300, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mStart.x() = 0; mStart.z() = 300; @@ -504,7 +506,7 @@ namespace mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mStart.x() = 0; mEnd.x() = 0; @@ -551,7 +553,7 @@ namespace mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addWater(osg::Vec2i(0, 0), std::numeric_limits::max(), -25, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mStart.x() = 0; mEnd.x() = 0; @@ -598,7 +600,7 @@ namespace mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mStart.x() = 0; mEnd.x() = 0; @@ -642,15 +644,15 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mNavigator->removeObject(ObjectId(&shape)); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -696,7 +698,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); Misc::Rng::init(42); @@ -745,7 +747,7 @@ namespace } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -788,7 +790,7 @@ namespace mNavigator->addObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); const auto start = std::chrono::steady_clock::now(); for (std::size_t i = 0; i < shapes.size(); ++i) @@ -797,7 +799,7 @@ namespace mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); for (std::size_t i = 0; i < shapes.size(); ++i) { @@ -805,7 +807,7 @@ namespace mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); const auto duration = std::chrono::steady_clock::now() - start; @@ -828,7 +830,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); const auto result = mNavigator->raycast(mAgentHalfExtents, mStart, mEnd, Flag_walk); @@ -859,7 +861,7 @@ namespace mNavigator->addObject(ObjectId(&boderBoxShape), boderBoxShape, btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200))); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); const auto navMeshes = mNavigator->getNavMeshes(); ASSERT_EQ(navMeshes.size(), 1); @@ -875,7 +877,7 @@ namespace oscillatingBoxShapePosition); mNavigator->updateObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, transform); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); } ASSERT_EQ(navMeshes.size(), 1); diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 7248850a6..124d3c4e0 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -6,6 +6,7 @@ #include #include +#include #include @@ -111,13 +112,44 @@ namespace DetourNavigator mHasJob.notify_all(); } - void AsyncNavMeshUpdater::wait() + void AsyncNavMeshUpdater::wait(Loading::Listener& listener) { - { - std::unique_lock lock(mMutex); - mDone.wait(lock, [&] { return mJobs.empty() && getTotalThreadJobsUnsafe() == 0; }); - } + listener.setLabel("Building navigation mesh"); + const std::size_t initialJobsLeft = getTotalJobs(); + std::size_t maxProgress = initialJobsLeft + mThreads.size(); + listener.setProgressRange(maxProgress); + waitUntilJobsDone(initialJobsLeft, maxProgress, listener); mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); + listener.setProgress(maxProgress); + } + + void AsyncNavMeshUpdater::waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener) + { + std::size_t prevJobsLeft = initialJobsLeft; + std::size_t jobsDone = 0; + std::size_t jobsLeft = 0; + const auto isDone = [&] + { + jobsLeft = mJobs.size() + getTotalThreadJobsUnsafe(); + return jobsLeft == 0; + }; + std::unique_lock lock(mMutex); + while (!mDone.wait_for(lock, std::chrono::milliseconds(250), isDone)) + { + if (maxProgress < jobsLeft) + { + maxProgress = jobsLeft + mThreads.size(); + listener.setProgressRange(maxProgress); + listener.setProgress(jobsDone); + } + else if (jobsLeft < prevJobsLeft) + { + const std::size_t newJobsDone = prevJobsLeft - jobsLeft; + jobsDone += newJobsDone; + prevJobsLeft = jobsLeft; + listener.increaseProgress(newJobsDone); + } + } } void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const @@ -381,6 +413,12 @@ namespace DetourNavigator mProcessed.notify_all(); } + std::size_t AsyncNavMeshUpdater::getTotalJobs() const + { + const std::scoped_lock lock(mMutex); + return mJobs.size() + getTotalThreadJobsUnsafe(); + } + std::size_t AsyncNavMeshUpdater::getTotalThreadJobsUnsafe() const { return std::accumulate(mThreadsQueues.begin(), mThreadsQueues.end(), std::size_t(0), diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 53e7fd7c1..fcf57d683 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -20,6 +20,11 @@ class dtNavMesh; +namespace Loading +{ + class Listener; +} + namespace DetourNavigator { enum class ChangeType @@ -55,7 +60,7 @@ namespace DetourNavigator void post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& mNavMeshCacheItem, const TilePosition& playerTile, const std::map& changedTiles); - void wait(); + void wait(Loading::Listener& listener); void reportStats(unsigned int frameNumber, osg::Stats& stats) const; @@ -131,9 +136,13 @@ namespace DetourNavigator void unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile); + inline std::size_t getTotalJobs() const; + inline std::size_t getTotalThreadJobsUnsafe() const; void cleanupLastUpdates(); + + void waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener); }; } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index edf597348..3ec5e5acc 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -14,6 +14,11 @@ namespace ESM struct Pathgrid; } +namespace Loading +{ + class Listener; +} + namespace DetourNavigator { struct ObjectShapes @@ -162,7 +167,7 @@ namespace DetourNavigator /** * @brief wait locks thread until all tiles are updated from last update call. */ - virtual void wait() = 0; + virtual void wait(Loading::Listener& listener) = 0; /** * @brief findPath fills output iterator with points of scene surfaces to be used for actor to walk through. diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index abfb20ba8..d1e75b864 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -153,9 +153,9 @@ namespace DetourNavigator mUpdatesEnabled = enabled; } - void NavigatorImpl::wait() + void NavigatorImpl::wait(Loading::Listener& listener) { - mNavMeshManager.wait(); + mNavMeshManager.wait(listener); } SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const osg::Vec3f& agentHalfExtents) const diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 74fff0dea..a53c9608d 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -48,7 +48,7 @@ namespace DetourNavigator void setUpdatesEnabled(bool enabled) override; - void wait() override; + void wait(Loading::Listener& listener) override; SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const override; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index f6892bf1b..c21db2bf8 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -3,6 +3,11 @@ #include "navigator.hpp" +namespace Loading +{ + class Listener; +} + namespace DetourNavigator { class NavigatorStub final : public Navigator @@ -68,7 +73,7 @@ namespace DetourNavigator void setUpdatesEnabled(bool /*enabled*/) override {} - void wait() override {} + void wait(Loading::Listener& /*listener*/) override {} SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override { diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 43d330648..21022d60b 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -188,9 +188,9 @@ namespace DetourNavigator " recastMeshManagerRevision=" << lastRevision; } - void NavMeshManager::wait() + void NavMeshManager::wait(Loading::Listener& listener) { - mAsyncNavMeshUpdater.wait(); + mAsyncNavMeshUpdater.wait(listener); } SharedNavMeshCacheItem NavMeshManager::getNavMesh(const osg::Vec3f& agentHalfExtents) const diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index f3861f8f2..ce90aafc5 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -45,7 +45,7 @@ namespace DetourNavigator void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents); - void wait(); + void wait(Loading::Listener& listener); SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const; diff --git a/components/misc/guarded.hpp b/components/misc/guarded.hpp index 55a2c670c..7f1005fc3 100644 --- a/components/misc/guarded.hpp +++ b/components/misc/guarded.hpp @@ -75,7 +75,7 @@ namespace Misc return Locked(mMutex, mValue); } - Locked lockConst() + Locked lockConst() const { return Locked(mMutex, mValue); } @@ -88,7 +88,7 @@ namespace Misc } private: - std::mutex mMutex; + mutable std::mutex mMutex; T mValue; }; } From 7a51d0db1886d549e81e3ad8e2f21110e4ac881e Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 5 May 2021 19:23:07 +0200 Subject: [PATCH 2/2] Wait until navmesh is generated within given distance around player Add a setting to change this distance. To prevent situations when there is not enough navmesh generated and actors can't find path correctly. --- CHANGELOG.md | 1 + apps/openmw/mwworld/scene.cpp | 2 + .../detournavigator/navigator.cpp | 1 + .../detournavigator/asyncnavmeshupdater.cpp | 37 ++++++++++++++++--- .../detournavigator/asyncnavmeshupdater.hpp | 2 +- components/detournavigator/settings.cpp | 1 + components/detournavigator/settings.hpp | 1 + .../reference/modding/settings/navigator.rst | 12 ++++++ files/settings-default.cfg | 4 ++ 9 files changed, 55 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba8081dae..867ae0412 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -141,6 +141,7 @@ Feature #5456: Basic collada animation support Feature #5457: Realistic diagonal movement Feature #5486: Fixes trainers to choose their training skills based on their base skill points + Feature #5500: Prepare enough navmesh tiles before scene loading ends Feature #5511: Add in game option to toggle HRTF support in OpenMW Feature #5519: Code Patch tab in launcher Feature #5524: Resume failed script execution after reload diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 0cfca3be2..c2d96788c 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -620,6 +620,8 @@ namespace MWWorld if (changeEvent) mCellChanged = true; + + mNavigator.wait(*loadingListener); } void Scene::testExteriorCells() diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 8d92b48e0..cb8ae4152 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -66,6 +66,7 @@ namespace mSettings.mRegionMergeSize = 20; mSettings.mRegionMinSize = 8; mSettings.mTileSize = 64; + mSettings.mWaitUntilMinDistanceToPlayer = std::numeric_limits::max(); mSettings.mAsyncNavMeshUpdaterThreads = 1; mSettings.mMaxNavMeshTilesCacheSize = 1024 * 1024; mSettings.mMaxPolygonPathSize = 1024; diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 124d3c4e0..ff51e39aa 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -21,6 +21,16 @@ namespace { return std::abs(lhs.x() - rhs.x()) + std::abs(lhs.y() - rhs.y()); } + + int getMinDistanceTo(const TilePosition& position, int maxDistance, + const std::map>& tilesPerHalfExtents) + { + int result = maxDistance; + for (const auto& [halfExtents, tiles] : tilesPerHalfExtents) + for (const TilePosition& tile : tiles) + result = std::min(result, getManhattanDistance(position, tile)); + return result; + } } namespace DetourNavigator @@ -114,24 +124,40 @@ namespace DetourNavigator void AsyncNavMeshUpdater::wait(Loading::Listener& listener) { + if (mSettings.get().mWaitUntilMinDistanceToPlayer == 0) + return; listener.setLabel("Building navigation mesh"); const std::size_t initialJobsLeft = getTotalJobs(); std::size_t maxProgress = initialJobsLeft + mThreads.size(); listener.setProgressRange(maxProgress); - waitUntilJobsDone(initialJobsLeft, maxProgress, listener); - mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); - listener.setProgress(maxProgress); + const int minDistanceToPlayer = waitUntilJobsDone(initialJobsLeft, maxProgress, listener); + if (minDistanceToPlayer < mSettings.get().mWaitUntilMinDistanceToPlayer) + { + mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); + listener.setProgress(maxProgress); + } } - void AsyncNavMeshUpdater::waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener) + int AsyncNavMeshUpdater::waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener) { std::size_t prevJobsLeft = initialJobsLeft; std::size_t jobsDone = 0; std::size_t jobsLeft = 0; + const int maxDistanceToPlayer = mSettings.get().mWaitUntilMinDistanceToPlayer; + const TilePosition playerPosition = *mPlayerTile.lockConst(); + int minDistanceToPlayer = 0; const auto isDone = [&] { jobsLeft = mJobs.size() + getTotalThreadJobsUnsafe(); - return jobsLeft == 0; + if (jobsLeft == 0) + { + minDistanceToPlayer = 0; + return true; + } + minDistanceToPlayer = getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed); + for (const auto& [threadId, queue] : mThreadsQueues) + minDistanceToPlayer = getMinDistanceTo(playerPosition, minDistanceToPlayer, queue.mPushed); + return minDistanceToPlayer >= maxDistanceToPlayer; }; std::unique_lock lock(mMutex); while (!mDone.wait_for(lock, std::chrono::milliseconds(250), isDone)) @@ -150,6 +176,7 @@ namespace DetourNavigator listener.increaseProgress(newJobsDone); } } + return minDistanceToPlayer; } void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index fcf57d683..c28d8f21d 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -142,7 +142,7 @@ namespace DetourNavigator void cleanupLastUpdates(); - void waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener); + int waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener); }; } diff --git a/components/detournavigator/settings.cpp b/components/detournavigator/settings.cpp index 0c4d0f838..ff99fae20 100644 --- a/components/detournavigator/settings.cpp +++ b/components/detournavigator/settings.cpp @@ -29,6 +29,7 @@ namespace DetourNavigator navigatorSettings.mRegionMergeSize = ::Settings::Manager::getInt("region merge size", "Navigator"); navigatorSettings.mRegionMinSize = ::Settings::Manager::getInt("region min size", "Navigator"); navigatorSettings.mTileSize = ::Settings::Manager::getInt("tile size", "Navigator"); + navigatorSettings.mWaitUntilMinDistanceToPlayer = ::Settings::Manager::getInt("wait until min distance to player", "Navigator"); navigatorSettings.mAsyncNavMeshUpdaterThreads = static_cast(::Settings::Manager::getInt("async nav mesh updater threads", "Navigator")); navigatorSettings.mMaxNavMeshTilesCacheSize = static_cast(::Settings::Manager::getInt("max nav mesh tiles cache size", "Navigator")); navigatorSettings.mMaxPolygonPathSize = static_cast(::Settings::Manager::getInt("max polygon path size", "Navigator")); diff --git a/components/detournavigator/settings.hpp b/components/detournavigator/settings.hpp index ece16e35a..39f5815b8 100644 --- a/components/detournavigator/settings.hpp +++ b/components/detournavigator/settings.hpp @@ -31,6 +31,7 @@ namespace DetourNavigator int mRegionMergeSize = 0; int mRegionMinSize = 0; int mTileSize = 0; + int mWaitUntilMinDistanceToPlayer = 0; std::size_t mAsyncNavMeshUpdaterThreads = 0; std::size_t mMaxNavMeshTilesCacheSize = 0; std::size_t mMaxPolygonPathSize = 0; diff --git a/docs/source/reference/modding/settings/navigator.rst b/docs/source/reference/modding/settings/navigator.rst index b9485c3e9..fee4b2626 100644 --- a/docs/source/reference/modding/settings/navigator.rst +++ b/docs/source/reference/modding/settings/navigator.rst @@ -42,6 +42,18 @@ Increasing this value may decrease performance. This condition is always true: ``max tiles number * max polygons per tile <= 4194304``. It's a limitation of `Recastnavigation `_ library. +wait until min distance to player +------------------------------ + +:Type: integer +:Range: >= 0 +:Default: 5 + +Distance in navmesh tiles around the player to keep loading screen until navigation mesh is generated. +Allows to complete cell loading only when minimal navigation mesh area is generated to correctly find path for actors +nearby the player. Increasing this value will keep loading screen longer but will slightly increase nav mesh generation +speed on systems bound by CPU. Zero means no waiting. + Advanced settings ***************** diff --git a/files/settings-default.cfg b/files/settings-default.cfg index ea678c70f..584fe5951 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -908,6 +908,10 @@ max tiles number = 512 # Min time duration for the same tile update in milliseconds (value >= 0) min update interval ms = 250 +# Keep loading screen until navmesh is generated around the player for all tiles within manhattan distance (value >= 0). +# Distance is measured in the number of tiles and can be only an integer value. +wait until min distance to player = 5 + [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.