From 4aba0fa85fde3825df7db2c2abe4021562a9cfc4 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 21 Apr 2018 02:57:01 +0300 Subject: [PATCH] Limit number of NavMesh tiles to add by distance from player tile --- apps/openmw/mwworld/scene.cpp | 4 ++ .../detournavigator/asyncnavmeshupdater.cpp | 37 ++++++++++++---- .../detournavigator/asyncnavmeshupdater.hpp | 9 +++- .../detournavigator/gettilespositions.hpp | 1 + components/detournavigator/makenavmesh.cpp | 15 +++++-- components/detournavigator/makenavmesh.hpp | 19 ++++++++- components/detournavigator/navmeshmanager.cpp | 42 +++++++++++++++---- components/detournavigator/navmeshmanager.hpp | 2 + components/detournavigator/settingsutils.hpp | 2 + .../tilecachedrecastmeshmanager.cpp | 15 +++++++ .../tilecachedrecastmeshmanager.hpp | 13 ++++++ 11 files changed, 140 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 026d067dd8..32c83b95e3 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -390,6 +390,10 @@ namespace MWWorld void Scene::playerMoved(const osg::Vec3f &pos) { + const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); + const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + navigator->update(player.getRefData().getPosition().asVec3()); + if (!mCurrentCell || !mCurrentCell->isExterior()) return; diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index fcfc342ebf..20438674af 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -64,13 +64,19 @@ namespace DetourNavigator { log("post jobs playerTile=", playerTile); + setPlayerTile(playerTile); + if (changedTiles.empty()) return; const std::lock_guard lock(mMutex); for (const auto& changedTile : changedTiles) - mJobs.push(Job {agentHalfExtents, navMeshCacheItem, changedTile, makePriority(changedTile, playerTile)}); + { + if (mPushed[agentHalfExtents].insert(changedTile).second) + mJobs.push(Job {agentHalfExtents, navMeshCacheItem, changedTile, + makePriority(changedTile, playerTile)}); + } mHasJob.notify_all(); } @@ -109,13 +115,14 @@ namespace DetourNavigator setFirstStart(start); const auto recastMesh = mRecastMeshManager.get().getMesh(job.mChangedTile); + const auto playerTile = getPlayerTile(); - const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, mSettings, - *job.mNavMeshCacheItem); + const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile, + mSettings, *job.mNavMeshCacheItem); const auto finish = std::chrono::steady_clock::now(); - writeDebugFiles(job, *recastMesh); + writeDebugFiles(job, recastMesh.get()); using FloatMs = std::chrono::duration; @@ -139,10 +146,14 @@ namespace DetourNavigator log("got ", mJobs.size(), " jobs"); const auto job = mJobs.top(); mJobs.pop(); + const auto pushed = mPushed.find(job.mAgentHalfExtents); + pushed->second.erase(job.mChangedTile); + if (pushed->second.empty()) + mPushed.erase(pushed); return job; } - void AsyncNavMeshUpdater::writeDebugFiles(const Job& job, const RecastMesh& recastMesh) const + void AsyncNavMeshUpdater::writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const { std::string revision; std::string recastMeshRevision; @@ -157,8 +168,8 @@ namespace DetourNavigator if (mSettings.get().mEnableNavMeshFileNameRevision) navMeshRevision = revision; } - if (mSettings.get().mEnableWriteRecastMeshToFile) - writeToFile(recastMesh, mSettings.get().mRecastMeshPathPrefix, recastMeshRevision); + if (recastMesh && mSettings.get().mEnableWriteRecastMeshToFile) + writeToFile(*recastMesh, mSettings.get().mRecastMeshPathPrefix, recastMeshRevision); if (mSettings.get().mEnableWriteNavMeshToFile) writeToFile(*job.mNavMeshCacheItem->mValue.lock(), mSettings.get().mNavMeshPathPrefix, navMeshRevision); } @@ -175,4 +186,16 @@ namespace DetourNavigator if (!mFirstStart) mFirstStart = value; } + + TilePosition AsyncNavMeshUpdater::getPlayerTile() + { + const std::lock_guard lock(mPlayerTileMutex); + return mPlayerTile; + } + + void AsyncNavMeshUpdater::setPlayerTile(const TilePosition& value) + { + const std::lock_guard lock(mPlayerTileMutex); + mPlayerTile = value; + } } diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index e31446a92a..6b4facf6de 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -56,6 +56,9 @@ namespace DetourNavigator std::condition_variable mHasJob; std::condition_variable mDone; Jobs mJobs; + std::map> mPushed; + std::mutex mPlayerTileMutex; + TilePosition mPlayerTile; std::mutex mFirstStartMutex; boost::optional mFirstStart; std::thread mThread; @@ -68,11 +71,15 @@ namespace DetourNavigator void notifyHasJob(); - void writeDebugFiles(const Job& job, const RecastMesh& recastMesh) const; + void writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const; std::chrono::steady_clock::time_point getFirstStart(); void setFirstStart(const std::chrono::steady_clock::time_point& value); + + TilePosition getPlayerTile(); + + void setPlayerTile(const TilePosition& value); }; } diff --git a/components/detournavigator/gettilespositions.hpp b/components/detournavigator/gettilespositions.hpp index 6f5a372600..b52984452a 100644 --- a/components/detournavigator/gettilespositions.hpp +++ b/components/detournavigator/gettilespositions.hpp @@ -3,6 +3,7 @@ #include "settings.hpp" #include "settingsutils.hpp" +#include "tileposition.hpp" #include diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 4d54eac6cb..8db1d32b49 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -270,8 +270,8 @@ namespace DetourNavigator return navMesh; } - UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, - const RecastMesh* recastMesh, const TilePosition& changedTile, const Settings& settings, + UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh, + const TilePosition& changedTile, const TilePosition& playerTile, const Settings& settings, NavMeshCacheItem& navMeshCacheItem) { log("update NavMesh with mutiple tiles:", @@ -281,7 +281,9 @@ namespace DetourNavigator getMaxClimb(settings), " agentRadius=", std::setprecision(std::numeric_limits::max_exponent10), getRadius(settings, agentHalfExtents), - " changedTile=", changedTile); + " changedTile=", changedTile, + " playerTile=", playerTile, + " changedTileDistance=", getDistance(changedTile, playerTile)); auto& navMesh = navMeshCacheItem.mValue; const auto& params = *navMesh.lock()->getParams(); @@ -315,6 +317,13 @@ namespace DetourNavigator return makeUpdateNavMeshStatus(removed, false); } + const auto maxTiles = navMesh.lock()->getParams()->maxTiles; + if (!shouldAddTile(changedTile, playerTile, maxTiles)) + { + log("ignore add tile: too far from player"); + return makeUpdateNavMeshStatus(removed, false); + } + const auto tileBounds = makeTileBounds(settings, changedTile); const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), boundsMin.y() - 1, tileBounds.mMin.y()); const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), boundsMax.y() + 1, tileBounds.mMax.y()); diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index c2767dc75a..cc9e6d8559 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -29,10 +29,27 @@ namespace DetourNavigator replaced }; + inline float getLength(const osg::Vec2i& value) + { + return std::sqrt(float(osg::square(value.x()) + osg::square(value.y()))); + } + + inline float getDistance(const TilePosition& lhs, const TilePosition& rhs) + { + return getLength(lhs - rhs); + } + + 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; + } + NavMeshPtr makeEmptyNavMesh(const Settings& settings); UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh, - const TilePosition& changedTile, const Settings& settings, NavMeshCacheItem& navMeshCacheItem); + const TilePosition& changedTile, const TilePosition& playerTile, const Settings& settings, + NavMeshCacheItem& navMeshCacheItem); } #endif diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index a9fb700b70..5f6cdb93c3 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -56,17 +56,45 @@ namespace DetourNavigator void NavMeshManager::update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents) { + playerPosition *= mSettings.mRecastScaleFactor; + std::swap(playerPosition.y(), playerPosition.z()); + const auto playerTile = getTilePosition(mSettings, playerPosition); + if (mLastRecastMeshManagerRevision >= mRecastMeshManager.getRevision() && mPlayerTile + && *mPlayerTile == playerTile) + return; + mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision(); + mPlayerTile = playerTile; + std::set tilesToPost; const auto& cached = getCached(agentHalfExtents); const auto changedTiles = mChangedTiles.find(agentHalfExtents); - if (changedTiles != mChangedTiles.end()) { - playerPosition *= mSettings.mRecastScaleFactor; - std::swap(playerPosition.y(), playerPosition.z()); - mAsyncNavMeshUpdater.post(agentHalfExtents, cached, getTilePosition(mSettings, playerPosition), - changedTiles->second); - log("cache update posted for agent=", agentHalfExtents, " changedTiles=", changedTiles->second.size()); - mChangedTiles.erase(changedTiles); + const auto locked = cached->mValue.lock(); + if (changedTiles != mChangedTiles.end()) + { + for (const auto& tile : changedTiles->second) + if (locked->getTileAt(tile.x(), tile.y(), 0)) + tilesToPost.insert(tile); + for (const auto& tile : tilesToPost) + changedTiles->second.erase(tile); + if (changedTiles->second.empty()) + mChangedTiles.erase(changedTiles); + } + const auto maxTiles = locked->getParams()->maxTiles; + mRecastMeshManager.forEachTilePosition([&] (const TilePosition& tile) + { + if (tilesToPost.count(tile)) + return; + const auto shouldAdd = shouldAddTile(tile, playerTile, maxTiles); + const auto presentInNavMesh = bool(locked->getTileAt(tile.x(), tile.y(), 0)); + if ((shouldAdd && !presentInNavMesh) || (!shouldAdd && presentInNavMesh)) + tilesToPost.insert(tile); + }); } + mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, tilesToPost); + log("cache update posted for agent=", agentHalfExtents, + " playerTile=", *mPlayerTile, + " recastMeshManagerRevision=", mLastRecastMeshManagerRevision, + " changedTiles=", changedTiles->second.size()); } void NavMeshManager::wait() diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index 21328454e2..f0b273efd0 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -46,6 +46,8 @@ namespace DetourNavigator std::map> mChangedTiles; AsyncNavMeshUpdater mAsyncNavMeshUpdater; std::size_t mGenerationCounter = 0; + boost::optional mPlayerTile; + std::size_t mLastRecastMeshManagerRevision = 0; void addChangedTiles(const btCollisionShape& shape, const btTransform& transform); diff --git a/components/detournavigator/settingsutils.hpp b/components/detournavigator/settingsutils.hpp index 0b327717f5..8127536862 100644 --- a/components/detournavigator/settingsutils.hpp +++ b/components/detournavigator/settingsutils.hpp @@ -6,6 +6,8 @@ #include "tileposition.hpp" #include "tilebounds.hpp" +#include +#include #include #include diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index 9c4f63e59f..eb4dffcb6b 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -35,6 +35,8 @@ namespace DetourNavigator result = true; } }); + if (result) + ++mRevision; return result; } @@ -57,6 +59,8 @@ namespace DetourNavigator if (tileResult && !result) result = tileResult; } + if (result) + ++mRevision; return result; } @@ -68,4 +72,15 @@ namespace DetourNavigator return nullptr; return it->second.getMesh(); } + + bool TileCachedRecastMeshManager::hasTile(const TilePosition& tilePosition) + { + const std::lock_guard lock(mTilesMutex); + return mTiles.count(tilePosition); + } + + std::size_t TileCachedRecastMeshManager::getRevision() const + { + return mRevision; + } } diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index f957dec1dc..1c685b2ec6 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -20,11 +20,24 @@ namespace DetourNavigator std::shared_ptr getMesh(const TilePosition& tilePosition); + bool hasTile(const TilePosition& tilePosition); + + template + void forEachTilePosition(Function&& function) + { + const std::lock_guard lock(mTilesMutex); + for (const auto& tile : mTiles) + function(tile.first); + } + + std::size_t getRevision() const; + private: const Settings& mSettings; std::mutex mTilesMutex; std::map mTiles; std::unordered_map> mObjectsTilesPositions; + std::size_t mRevision = 0; }; }