From b6243e7d1fa1354f1eb19b731d61ce31c969ca12 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 8 Mar 2019 15:02:49 +0300 Subject: [PATCH 1/7] Fix name styleguide --- components/detournavigator/navmeshtilescache.cpp | 14 +++++++------- components/detournavigator/navmeshtilescache.hpp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index 64edab0ad0..418e69e824 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -62,8 +62,8 @@ namespace DetourNavigator return Value(); // TODO: use different function to make key to avoid unnecessary std::string allocation - const auto tile = tileValues->second.Map.find(makeNavMeshKey(recastMesh, offMeshConnections)); - if (tile == tileValues->second.Map.end()) + const auto tile = tileValues->second.mMap.find(makeNavMeshKey(recastMesh, offMeshConnections)); + if (tile == tileValues->second.mMap.end()) return Value(); acquireItemUnsafe(tile->second); @@ -96,7 +96,7 @@ namespace DetourNavigator const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, navMeshKey); // TODO: use std::string_view or some alternative to avoid navMeshKey copy into both mFreeItems and mValues - const auto emplaced = mValues[agentHalfExtents][changedTile].Map.emplace(navMeshKey, iterator); + const auto emplaced = mValues[agentHalfExtents][changedTile].mMap.emplace(navMeshKey, iterator); if (!emplaced.second) { @@ -125,16 +125,16 @@ namespace DetourNavigator if (tileValues == agentValues->second.end()) return; - const auto value = tileValues->second.Map.find(item.mNavMeshKey); - if (value == tileValues->second.Map.end()) + const auto value = tileValues->second.mMap.find(item.mNavMeshKey); + if (value == tileValues->second.mMap.end()) return; mUsedNavMeshDataSize -= getSize(item); mFreeNavMeshDataSize -= getSize(item); mFreeItems.pop_back(); - tileValues->second.Map.erase(value); - if (!tileValues->second.Map.empty()) + tileValues->second.mMap.erase(value); + if (!tileValues->second.mMap.empty()) return; agentValues->second.erase(tileValues); diff --git a/components/detournavigator/navmeshtilescache.hpp b/components/detournavigator/navmeshtilescache.hpp index 7418c4d3a2..e244cda9d7 100644 --- a/components/detournavigator/navmeshtilescache.hpp +++ b/components/detournavigator/navmeshtilescache.hpp @@ -108,7 +108,7 @@ namespace DetourNavigator struct TileMap { - std::map Map; + std::map mMap; }; std::mutex mMutex; From 14c9190f490956ae86f01ff79c7fe488247d76cf Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 8 Mar 2019 15:06:11 +0300 Subject: [PATCH 2/7] Move tile replacement into separate function --- components/detournavigator/makenavmesh.cpp | 84 ++++++++++++---------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index f1f6205c70..5b39230063 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -461,6 +461,49 @@ namespace ++power; return power; } + + dtStatus addTile(dtNavMesh& navMesh, const NavMeshData& navMeshData) + { + const dtTileRef lastRef = 0; + dtTileRef* const result = nullptr; + return navMesh.addTile(navMeshData.mValue.get(), navMeshData.mSize, + doNotTransferOwnership, lastRef, result); + } + + dtStatus addTile(dtNavMesh& navMesh, const NavMeshTilesCache::Value& cachedNavMeshData) + { + const dtTileRef lastRef = 0; + dtTileRef* const result = nullptr; + return navMesh.addTile(cachedNavMeshData.get().mValue, cachedNavMeshData.get().mSize, + doNotTransferOwnership, lastRef, result); + } + + template + UpdateNavMeshStatus replaceTile(const SharedNavMeshCacheItem& navMeshCacheItem, + const TilePosition& changedTile, T&& navMeshData) + { + const auto locked = navMeshCacheItem.lock(); + auto& navMesh = locked->getValue(); + const int layer = 0; + const auto tileRef = navMesh.getTileRefAt(changedTile.x(), changedTile.y(), layer); + unsigned char** const data = nullptr; + int* const dataSize = nullptr; + const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, data, dataSize)); + const auto addStatus = addTile(navMesh, navMeshData); + + if (dtStatusSucceed(addStatus)) + { + locked->setUsedTile(changedTile, std::forward(navMeshData)); + return makeUpdateNavMeshStatus(removed, true); + } + else + { + if (removed) + locked->removeUsedTile(changedTile); + log("failed to add tile with status=", WriteDtStatus {addStatus}); + return makeUpdateNavMeshStatus(removed, false); + } + } } namespace DetourNavigator @@ -583,47 +626,10 @@ namespace DetourNavigator if (!cachedNavMeshData) { log("cache overflow"); - - const auto locked = navMeshCacheItem.lock(); - auto& navMesh = locked->getValue(); - const auto tileRef = navMesh.getTileRefAt(x, y, 0); - const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, nullptr, nullptr)); - const auto addStatus = navMesh.addTile(navMeshData.mValue.get(), navMeshData.mSize, - doNotTransferOwnership, 0, 0); - - if (dtStatusSucceed(addStatus)) - { - locked->setUsedTile(changedTile, std::move(navMeshData)); - return makeUpdateNavMeshStatus(removed, true); - } - else - { - if (removed) - locked->removeUsedTile(changedTile); - log("failed to add tile with status=", WriteDtStatus {addStatus}); - return makeUpdateNavMeshStatus(removed, false); - } + return replaceTile(navMeshCacheItem, changedTile, std::move(navMeshData)); } } - const auto locked = navMeshCacheItem.lock(); - auto& navMesh = locked->getValue(); - const auto tileRef = navMesh.getTileRefAt(x, y, 0); - const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, nullptr, nullptr)); - const auto addStatus = navMesh.addTile(cachedNavMeshData.get().mValue, cachedNavMeshData.get().mSize, - doNotTransferOwnership, 0, 0); - - if (dtStatusSucceed(addStatus)) - { - locked->setUsedTile(changedTile, std::move(cachedNavMeshData)); - return makeUpdateNavMeshStatus(removed, true); - } - else - { - if (removed) - locked->removeUsedTile(changedTile); - log("failed to add tile with status=", WriteDtStatus {addStatus}); - return makeUpdateNavMeshStatus(removed, false); - } + return replaceTile(navMeshCacheItem, changedTile, std::move(cachedNavMeshData)); } } From 0c16fef285bdccee912887c9a027f2d4b773636c Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 8 Mar 2019 15:06:47 +0300 Subject: [PATCH 3/7] Add navmesh update status builder --- .../detournavigator/asyncnavmeshupdater.cpp | 4 +- components/detournavigator/makenavmesh.cpp | 56 ++++++++++++++----- components/detournavigator/makenavmesh.hpp | 10 ++-- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 1e158667b0..fd240e4a0b 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -32,11 +32,11 @@ namespace DetourNavigator { switch (value) { - case UpdateNavMeshStatus::ignore: + case UpdateNavMeshStatus::ignored: return stream << "ignore"; case UpdateNavMeshStatus::removed: return stream << "removed"; - case UpdateNavMeshStatus::add: + case UpdateNavMeshStatus::added: return stream << "add"; case UpdateNavMeshStatus::replaced: return stream << "replaced"; diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 5b39230063..a03e9fd8d5 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -441,17 +441,47 @@ namespace return NavMeshData(navMeshData, navMeshDataSize); } - UpdateNavMeshStatus makeUpdateNavMeshStatus(bool removed, bool add) + class UpdateNavMeshStatusBuilder { - if (removed && add) - return UpdateNavMeshStatus::replaced; - else if (removed) - return UpdateNavMeshStatus::removed; - else if (add) - return UpdateNavMeshStatus::add; - else - return UpdateNavMeshStatus::ignore; - } + public: + UpdateNavMeshStatusBuilder() = default; + + UpdateNavMeshStatusBuilder removed(bool value) + { + if (value) + set(UpdateNavMeshStatus::removed); + else + unset(UpdateNavMeshStatus::removed); + return *this; + } + + UpdateNavMeshStatusBuilder added(bool value) + { + if (value) + set(UpdateNavMeshStatus::added); + else + unset(UpdateNavMeshStatus::added); + return *this; + } + + UpdateNavMeshStatus getResult() const + { + return mResult; + } + + private: + UpdateNavMeshStatus mResult = UpdateNavMeshStatus::ignored; + + void set(UpdateNavMeshStatus value) + { + mResult = static_cast(static_cast(mResult) | static_cast(value)); + } + + void unset(UpdateNavMeshStatus value) + { + mResult = static_cast(static_cast(mResult) & ~static_cast(value)); + } + }; template unsigned long getMinValuableBitsNumber(const T value) @@ -494,14 +524,14 @@ namespace if (dtStatusSucceed(addStatus)) { locked->setUsedTile(changedTile, std::forward(navMeshData)); - return makeUpdateNavMeshStatus(removed, true); + return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult(); } else { if (removed) locked->removeUsedTile(changedTile); log("failed to add tile with status=", WriteDtStatus {addStatus}); - return makeUpdateNavMeshStatus(removed, false); + return UpdateNavMeshStatusBuilder().removed(removed).getResult(); } } } @@ -565,7 +595,7 @@ namespace DetourNavigator const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, nullptr, nullptr)); if (removed) locked->removeUsedTile(changedTile); - return makeUpdateNavMeshStatus(removed, false); + return UpdateNavMeshStatusBuilder().removed(removed).getResult(); }; if (!recastMesh) diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index 55d3e261c5..20198047c3 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -20,12 +20,12 @@ namespace DetourNavigator class RecastMesh; struct Settings; - enum class UpdateNavMeshStatus + enum class UpdateNavMeshStatus : unsigned { - ignore, - removed, - add, - replaced + ignored = 0, + removed = 1 << 0, + added = 1 << 1, + replaced = removed | added, }; inline float getLength(const osg::Vec2i& value) From b9b8ed177c389e1d4d65e9664ed7a2931f9dd75b Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 8 Mar 2019 15:23:36 +0300 Subject: [PATCH 4/7] Store priority values as named fields --- .../detournavigator/asyncnavmeshupdater.cpp | 24 +++++++++---------- .../detournavigator/asyncnavmeshupdater.hpp | 11 +++++++-- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index fd240e4a0b..ce52c1416c 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -14,16 +14,6 @@ namespace { return std::abs(lhs.x() - rhs.x()) + std::abs(lhs.y() - rhs.y()); } - - std::tuple makePriority(const TilePosition& position, const ChangeType changeType, - const TilePosition& playerTile) - { - return std::make_tuple( - changeType, - getManhattanDistance(position, playerTile), - getManhattanDistance(position, TilePosition {0, 0}) - ); - } } namespace DetourNavigator @@ -81,8 +71,18 @@ namespace DetourNavigator for (const auto& changedTile : changedTiles) { if (mPushed[agentHalfExtents].insert(changedTile.first).second) - mJobs.push(Job {agentHalfExtents, navMeshCacheItem, changedTile.first, - makePriority(changedTile.first, changedTile.second, playerTile)}); + { + Job job; + + job.mAgentHalfExtents = agentHalfExtents; + job.mNavMeshCacheItem = navMeshCacheItem; + job.mChangedTile = changedTile.first; + job.mChangeType = changedTile.second; + job.mDistanceToPlayer = getManhattanDistance(changedTile.first, playerTile); + job.mDistanceToOrigin = getManhattanDistance(changedTile.first, TilePosition {0, 0}); + + mJobs.push(std::move(job)); + } } log("posted ", mJobs.size(), " jobs"); diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 39898e48e1..da97333627 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -50,11 +50,18 @@ namespace DetourNavigator osg::Vec3f mAgentHalfExtents; SharedNavMeshCacheItem mNavMeshCacheItem; TilePosition mChangedTile; - std::tuple mPriority; + ChangeType mChangeType; + int mDistanceToPlayer; + int mDistanceToOrigin; + + std::tuple getPriority() const + { + return std::make_tuple(mChangeType, mDistanceToPlayer, mDistanceToOrigin); + } friend inline bool operator <(const Job& lhs, const Job& rhs) { - return lhs.mPriority > rhs.mPriority; + return lhs.getPriority() > rhs.getPriority(); } }; From 82e2739bae198f5854629a1a3a93f2186513f783 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 8 Mar 2019 15:27:16 +0300 Subject: [PATCH 5/7] Notify workers when only at least one job is posted --- components/detournavigator/asyncnavmeshupdater.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index ce52c1416c..6cfd75b27b 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -87,7 +87,8 @@ namespace DetourNavigator log("posted ", mJobs.size(), " jobs"); - mHasJob.notify_all(); + if (!mJobs.empty()) + mHasJob.notify_all(); } void AsyncNavMeshUpdater::wait() From ff47df4f2ccd8ff41d78db6a07ba31793ac7cc34 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 8 Mar 2019 15:28:32 +0300 Subject: [PATCH 6/7] Repost navmesh update jobs when failed because of out of memory DT_OUT_OF_MEMORY error is returned when limit of tiles is reached. --- .../detournavigator/asyncnavmeshupdater.cpp | 45 ++++++++++++++----- .../detournavigator/asyncnavmeshupdater.hpp | 9 ++-- components/detournavigator/makenavmesh.cpp | 11 ++++- components/detournavigator/makenavmesh.hpp | 9 +++- 4 files changed, 59 insertions(+), 15 deletions(-) diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 6cfd75b27b..ee3b6d77ab 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -1,4 +1,4 @@ -#include "asyncnavmeshupdater.hpp" +#include "asyncnavmeshupdater.hpp" #include "debug.hpp" #include "makenavmesh.hpp" #include "settings.hpp" @@ -30,6 +30,10 @@ namespace DetourNavigator return stream << "add"; case UpdateNavMeshStatus::replaced: return stream << "replaced"; + case UpdateNavMeshStatus::failed: + return stream << "failed"; + case UpdateNavMeshStatus::lost: + return stream << "lost"; } return stream << "unknown"; } @@ -77,6 +81,7 @@ namespace DetourNavigator job.mAgentHalfExtents = agentHalfExtents; job.mNavMeshCacheItem = navMeshCacheItem; job.mChangedTile = changedTile.first; + job.mTryNumber = 0; job.mChangeType = changedTile.second; job.mDistanceToPlayer = getManhattanDistance(changedTile.first, playerTile); job.mDistanceToOrigin = getManhattanDistance(changedTile.first, TilePosition {0, 0}); @@ -104,8 +109,9 @@ namespace DetourNavigator { try { - if (const auto job = getNextJob()) - processJob(*job); + if (auto job = getNextJob()) + if (!processJob(*job)) + repost(std::move(*job)); } catch (const std::exception& e) { @@ -115,7 +121,7 @@ namespace DetourNavigator log("stop process jobs"); } - void AsyncNavMeshUpdater::processJob(const Job& job) + bool AsyncNavMeshUpdater::processJob(const Job& job) { log("process job for agent=", job.mAgentHalfExtents); @@ -136,12 +142,16 @@ namespace DetourNavigator using FloatMs = std::chrono::duration; - const auto locked = job.mNavMeshCacheItem.lockConst(); - log("cache updated for agent=", job.mAgentHalfExtents, " status=", status, - " 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"); + { + const auto locked = job.mNavMeshCacheItem.lockConst(); + log("cache updated for agent=", job.mAgentHalfExtents, " status=", status, + " 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"); + } + + return isSuccess(status); } boost::optional AsyncNavMeshUpdater::getNextJob() @@ -194,4 +204,19 @@ namespace DetourNavigator *locked = value; return *locked.get(); } + + void AsyncNavMeshUpdater::repost(Job&& job) + { + if (mShouldStop || job.mTryNumber > 2) + return; + + const std::lock_guard lock(mMutex); + + if (mPushed[job.mAgentHalfExtents].insert(job.mChangedTile).second) + { + ++job.mTryNumber; + mJobs.push(std::move(job)); + mHasJob.notify_all(); + } + } } diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index da97333627..98359964d1 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -50,13 +50,14 @@ namespace DetourNavigator osg::Vec3f mAgentHalfExtents; SharedNavMeshCacheItem mNavMeshCacheItem; TilePosition mChangedTile; + unsigned mTryNumber; ChangeType mChangeType; int mDistanceToPlayer; int mDistanceToOrigin; - std::tuple getPriority() const + std::tuple getPriority() const { - return std::make_tuple(mChangeType, mDistanceToPlayer, mDistanceToOrigin); + return std::make_tuple(mTryNumber, mChangeType, mDistanceToPlayer, mDistanceToOrigin); } friend inline bool operator <(const Job& lhs, const Job& rhs) @@ -83,13 +84,15 @@ namespace DetourNavigator void process() throw(); - void processJob(const Job& job); + bool processJob(const Job& job); boost::optional getNextJob(); void writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const; std::chrono::steady_clock::time_point setFirstStart(const std::chrono::steady_clock::time_point& value); + + void repost(Job&& job); }; } diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index a03e9fd8d5..e62509940c 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -464,6 +464,15 @@ namespace return *this; } + UpdateNavMeshStatusBuilder failed(bool value) + { + if (value) + set(UpdateNavMeshStatus::failed); + else + unset(UpdateNavMeshStatus::failed); + return *this; + } + UpdateNavMeshStatus getResult() const { return mResult; @@ -531,7 +540,7 @@ namespace if (removed) locked->removeUsedTile(changedTile); log("failed to add tile with status=", WriteDtStatus {addStatus}); - return UpdateNavMeshStatusBuilder().removed(removed).getResult(); + return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult(); } } } diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index 20198047c3..1dfa242eed 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -26,8 +26,15 @@ namespace DetourNavigator removed = 1 << 0, added = 1 << 1, replaced = removed | added, + failed = 1 << 2, + lost = removed | failed, }; + inline bool isSuccess(UpdateNavMeshStatus value) + { + return (static_cast(value) & static_cast(UpdateNavMeshStatus::failed)) == 0; + } + inline float getLength(const osg::Vec2i& value) { return std::sqrt(float(osg::square(value.x()) + osg::square(value.y()))); @@ -41,7 +48,7 @@ namespace DetourNavigator inline bool shouldAddTile(const TilePosition& changedTile, const TilePosition& playerTile, int maxTiles) { const auto expectedTilesCount = std::ceil(osg::PI * osg::square(getDistance(changedTile, playerTile))); - return expectedTilesCount * 3 <= maxTiles; + return expectedTilesCount <= maxTiles; } NavMeshPtr makeEmptyNavMesh(const Settings& settings); From f2e47d640d15dc5b1e96f34dd1ca18245ad8d0b0 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 22 Feb 2019 01:22:10 +0300 Subject: [PATCH 7/7] Add option to limit max number of navmesh tiles --- .../detournavigator/navigator.cpp | 1 + components/detournavigator/makenavmesh.cpp | 2 +- components/detournavigator/navmeshmanager.cpp | 2 +- components/detournavigator/settings.cpp | 1 + components/detournavigator/settings.hpp | 1 + .../reference/modding/settings/navigator.rst | 24 +++++++++++++++++++ files/settings-default.cfg | 3 +++ 7 files changed, 32 insertions(+), 2 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 42b69d7f91..9daef6f64e 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -61,6 +61,7 @@ namespace mSettings.mMaxSmoothPathSize = 1024; mSettings.mTrianglesPerChunk = 256; mSettings.mMaxPolys = 4096; + mSettings.mMaxTilesNumber = 512; mNavigator.reset(new NavigatorImpl(mSettings)); } }; diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index e62509940c..5ac28127ec 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -628,7 +628,7 @@ namespace DetourNavigator return removeTile(); } - if (!shouldAddTile(changedTile, playerTile, params.maxTiles)) + if (!shouldAddTile(changedTile, playerTile, std::min(settings.mMaxTilesNumber, params.maxTiles))) { log("ignore add tile: too far from player"); return removeTile(); diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 2a29d1dd4a..217c17e410 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -157,7 +157,7 @@ namespace DetourNavigator if (changedTiles->second.empty()) mChangedTiles.erase(changedTiles); } - const auto maxTiles = navMesh.getParams()->maxTiles; + const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles); mRecastMeshManager.forEachTilePosition([&] (const TilePosition& tile) { if (tilesToPost.count(tile)) diff --git a/components/detournavigator/settings.cpp b/components/detournavigator/settings.cpp index ddffd0d1f5..735194dbaf 100644 --- a/components/detournavigator/settings.cpp +++ b/components/detournavigator/settings.cpp @@ -24,6 +24,7 @@ namespace DetourNavigator navigatorSettings.mMaxEdgeLen = ::Settings::Manager::getInt("max edge len", "Navigator"); navigatorSettings.mMaxNavMeshQueryNodes = ::Settings::Manager::getInt("max nav mesh query nodes", "Navigator"); navigatorSettings.mMaxPolys = ::Settings::Manager::getInt("max polygons per tile", "Navigator"); + navigatorSettings.mMaxTilesNumber = ::Settings::Manager::getInt("max tiles number", "Navigator"); navigatorSettings.mMaxVertsPerPoly = ::Settings::Manager::getInt("max verts per poly", "Navigator"); navigatorSettings.mRegionMergeSize = ::Settings::Manager::getInt("region merge size", "Navigator"); navigatorSettings.mRegionMinSize = ::Settings::Manager::getInt("region min size", "Navigator"); diff --git a/components/detournavigator/settings.hpp b/components/detournavigator/settings.hpp index 0316092a0e..dc0e5dc5a0 100644 --- a/components/detournavigator/settings.hpp +++ b/components/detournavigator/settings.hpp @@ -26,6 +26,7 @@ namespace DetourNavigator int mMaxEdgeLen = 0; int mMaxNavMeshQueryNodes = 0; int mMaxPolys = 0; + int mMaxTilesNumber = 0; int mMaxVertsPerPoly = 0; int mRegionMergeSize = 0; int mRegionMinSize = 0; diff --git a/docs/source/reference/modding/settings/navigator.rst b/docs/source/reference/modding/settings/navigator.rst index 1b65c35a85..d9ddf5381a 100644 --- a/docs/source/reference/modding/settings/navigator.rst +++ b/docs/source/reference/modding/settings/navigator.rst @@ -24,6 +24,24 @@ Moving across external world, entering/exiting location produce nav mesh update. NPC and creatures may not be able to find path before nav mesh is built around them. Try to disable this if you want to have old fashioned AI which doesn't know where to go when you stand behind that stone and casting a firebolt. +max tiles number +---------------- + +:Type: integer +:Range: >= 0 +:Default: 512 + +Number of tiles at nav mesh. +Nav mesh covers circle area around player. +This option allows to set an explicit limit for nav mesh size, how many tiles should fit into circle. +If actor is inside this area it able to find path over nav mesh. +Increasing this value may decrease performance. + +.. note:: + Don't expect infinite nav mesh size increasing. + This condition is always true: ``max tiles number * max polygons per tile <= 4194304``. + It's a limitation of `Recastnavigation `_ library. + Advanced settings ***************** @@ -322,6 +340,12 @@ Maximum number of polygons per nav mesh tile. Maximum number of nav mesh tiles d this value. 22 bits is a limit to store both tile identifier and polygon identifier (tiles = 2^(22 - log2(polygons))). See `recastnavigation `_ for more details. +.. Warning:: + Lower value may lead to ignored world geometry on nav mesh. + Greater value will reduce number of nav mesh tiles. + This condition is always true: ``max tiles number * max polygons per tile <= 4194304``. + It's a limitation of `Recastnavigation `_ library. + max verts per poly ------------------ diff --git a/files/settings-default.cfg b/files/settings-default.cfg index e3920a4bf9..e95646602d 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -670,6 +670,9 @@ enable nav mesh render = false # Render agents paths (true, false) enable agents paths render = false +# Max number of navmesh tiles (value >= 0) +max tiles number = 512 + [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.