mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 13:23:53 +00:00
Merge pull request #2225 from elsid/retry_async_navmesh_update_job
Support explicit limit of navmesh tiles for scene
This commit is contained in:
commit
c2a7aa2932
12 changed files with 212 additions and 94 deletions
|
@ -61,6 +61,7 @@ namespace
|
||||||
mSettings.mMaxSmoothPathSize = 1024;
|
mSettings.mMaxSmoothPathSize = 1024;
|
||||||
mSettings.mTrianglesPerChunk = 256;
|
mSettings.mTrianglesPerChunk = 256;
|
||||||
mSettings.mMaxPolys = 4096;
|
mSettings.mMaxPolys = 4096;
|
||||||
|
mSettings.mMaxTilesNumber = 512;
|
||||||
mNavigator.reset(new NavigatorImpl(mSettings));
|
mNavigator.reset(new NavigatorImpl(mSettings));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "asyncnavmeshupdater.hpp"
|
#include "asyncnavmeshupdater.hpp"
|
||||||
#include "debug.hpp"
|
#include "debug.hpp"
|
||||||
#include "makenavmesh.hpp"
|
#include "makenavmesh.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
|
@ -14,16 +14,6 @@ namespace
|
||||||
{
|
{
|
||||||
return std::abs(lhs.x() - rhs.x()) + std::abs(lhs.y() - rhs.y());
|
return std::abs(lhs.x() - rhs.x()) + std::abs(lhs.y() - rhs.y());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<ChangeType, int, int> 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
|
namespace DetourNavigator
|
||||||
|
@ -32,14 +22,18 @@ namespace DetourNavigator
|
||||||
{
|
{
|
||||||
switch (value)
|
switch (value)
|
||||||
{
|
{
|
||||||
case UpdateNavMeshStatus::ignore:
|
case UpdateNavMeshStatus::ignored:
|
||||||
return stream << "ignore";
|
return stream << "ignore";
|
||||||
case UpdateNavMeshStatus::removed:
|
case UpdateNavMeshStatus::removed:
|
||||||
return stream << "removed";
|
return stream << "removed";
|
||||||
case UpdateNavMeshStatus::add:
|
case UpdateNavMeshStatus::added:
|
||||||
return stream << "add";
|
return stream << "add";
|
||||||
case UpdateNavMeshStatus::replaced:
|
case UpdateNavMeshStatus::replaced:
|
||||||
return stream << "replaced";
|
return stream << "replaced";
|
||||||
|
case UpdateNavMeshStatus::failed:
|
||||||
|
return stream << "failed";
|
||||||
|
case UpdateNavMeshStatus::lost:
|
||||||
|
return stream << "lost";
|
||||||
}
|
}
|
||||||
return stream << "unknown";
|
return stream << "unknown";
|
||||||
}
|
}
|
||||||
|
@ -81,13 +75,25 @@ namespace DetourNavigator
|
||||||
for (const auto& changedTile : changedTiles)
|
for (const auto& changedTile : changedTiles)
|
||||||
{
|
{
|
||||||
if (mPushed[agentHalfExtents].insert(changedTile.first).second)
|
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.mTryNumber = 0;
|
||||||
|
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");
|
log("posted ", mJobs.size(), " jobs");
|
||||||
|
|
||||||
mHasJob.notify_all();
|
if (!mJobs.empty())
|
||||||
|
mHasJob.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncNavMeshUpdater::wait()
|
void AsyncNavMeshUpdater::wait()
|
||||||
|
@ -103,8 +109,9 @@ namespace DetourNavigator
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (const auto job = getNextJob())
|
if (auto job = getNextJob())
|
||||||
processJob(*job);
|
if (!processJob(*job))
|
||||||
|
repost(std::move(*job));
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
@ -114,7 +121,7 @@ namespace DetourNavigator
|
||||||
log("stop process jobs");
|
log("stop process jobs");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncNavMeshUpdater::processJob(const Job& job)
|
bool AsyncNavMeshUpdater::processJob(const Job& job)
|
||||||
{
|
{
|
||||||
log("process job for agent=", job.mAgentHalfExtents);
|
log("process job for agent=", job.mAgentHalfExtents);
|
||||||
|
|
||||||
|
@ -135,12 +142,16 @@ namespace DetourNavigator
|
||||||
|
|
||||||
using FloatMs = std::chrono::duration<float, std::milli>;
|
using FloatMs = std::chrono::duration<float, std::milli>;
|
||||||
|
|
||||||
const auto locked = job.mNavMeshCacheItem.lockConst();
|
{
|
||||||
log("cache updated for agent=", job.mAgentHalfExtents, " status=", status,
|
const auto locked = job.mNavMeshCacheItem.lockConst();
|
||||||
" generation=", locked->getGeneration(),
|
log("cache updated for agent=", job.mAgentHalfExtents, " status=", status,
|
||||||
" revision=", locked->getNavMeshRevision(),
|
" generation=", locked->getGeneration(),
|
||||||
" time=", std::chrono::duration_cast<FloatMs>(finish - start).count(), "ms",
|
" revision=", locked->getNavMeshRevision(),
|
||||||
" total_time=", std::chrono::duration_cast<FloatMs>(finish - firstStart).count(), "ms");
|
" time=", std::chrono::duration_cast<FloatMs>(finish - start).count(), "ms",
|
||||||
|
" total_time=", std::chrono::duration_cast<FloatMs>(finish - firstStart).count(), "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
return isSuccess(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<AsyncNavMeshUpdater::Job> AsyncNavMeshUpdater::getNextJob()
|
boost::optional<AsyncNavMeshUpdater::Job> AsyncNavMeshUpdater::getNextJob()
|
||||||
|
@ -193,4 +204,19 @@ namespace DetourNavigator
|
||||||
*locked = value;
|
*locked = value;
|
||||||
return *locked.get();
|
return *locked.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AsyncNavMeshUpdater::repost(Job&& job)
|
||||||
|
{
|
||||||
|
if (mShouldStop || job.mTryNumber > 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
if (mPushed[job.mAgentHalfExtents].insert(job.mChangedTile).second)
|
||||||
|
{
|
||||||
|
++job.mTryNumber;
|
||||||
|
mJobs.push(std::move(job));
|
||||||
|
mHasJob.notify_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,11 +50,19 @@ namespace DetourNavigator
|
||||||
osg::Vec3f mAgentHalfExtents;
|
osg::Vec3f mAgentHalfExtents;
|
||||||
SharedNavMeshCacheItem mNavMeshCacheItem;
|
SharedNavMeshCacheItem mNavMeshCacheItem;
|
||||||
TilePosition mChangedTile;
|
TilePosition mChangedTile;
|
||||||
std::tuple<ChangeType, int, int> mPriority;
|
unsigned mTryNumber;
|
||||||
|
ChangeType mChangeType;
|
||||||
|
int mDistanceToPlayer;
|
||||||
|
int mDistanceToOrigin;
|
||||||
|
|
||||||
|
std::tuple<unsigned, ChangeType, int, int> getPriority() const
|
||||||
|
{
|
||||||
|
return std::make_tuple(mTryNumber, mChangeType, mDistanceToPlayer, mDistanceToOrigin);
|
||||||
|
}
|
||||||
|
|
||||||
friend inline bool operator <(const Job& lhs, const Job& rhs)
|
friend inline bool operator <(const Job& lhs, const Job& rhs)
|
||||||
{
|
{
|
||||||
return lhs.mPriority > rhs.mPriority;
|
return lhs.getPriority() > rhs.getPriority();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -76,13 +84,15 @@ namespace DetourNavigator
|
||||||
|
|
||||||
void process() throw();
|
void process() throw();
|
||||||
|
|
||||||
void processJob(const Job& job);
|
bool processJob(const Job& job);
|
||||||
|
|
||||||
boost::optional<Job> getNextJob();
|
boost::optional<Job> getNextJob();
|
||||||
|
|
||||||
void writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const;
|
void writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const;
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point setFirstStart(const std::chrono::steady_clock::time_point& value);
|
std::chrono::steady_clock::time_point setFirstStart(const std::chrono::steady_clock::time_point& value);
|
||||||
|
|
||||||
|
void repost(Job&& job);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -441,17 +441,56 @@ namespace
|
||||||
return NavMeshData(navMeshData, navMeshDataSize);
|
return NavMeshData(navMeshData, navMeshDataSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateNavMeshStatus makeUpdateNavMeshStatus(bool removed, bool add)
|
class UpdateNavMeshStatusBuilder
|
||||||
{
|
{
|
||||||
if (removed && add)
|
public:
|
||||||
return UpdateNavMeshStatus::replaced;
|
UpdateNavMeshStatusBuilder() = default;
|
||||||
else if (removed)
|
|
||||||
return UpdateNavMeshStatus::removed;
|
UpdateNavMeshStatusBuilder removed(bool value)
|
||||||
else if (add)
|
{
|
||||||
return UpdateNavMeshStatus::add;
|
if (value)
|
||||||
else
|
set(UpdateNavMeshStatus::removed);
|
||||||
return UpdateNavMeshStatus::ignore;
|
else
|
||||||
}
|
unset(UpdateNavMeshStatus::removed);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateNavMeshStatusBuilder added(bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
set(UpdateNavMeshStatus::added);
|
||||||
|
else
|
||||||
|
unset(UpdateNavMeshStatus::added);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateNavMeshStatusBuilder failed(bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
set(UpdateNavMeshStatus::failed);
|
||||||
|
else
|
||||||
|
unset(UpdateNavMeshStatus::failed);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateNavMeshStatus getResult() const
|
||||||
|
{
|
||||||
|
return mResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
UpdateNavMeshStatus mResult = UpdateNavMeshStatus::ignored;
|
||||||
|
|
||||||
|
void set(UpdateNavMeshStatus value)
|
||||||
|
{
|
||||||
|
mResult = static_cast<UpdateNavMeshStatus>(static_cast<unsigned>(mResult) | static_cast<unsigned>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void unset(UpdateNavMeshStatus value)
|
||||||
|
{
|
||||||
|
mResult = static_cast<UpdateNavMeshStatus>(static_cast<unsigned>(mResult) & ~static_cast<unsigned>(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
unsigned long getMinValuableBitsNumber(const T value)
|
unsigned long getMinValuableBitsNumber(const T value)
|
||||||
|
@ -461,6 +500,49 @@ namespace
|
||||||
++power;
|
++power;
|
||||||
return 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 <class T>
|
||||||
|
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<T>(navMeshData));
|
||||||
|
return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (removed)
|
||||||
|
locked->removeUsedTile(changedTile);
|
||||||
|
log("failed to add tile with status=", WriteDtStatus {addStatus});
|
||||||
|
return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
|
@ -522,7 +604,7 @@ namespace DetourNavigator
|
||||||
const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, nullptr, nullptr));
|
const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, nullptr, nullptr));
|
||||||
if (removed)
|
if (removed)
|
||||||
locked->removeUsedTile(changedTile);
|
locked->removeUsedTile(changedTile);
|
||||||
return makeUpdateNavMeshStatus(removed, false);
|
return UpdateNavMeshStatusBuilder().removed(removed).getResult();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!recastMesh)
|
if (!recastMesh)
|
||||||
|
@ -546,7 +628,7 @@ namespace DetourNavigator
|
||||||
return removeTile();
|
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");
|
log("ignore add tile: too far from player");
|
||||||
return removeTile();
|
return removeTile();
|
||||||
|
@ -583,47 +665,10 @@ namespace DetourNavigator
|
||||||
if (!cachedNavMeshData)
|
if (!cachedNavMeshData)
|
||||||
{
|
{
|
||||||
log("cache overflow");
|
log("cache overflow");
|
||||||
|
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(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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto locked = navMeshCacheItem.lock();
|
return replaceTile(navMeshCacheItem, changedTile, std::move(cachedNavMeshData));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,21 @@ namespace DetourNavigator
|
||||||
class RecastMesh;
|
class RecastMesh;
|
||||||
struct Settings;
|
struct Settings;
|
||||||
|
|
||||||
enum class UpdateNavMeshStatus
|
enum class UpdateNavMeshStatus : unsigned
|
||||||
{
|
{
|
||||||
ignore,
|
ignored = 0,
|
||||||
removed,
|
removed = 1 << 0,
|
||||||
add,
|
added = 1 << 1,
|
||||||
replaced
|
replaced = removed | added,
|
||||||
|
failed = 1 << 2,
|
||||||
|
lost = removed | failed,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool isSuccess(UpdateNavMeshStatus value)
|
||||||
|
{
|
||||||
|
return (static_cast<unsigned>(value) & static_cast<unsigned>(UpdateNavMeshStatus::failed)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
inline float getLength(const osg::Vec2i& value)
|
inline float getLength(const osg::Vec2i& value)
|
||||||
{
|
{
|
||||||
return std::sqrt(float(osg::square(value.x()) + osg::square(value.y())));
|
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)
|
inline bool shouldAddTile(const TilePosition& changedTile, const TilePosition& playerTile, int maxTiles)
|
||||||
{
|
{
|
||||||
const auto expectedTilesCount = std::ceil(osg::PI * osg::square(getDistance(changedTile, playerTile)));
|
const auto expectedTilesCount = std::ceil(osg::PI * osg::square(getDistance(changedTile, playerTile)));
|
||||||
return expectedTilesCount * 3 <= maxTiles;
|
return expectedTilesCount <= maxTiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
NavMeshPtr makeEmptyNavMesh(const Settings& settings);
|
NavMeshPtr makeEmptyNavMesh(const Settings& settings);
|
||||||
|
|
|
@ -157,7 +157,7 @@ namespace DetourNavigator
|
||||||
if (changedTiles->second.empty())
|
if (changedTiles->second.empty())
|
||||||
mChangedTiles.erase(changedTiles);
|
mChangedTiles.erase(changedTiles);
|
||||||
}
|
}
|
||||||
const auto maxTiles = navMesh.getParams()->maxTiles;
|
const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles);
|
||||||
mRecastMeshManager.forEachTilePosition([&] (const TilePosition& tile)
|
mRecastMeshManager.forEachTilePosition([&] (const TilePosition& tile)
|
||||||
{
|
{
|
||||||
if (tilesToPost.count(tile))
|
if (tilesToPost.count(tile))
|
||||||
|
|
|
@ -62,8 +62,8 @@ namespace DetourNavigator
|
||||||
return Value();
|
return Value();
|
||||||
|
|
||||||
// TODO: use different function to make key to avoid unnecessary std::string allocation
|
// TODO: use different function to make key to avoid unnecessary std::string allocation
|
||||||
const auto tile = tileValues->second.Map.find(makeNavMeshKey(recastMesh, offMeshConnections));
|
const auto tile = tileValues->second.mMap.find(makeNavMeshKey(recastMesh, offMeshConnections));
|
||||||
if (tile == tileValues->second.Map.end())
|
if (tile == tileValues->second.mMap.end())
|
||||||
return Value();
|
return Value();
|
||||||
|
|
||||||
acquireItemUnsafe(tile->second);
|
acquireItemUnsafe(tile->second);
|
||||||
|
@ -96,7 +96,7 @@ namespace DetourNavigator
|
||||||
|
|
||||||
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, navMeshKey);
|
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
|
// 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)
|
if (!emplaced.second)
|
||||||
{
|
{
|
||||||
|
@ -125,16 +125,16 @@ namespace DetourNavigator
|
||||||
if (tileValues == agentValues->second.end())
|
if (tileValues == agentValues->second.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto value = tileValues->second.Map.find(item.mNavMeshKey);
|
const auto value = tileValues->second.mMap.find(item.mNavMeshKey);
|
||||||
if (value == tileValues->second.Map.end())
|
if (value == tileValues->second.mMap.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mUsedNavMeshDataSize -= getSize(item);
|
mUsedNavMeshDataSize -= getSize(item);
|
||||||
mFreeNavMeshDataSize -= getSize(item);
|
mFreeNavMeshDataSize -= getSize(item);
|
||||||
mFreeItems.pop_back();
|
mFreeItems.pop_back();
|
||||||
|
|
||||||
tileValues->second.Map.erase(value);
|
tileValues->second.mMap.erase(value);
|
||||||
if (!tileValues->second.Map.empty())
|
if (!tileValues->second.mMap.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
agentValues->second.erase(tileValues);
|
agentValues->second.erase(tileValues);
|
||||||
|
|
|
@ -108,7 +108,7 @@ namespace DetourNavigator
|
||||||
|
|
||||||
struct TileMap
|
struct TileMap
|
||||||
{
|
{
|
||||||
std::map<std::string, ItemIterator> Map;
|
std::map<std::string, ItemIterator> mMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::mutex mMutex;
|
std::mutex mMutex;
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace DetourNavigator
|
||||||
navigatorSettings.mMaxEdgeLen = ::Settings::Manager::getInt("max edge len", "Navigator");
|
navigatorSettings.mMaxEdgeLen = ::Settings::Manager::getInt("max edge len", "Navigator");
|
||||||
navigatorSettings.mMaxNavMeshQueryNodes = ::Settings::Manager::getInt("max nav mesh query nodes", "Navigator");
|
navigatorSettings.mMaxNavMeshQueryNodes = ::Settings::Manager::getInt("max nav mesh query nodes", "Navigator");
|
||||||
navigatorSettings.mMaxPolys = ::Settings::Manager::getInt("max polygons per tile", "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.mMaxVertsPerPoly = ::Settings::Manager::getInt("max verts per poly", "Navigator");
|
||||||
navigatorSettings.mRegionMergeSize = ::Settings::Manager::getInt("region merge size", "Navigator");
|
navigatorSettings.mRegionMergeSize = ::Settings::Manager::getInt("region merge size", "Navigator");
|
||||||
navigatorSettings.mRegionMinSize = ::Settings::Manager::getInt("region min size", "Navigator");
|
navigatorSettings.mRegionMinSize = ::Settings::Manager::getInt("region min size", "Navigator");
|
||||||
|
|
|
@ -26,6 +26,7 @@ namespace DetourNavigator
|
||||||
int mMaxEdgeLen = 0;
|
int mMaxEdgeLen = 0;
|
||||||
int mMaxNavMeshQueryNodes = 0;
|
int mMaxNavMeshQueryNodes = 0;
|
||||||
int mMaxPolys = 0;
|
int mMaxPolys = 0;
|
||||||
|
int mMaxTilesNumber = 0;
|
||||||
int mMaxVertsPerPoly = 0;
|
int mMaxVertsPerPoly = 0;
|
||||||
int mRegionMergeSize = 0;
|
int mRegionMergeSize = 0;
|
||||||
int mRegionMinSize = 0;
|
int mRegionMinSize = 0;
|
||||||
|
|
|
@ -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.
|
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.
|
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 <https://github.com/recastnavigation/recastnavigation>`_ library.
|
||||||
|
|
||||||
Advanced settings
|
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))).
|
this value. 22 bits is a limit to store both tile identifier and polygon identifier (tiles = 2^(22 - log2(polygons))).
|
||||||
See `recastnavigation <https://github.com/recastnavigation/recastnavigation>`_ for more details.
|
See `recastnavigation <https://github.com/recastnavigation/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 <https://github.com/recastnavigation/recastnavigation>`_ library.
|
||||||
|
|
||||||
max verts per poly
|
max verts per poly
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
|
@ -670,6 +670,9 @@ enable nav mesh render = false
|
||||||
# Render agents paths (true, false)
|
# Render agents paths (true, false)
|
||||||
enable agents paths render = false
|
enable agents paths render = false
|
||||||
|
|
||||||
|
# Max number of navmesh tiles (value >= 0)
|
||||||
|
max tiles number = 512
|
||||||
|
|
||||||
[Shadows]
|
[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.
|
# Enable or disable shadows. Bear in mind that this will force OpenMW to use shaders as if "[Shaders]/force shaders" was set to true.
|
||||||
|
|
Loading…
Reference in a new issue