1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-09 03:39:14 +00:00
OpenMW/components/detournavigator/navmeshmanager.cpp
elsid c771986c56
Prioritise NavMesh jobs first to remove and last to add
When player move fast enough, tiles update for specific area square
couldn't catch player move. Tiles to be removed are left in the queue
with lower priority then tiles to be added which are nearest to player.
This can lead to overflow for amount of tiles. So we try to do remove
first. But we detect change type approximately using mixed change type,
because even if we do it precise, change type could change while job
is in queue.
2018-10-13 22:16:31 +03:00

168 lines
6.2 KiB
C++

#include "navmeshmanager.hpp"
#include "debug.hpp"
#include "exceptions.hpp"
#include "gettilespositions.hpp"
#include "makenavmesh.hpp"
#include "navmeshcacheitem.hpp"
#include "settings.hpp"
#include "sharednavmesh.hpp"
#include <DetourNavMesh.h>
#include <BulletCollision/CollisionShapes/btConcaveShape.h>
#include <iostream>
namespace
{
using DetourNavigator::ChangeType;
ChangeType addChangeType(const ChangeType current, const ChangeType add)
{
return current == add ? current : ChangeType::mixed;
}
}
namespace DetourNavigator
{
NavMeshManager::NavMeshManager(const Settings& settings)
: mSettings(settings)
, mRecastMeshManager(settings)
, mAsyncNavMeshUpdater(settings, mRecastMeshManager)
{
}
bool NavMeshManager::addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform)
{
if (!mRecastMeshManager.addObject(id, shape, transform))
return false;
addChangedTiles(shape, transform, ChangeType::add);
return true;
}
bool NavMeshManager::updateObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform)
{
if (!mRecastMeshManager.updateObject(id, transform))
return false;
addChangedTiles(shape, transform, ChangeType::mixed);
return true;
}
bool NavMeshManager::removeObject(std::size_t id)
{
const auto object = mRecastMeshManager.removeObject(id);
if (!object)
return false;
addChangedTiles(object->mShape, object->mTransform, ChangeType::remove);
return true;
}
void NavMeshManager::addAgent(const osg::Vec3f& agentHalfExtents)
{
auto cached = mCache.find(agentHalfExtents);
if (cached != mCache.end())
return;
mCache.insert(std::make_pair(agentHalfExtents,
std::make_shared<NavMeshCacheItem>(makeEmptyNavMesh(mSettings), ++mGenerationCounter)));
log("cache add for agent=", agentHalfExtents);
}
void NavMeshManager::reset(const osg::Vec3f& agentHalfExtents)
{
mCache.erase(agentHalfExtents);
}
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::map<TilePosition, ChangeType> tilesToPost;
const auto& cached = getCached(agentHalfExtents);
const auto changedTiles = mChangedTiles.find(agentHalfExtents);
{
const auto locked = cached->mValue.lock();
if (changedTiles != mChangedTiles.end())
{
for (const auto& tile : changedTiles->second)
if (locked->getTileAt(tile.first.x(), tile.first.y(), 0))
{
auto tileToPost = tilesToPost.find(tile.first);
if (tileToPost == tilesToPost.end())
tilesToPost.insert(tile);
else
tileToPost->second = addChangeType(tileToPost->second, tile.second);
}
for (const auto& tile : tilesToPost)
changedTiles->second.erase(tile.first);
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)
tilesToPost.insert(std::make_pair(tile, ChangeType::add));
else if (!shouldAdd && presentInNavMesh)
tilesToPost.insert(std::make_pair(tile, ChangeType::mixed));
});
}
mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, tilesToPost);
log("cache update posted for agent=", agentHalfExtents,
" playerTile=", *mPlayerTile,
" recastMeshManagerRevision=", mLastRecastMeshManagerRevision,
" changedTiles=", changedTiles->second.size());
}
void NavMeshManager::wait()
{
mAsyncNavMeshUpdater.wait();
}
SharedNavMesh NavMeshManager::getNavMesh(const osg::Vec3f& agentHalfExtents) const
{
return getCached(agentHalfExtents)->mValue;
}
std::map<osg::Vec3f, std::shared_ptr<NavMeshCacheItem>> NavMeshManager::getNavMeshes() const
{
return mCache;
}
void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform,
const ChangeType changeType)
{
getTilesPositions(shape, transform, mSettings, [&] (const TilePosition& v) {
for (const auto& cached : mCache)
if (cached.second)
{
auto& tiles = mChangedTiles[cached.first];
auto tile = tiles.find(v);
if (tile == tiles.end())
tiles.insert(std::make_pair(v, changeType));
else
tile->second = addChangeType(tile->second, changeType);
}
});
}
const std::shared_ptr<NavMeshCacheItem>& NavMeshManager::getCached(const osg::Vec3f& agentHalfExtents) const
{
const auto cached = mCache.find(agentHalfExtents);
if (cached != mCache.end())
return cached->second;
std::ostringstream stream;
stream << "Agent with half extents is not found: " << agentHalfExtents;
throw InvalidArgument(stream.str());
}
}