From 430ba9d7a55fa0d7131fb18c2aadf17441364a83 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 1 Apr 2018 03:44:16 +0300 Subject: [PATCH] Build NavMesh tile data only for changed tiles --- .../detournavigator/asyncnavmeshupdater.cpp | 31 ++--- .../detournavigator/asyncnavmeshupdater.hpp | 14 +-- .../cachedrecastmeshmanager.cpp | 12 +- .../cachedrecastmeshmanager.hpp | 2 +- components/detournavigator/makenavmesh.cpp | 110 ++++++++++++++---- components/detournavigator/makenavmesh.hpp | 12 +- components/detournavigator/navmeshmanager.cpp | 83 +++++++++++-- components/detournavigator/navmeshmanager.hpp | 9 ++ .../detournavigator/recastmeshmanager.cpp | 14 ++- .../detournavigator/recastmeshmanager.hpp | 16 +-- 10 files changed, 231 insertions(+), 72 deletions(-) diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 3a95f19d9..ba4bd3ab8 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -10,8 +10,7 @@ namespace DetourNavigator { AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings) - : mSettings(settings) - , mMaxRevision(0) + : mSettings(std::cref(settings)) , mShouldStop() , mThread([&] { process(); }) { @@ -28,10 +27,19 @@ namespace DetourNavigator } void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr& recastMesh, - const std::shared_ptr& navMeshCacheItem) + const std::shared_ptr& navMeshCacheItem, std::set&& changedTiles) { const std::lock_guard lock(mMutex); - mJobs[agentHalfExtents] = Job {agentHalfExtents, recastMesh, navMeshCacheItem}; + const auto job = mJobs.find(agentHalfExtents); + if (job == mJobs.end() || job->second.mChangedTiles.empty()) + { + mJobs[agentHalfExtents] = Job {agentHalfExtents, recastMesh, navMeshCacheItem, std::move(changedTiles)}; + } + else + { + job->second.mRecastMesh = recastMesh; + job->second.mChangedTiles.insert(changedTiles.begin(), changedTiles.end()); + } mHasJob.notify_all(); } @@ -62,18 +70,15 @@ namespace DetourNavigator void AsyncNavMeshUpdater::processJob(const Job& job) { - log("process job for agent=", job.mAgentHalfExtents, - " revision=", job.mNavMeshCacheItem->mRevision, - " max_revision=", mMaxRevision); - - if (job.mNavMeshCacheItem->mRevision < mMaxRevision) - return; - - mMaxRevision = job.mNavMeshCacheItem->mRevision; + log("process job for agent=", job.mAgentHalfExtents); const auto start = std::chrono::steady_clock::now(); - job.mNavMeshCacheItem->mValue = makeNavMesh(job.mAgentHalfExtents, *job.mRecastMesh, mSettings); + if (job.mNavMeshCacheItem->mValue && !job.mChangedTiles.empty()) + updateNavMesh(job.mAgentHalfExtents, *job.mRecastMesh, job.mChangedTiles, mSettings, + *job.mNavMeshCacheItem->mValue); + else + job.mNavMeshCacheItem->mValue = makeNavMesh(job.mAgentHalfExtents, *job.mRecastMesh, mSettings); const auto finish = std::chrono::steady_clock::now(); diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index cdcc3432c..ec8f0e9de 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -2,6 +2,7 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_ASYNCNAVMESHUPDATER_H #include "recastmesh.hpp" +#include "tileposition.hpp" #include @@ -12,22 +13,19 @@ #include #include #include +#include #include class dtNavMesh; namespace DetourNavigator { - using NavMeshConstPtr = std::shared_ptr; + using NavMeshPtr = std::shared_ptr; struct NavMeshCacheItem { - NavMeshConstPtr mValue = nullptr; + NavMeshPtr mValue; std::size_t mRevision; - - NavMeshCacheItem(std::size_t mRevision) - : mRevision(mRevision) - {} }; class AsyncNavMeshUpdater @@ -37,7 +35,7 @@ namespace DetourNavigator ~AsyncNavMeshUpdater(); void post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr& recastMesh, - const std::shared_ptr& navMeshCacheItem); + const std::shared_ptr& mNavMeshCacheItem, std::set&& changedTiles); void wait(); @@ -47,12 +45,12 @@ namespace DetourNavigator osg::Vec3f mAgentHalfExtents; std::shared_ptr mRecastMesh; std::shared_ptr mNavMeshCacheItem; + std::set mChangedTiles; }; using Jobs = std::map; std::reference_wrapper mSettings; - std::atomic_size_t mMaxRevision; std::atomic_bool mShouldStop; std::mutex mMutex; std::condition_variable mHasJob; diff --git a/components/detournavigator/cachedrecastmeshmanager.cpp b/components/detournavigator/cachedrecastmeshmanager.cpp index 126732038..0fcaf116e 100644 --- a/components/detournavigator/cachedrecastmeshmanager.cpp +++ b/components/detournavigator/cachedrecastmeshmanager.cpp @@ -8,12 +8,12 @@ namespace DetourNavigator { } - bool CachedRecastMeshManager::removeObject(std::size_t id) + boost::optional CachedRecastMeshManager::removeObject(std::size_t id) { - if (!mImpl.removeObject(id)) - return false; - mCached.reset(); - return true; + const auto object = mImpl.removeObject(id); + if (object) + mCached.reset(); + return object; } std::shared_ptr CachedRecastMeshManager::getMesh() @@ -21,5 +21,5 @@ namespace DetourNavigator if (!mCached) mCached = mImpl.getMesh(); return mCached; - } +} } diff --git a/components/detournavigator/cachedrecastmeshmanager.hpp b/components/detournavigator/cachedrecastmeshmanager.hpp index 3cfe9a56f..4be6fb134 100644 --- a/components/detournavigator/cachedrecastmeshmanager.hpp +++ b/components/detournavigator/cachedrecastmeshmanager.hpp @@ -21,7 +21,7 @@ namespace DetourNavigator return true; } - bool removeObject(std::size_t id); + boost::optional removeObject(std::size_t id); std::shared_ptr getMesh(); diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index ef9b06374..cac6d0952 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -5,13 +5,16 @@ #include "recastmesh.hpp" #include "settings.hpp" #include "settingsutils.hpp" +#include "debug.hpp" #include #include #include #include -#include +#include +#include +#include namespace { @@ -211,22 +214,57 @@ namespace return NavMeshData(navMeshData, navMeshDataSize); } +} - int nextPow2(int v) +namespace DetourNavigator +{ + NavMeshPtr makeEmptyNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, + const Settings& settings) { - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; + log("build empty NavMesh:", + " agentHeight=", std::setprecision(std::numeric_limits::max_exponent10), + getHeight(settings, agentHalfExtents), + " agentMaxClimb=", std::setprecision(std::numeric_limits::max_exponent10), + getMaxClimb(settings), + " agentRadius=", std::setprecision(std::numeric_limits::max_exponent10), + getRadius(settings, agentHalfExtents)); + + osg::Vec3f boundsMin; + osg::Vec3f boundsMax; + rcCalcBounds(recastMesh.getVertices().data(), static_cast(recastMesh.getVerticesCount()), + boundsMin.ptr(), boundsMax.ptr()); + + // Max tiles and max polys affect how the tile IDs are caculated. + // There are 22 bits available for identifying a tile and a polygon. + const auto tileBits = 10; + const auto polyBits = 22 - tileBits; + const auto maxTiles = 1 << tileBits; + const auto maxPolysPerTile = 1 << polyBits; + + dtNavMeshParams params; + rcVcopy(params.orig, boundsMin.ptr()); + params.tileWidth = settings.mTileSize * settings.mCellSize; + params.tileHeight = settings.mTileSize * settings.mCellSize; + params.maxTiles = maxTiles; + params.maxPolys = maxPolysPerTile; + + NavMeshPtr navMesh(dtAllocNavMesh(), &dtFreeNavMesh); + OPENMW_CHECK_DT_STATUS(navMesh->init(¶ms)); + + return navMesh; } - NavMeshPtr makeNavMeshWithMultiTiles(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, + NavMeshPtr makeNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, const Settings& settings) { + log("build NavMesh with mutiple tiles:", + " agentHeight=", std::setprecision(std::numeric_limits::max_exponent10), + getHeight(settings, agentHalfExtents), + " agentMaxClimb=", std::setprecision(std::numeric_limits::max_exponent10), + getMaxClimb(settings), + " agentRadius=", std::setprecision(std::numeric_limits::max_exponent10), + getRadius(settings, agentHalfExtents)); + osg::Vec3f boundsMin; osg::Vec3f boundsMax; rcCalcBounds(recastMesh.getVertices().data(), static_cast(recastMesh.getVerticesCount()), @@ -235,12 +273,9 @@ namespace const auto minTilePosition = getTilePosition(settings, boundsMin); const auto maxTilePosition = getTilePosition(settings, boundsMax); - const auto tileWidth = maxTilePosition.x() - minTilePosition.x() + 1; - const auto tileHeight = maxTilePosition.y() - minTilePosition.y() + 1; - // Max tiles and max polys affect how the tile IDs are caculated. // There are 22 bits available for identifying a tile and a polygon. - const auto tileBits = std::min(static_cast(std::log2(nextPow2(tileWidth * tileHeight))), 14); + const auto tileBits = 10; const auto polyBits = 22 - tileBits; const auto maxTiles = 1 << tileBits; const auto maxPolysPerTile = 1 << polyBits; @@ -277,12 +312,47 @@ namespace return navMesh; } -} -namespace DetourNavigator -{ - NavMeshPtr makeNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, const Settings& settings) + void updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, + const std::set& changedTiles, const Settings& settings, dtNavMesh& navMesh) { - return makeNavMeshWithMultiTiles(agentHalfExtents, recastMesh, settings); + log("update NavMesh with mutiple tiles:", + " agentHeight=", std::setprecision(std::numeric_limits::max_exponent10), + getHeight(settings, agentHalfExtents), + " agentMaxClimb=", std::setprecision(std::numeric_limits::max_exponent10), + getMaxClimb(settings), + " agentRadius=", std::setprecision(std::numeric_limits::max_exponent10), + getRadius(settings, agentHalfExtents), + " changedTiles.size()=", changedTiles.size()); + + osg::Vec3f boundsMin; + osg::Vec3f boundsMax; + rcCalcBounds(recastMesh.getVertices().data(), int(recastMesh.getVerticesCount()), + boundsMin.ptr(), boundsMax.ptr()); + + const auto& params = *navMesh.getParams(); + const osg::Vec3f origin(params.orig[0], params.orig[1], params.orig[2]); + + for (const auto& tilePosition : changedTiles) + { + const auto x = tilePosition.x(); + const auto y = tilePosition.y(); + + navMesh.removeTile(navMesh.getTileRefAt(x, y, 0), nullptr, nullptr); + + const auto tileBounds = makeTileBounds(settings, tilePosition); + 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()); + + auto navMeshData = makeNavMeshTileData(agentHalfExtents, recastMesh, x, y, + tileBorderMin, tileBorderMax, settings); + + if (!navMeshData.mValue) + continue; + + OPENMW_CHECK_DT_STATUS(navMesh.addTile(navMeshData.mValue.get(), navMeshData.mSize, + DT_TILE_FREE_DATA, 0, 0)); + navMeshData.mValue.release(); + } } } diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index a94e71c3f..96521608b 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -1,9 +1,12 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_MAKENAVMESH_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_MAKENAVMESH_H +#include "tileposition.hpp" + #include #include +#include class dtNavMesh; @@ -14,7 +17,14 @@ namespace DetourNavigator using NavMeshPtr = std::shared_ptr; - NavMeshPtr makeNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, const Settings& settings); + NavMeshPtr makeEmptyNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, + const Settings& settings); + + NavMeshPtr makeNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, + const Settings& settings); + + void updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, + const std::set& changedTiles, const Settings& settings, dtNavMesh& navMesh); } #endif diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 55e0236ed..d423f2120 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -1,21 +1,29 @@ #include "navmeshmanager.hpp" #include "debug.hpp" +#include "makenavmesh.hpp" +#include "settings.hpp" + +#include + +#include #include namespace DetourNavigator { NavMeshManager::NavMeshManager(const Settings& settings) - : mRecastMeshManager(settings) + : mSettings(settings) + , mRecastMeshManager(settings) , mAsyncNavMeshUpdater(settings) - { - } + {} bool NavMeshManager::removeObject(std::size_t id) { - if (!mRecastMeshManager.removeObject(id)) + const auto object = mRecastMeshManager.removeObject(id); + if (!object) return false; ++mRevision; + addChangedTiles(*object->mShape, object->mTransform); return true; } @@ -26,13 +34,28 @@ namespace DetourNavigator void NavMeshManager::update(const osg::Vec3f& agentHalfExtents) { - auto it = mCache.find(agentHalfExtents); - if (it == mCache.end()) - it = mCache.insert(std::make_pair(agentHalfExtents, std::make_shared(mRevision))).first; - else if (it->second->mRevision >= mRevision) + auto cached = mCache.find(agentHalfExtents); + if (cached == mCache.end()) + cached = mCache.insert(std::make_pair(agentHalfExtents, + std::make_shared(NavMeshCacheItem { + makeEmptyNavMesh(agentHalfExtents, *mRecastMeshManager.getMesh(), mSettings), + mRevision + }))).first; + else if (cached->second->mRevision >= mRevision) return; - it->second->mRevision = mRevision; - mAsyncNavMeshUpdater.post(agentHalfExtents, mRecastMeshManager.getMesh(), it->second); + cached->second->mRevision = mRevision; + const auto changedTiles = mChangedTiles.find(agentHalfExtents); + if (changedTiles == mChangedTiles.end()) + { + mAsyncNavMeshUpdater.post(agentHalfExtents, mRecastMeshManager.getMesh(), cached->second, + std::set()); + } + else + { + mAsyncNavMeshUpdater.post(agentHalfExtents, mRecastMeshManager.getMesh(), cached->second, + std::move(changedTiles->second)); + mChangedTiles.erase(changedTiles); + } } void NavMeshManager::wait() @@ -47,4 +70,44 @@ namespace DetourNavigator return nullptr; return it->second->mValue; } + + void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform) + { + btVector3 aabbMin; + btVector3 aabbMax; + shape.getAabb(transform, aabbMin, aabbMax); + osg::Vec3f min(aabbMin.x(), aabbMin.z(), aabbMin.y()); + osg::Vec3f max(aabbMax.x(), aabbMax.z(), aabbMax.y()); + min *= mSettings.mRecastScaleFactor; + max *= mSettings.mRecastScaleFactor; + + for (auto& v : mCache) + { + if (const auto& item = v.second) + { + if (const auto& navMesh = item->mValue) + { + auto& changedTiles = mChangedTiles[v.first]; + + int minTileX; + int minTileY; + navMesh->calcTileLoc(min.ptr(), &minTileX, &minTileY); + + int maxTileX; + int maxTileY; + navMesh->calcTileLoc(max.ptr(), &maxTileX, &maxTileY); + + if (minTileX > maxTileX) + std::swap(minTileX, maxTileX); + + if (minTileY > maxTileY) + std::swap(minTileY, maxTileY); + + for (int tileX = minTileX; tileX <= maxTileX; ++tileX) + for (int tileY = minTileY; tileY <= maxTileY; ++tileY) + changedTiles.insert(TilePosition {tileX, tileY}); + } + } + } + } } diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index 02309c637..40440bafa 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -4,6 +4,8 @@ #include "asyncnavmeshupdater.hpp" #include "cachedrecastmeshmanager.hpp" +#include + #include #include @@ -13,6 +15,8 @@ class dtNavMesh; namespace DetourNavigator { + using NavMeshConstPtr = std::shared_ptr; + class NavMeshManager { public: @@ -24,6 +28,7 @@ namespace DetourNavigator if (!mRecastMeshManager.addObject(id, shape, transform)) return false; ++mRevision; + addChangedTiles(shape, transform); return true; } @@ -39,9 +44,13 @@ namespace DetourNavigator private: std::size_t mRevision = 0; + const Settings& mSettings; CachedRecastMeshManager mRecastMeshManager; std::map> mCache; + std::map> mChangedTiles; AsyncNavMeshUpdater mAsyncNavMeshUpdater; + + void addChangedTiles(const btCollisionShape& shape, const btTransform& transform); }; } diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index 25b3bda96..8568c6e14 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -7,8 +7,7 @@ namespace DetourNavigator RecastMeshManager::RecastMeshManager(const Settings& settings) : mShouldRebuild(false) , mMeshBuilder(settings) - { - } + {} bool RecastMeshManager::addObject(std::size_t id, const btHeightfieldTerrainShape& shape, const btTransform& transform) { @@ -26,12 +25,15 @@ namespace DetourNavigator return true; } - bool RecastMeshManager::removeObject(std::size_t id) + boost::optional RecastMeshManager::removeObject(std::size_t id) { - if (!mObjects.erase(id)) - return false; + const auto object = mObjects.find(id); + if (object == mObjects.end()) + return boost::none; + const auto result = object->second; + mObjects.erase(object); mShouldRebuild = true; - return true; + return result; } std::shared_ptr RecastMeshManager::getMesh() diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index 12c7b391b..b4a57ffe0 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -5,6 +5,8 @@ #include +#include + #include class btCollisionShape; @@ -14,23 +16,23 @@ namespace DetourNavigator class RecastMeshManager { public: + struct Object + { + const btCollisionShape* mShape; + btTransform mTransform; + }; + RecastMeshManager(const Settings& settings); bool addObject(std::size_t id, const btHeightfieldTerrainShape& shape, const btTransform& transform); bool addObject(std::size_t id, const btConcaveShape& shape, const btTransform& transform); - bool removeObject(std::size_t id); + boost::optional removeObject(std::size_t id); std::shared_ptr getMesh(); private: - struct Object - { - const btCollisionShape* mShape; - btTransform mTransform; - }; - bool mShouldRebuild; RecastMeshBuilder mMeshBuilder; std::unordered_map mObjects;