mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 18:59:57 +00:00
Limit number of NavMesh tiles to add by distance from player tile
This commit is contained in:
parent
d2fd9abd51
commit
4aba0fa85f
11 changed files with 140 additions and 19 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -64,13 +64,19 @@ namespace DetourNavigator
|
|||
{
|
||||
log("post jobs playerTile=", playerTile);
|
||||
|
||||
setPlayerTile(playerTile);
|
||||
|
||||
if (changedTiles.empty())
|
||||
return;
|
||||
|
||||
const std::lock_guard<std::mutex> 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<float, std::milli>;
|
||||
|
||||
|
@ -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<std::mutex> lock(mPlayerTileMutex);
|
||||
return mPlayerTile;
|
||||
}
|
||||
|
||||
void AsyncNavMeshUpdater::setPlayerTile(const TilePosition& value)
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mPlayerTileMutex);
|
||||
mPlayerTile = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,9 @@ namespace DetourNavigator
|
|||
std::condition_variable mHasJob;
|
||||
std::condition_variable mDone;
|
||||
Jobs mJobs;
|
||||
std::map<osg::Vec3f, std::set<TilePosition>> mPushed;
|
||||
std::mutex mPlayerTileMutex;
|
||||
TilePosition mPlayerTile;
|
||||
std::mutex mFirstStartMutex;
|
||||
boost::optional<std::chrono::steady_clock::time_point> 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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "settings.hpp"
|
||||
#include "settingsutils.hpp"
|
||||
#include "tileposition.hpp"
|
||||
|
||||
#include <BulletCollision/CollisionShapes/btCollisionShape.h>
|
||||
|
||||
|
|
|
@ -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<float>::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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<TilePosition> 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()
|
||||
|
|
|
@ -46,6 +46,8 @@ namespace DetourNavigator
|
|||
std::map<osg::Vec3f, std::set<TilePosition>> mChangedTiles;
|
||||
AsyncNavMeshUpdater mAsyncNavMeshUpdater;
|
||||
std::size_t mGenerationCounter = 0;
|
||||
boost::optional<TilePosition> mPlayerTile;
|
||||
std::size_t mLastRecastMeshManagerRevision = 0;
|
||||
|
||||
void addChangedTiles(const btCollisionShape& shape, const btTransform& transform);
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "tileposition.hpp"
|
||||
#include "tilebounds.hpp"
|
||||
|
||||
#include <osg/Vec2f>
|
||||
#include <osg/Vec2i>
|
||||
#include <osg/Vec3f>
|
||||
|
||||
#include <utility>
|
||||
|
|
|
@ -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<std::mutex> lock(mTilesMutex);
|
||||
return mTiles.count(tilePosition);
|
||||
}
|
||||
|
||||
std::size_t TileCachedRecastMeshManager::getRevision() const
|
||||
{
|
||||
return mRevision;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,11 +20,24 @@ namespace DetourNavigator
|
|||
|
||||
std::shared_ptr<RecastMesh> getMesh(const TilePosition& tilePosition);
|
||||
|
||||
bool hasTile(const TilePosition& tilePosition);
|
||||
|
||||
template <class Function>
|
||||
void forEachTilePosition(Function&& function)
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mTilesMutex);
|
||||
for (const auto& tile : mTiles)
|
||||
function(tile.first);
|
||||
}
|
||||
|
||||
std::size_t getRevision() const;
|
||||
|
||||
private:
|
||||
const Settings& mSettings;
|
||||
std::mutex mTilesMutex;
|
||||
std::map<TilePosition, CachedRecastMeshManager> mTiles;
|
||||
std::unordered_map<std::size_t, std::vector<TilePosition>> mObjectsTilesPositions;
|
||||
std::size_t mRevision = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue