1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2024-12-28 00:15:06 +00:00
OpenMW/components/detournavigator/navmeshcacheitem.cpp
elsid 28f7a89530
Reuse dtNavMeshQuery
To avoid redundant allocations.
2023-02-17 15:05:25 +01:00

159 lines
5.3 KiB
C++

#include "navmeshcacheitem.hpp"
#include "debug.hpp"
#include "makenavmesh.hpp"
#include "navmeshdata.hpp"
#include "navmeshtilescache.hpp"
#include "navmeshtileview.hpp"
#include "settings.hpp"
#include "tileposition.hpp"
#include <DetourNavMesh.h>
#include <ostream>
#include <sstream>
namespace
{
using DetourNavigator::TilePosition;
bool removeTile(dtNavMesh& navMesh, const TilePosition& position)
{
const int layer = 0;
const auto tileRef = navMesh.getTileRefAt(position.x(), position.y(), layer);
if (tileRef == 0)
return false;
unsigned char** const data = nullptr;
int* const dataSize = nullptr;
return dtStatusSucceed(navMesh.removeTile(tileRef, data, dataSize));
}
dtStatus addTile(dtNavMesh& navMesh, unsigned char* data, int size)
{
const int doNotTransferOwnership = 0;
const dtTileRef lastRef = 0;
dtTileRef* const result = nullptr;
return navMesh.addTile(data, size, doNotTransferOwnership, lastRef, result);
}
}
namespace DetourNavigator
{
std::ostream& operator<<(std::ostream& stream, UpdateNavMeshStatus value)
{
switch (value)
{
case UpdateNavMeshStatus::ignored:
return stream << "ignore";
case UpdateNavMeshStatus::removed:
return stream << "removed";
case UpdateNavMeshStatus::added:
return stream << "add";
case UpdateNavMeshStatus::replaced:
return stream << "replaced";
case UpdateNavMeshStatus::failed:
return stream << "failed";
case UpdateNavMeshStatus::lost:
return stream << "lost";
case UpdateNavMeshStatus::cached:
return stream << "cached";
case UpdateNavMeshStatus::unchanged:
return stream << "unchanged";
case UpdateNavMeshStatus::restored:
return stream << "restored";
}
return stream << "unknown(" << static_cast<unsigned>(value) << ")";
}
const dtMeshTile* getTile(const dtNavMesh& navMesh, const TilePosition& position)
{
const int layer = 0;
return navMesh.getTileAt(position.x(), position.y(), layer);
}
NavMeshCacheItem::NavMeshCacheItem(std::size_t generation, const Settings& settings)
: mVersion{ generation, 0 }
{
initEmptyNavMesh(settings, mImpl);
if (const dtStatus status = mQuery.init(&mImpl, settings.mDetour.mMaxNavMeshQueryNodes);
!dtStatusSucceed(status))
{
std::ostringstream error;
error << "Failed to init navmesh query: " << WriteDtStatus{ status };
throw std::runtime_error(error.str());
}
}
UpdateNavMeshStatus NavMeshCacheItem::updateTile(
const TilePosition& position, NavMeshTilesCache::Value&& cached, NavMeshData&& navMeshData)
{
const dtMeshTile* currentTile = getTile(mImpl, position);
if (currentTile != nullptr
&& asNavMeshTileConstView(*currentTile) == asNavMeshTileConstView(navMeshData.mValue.get()))
{
return UpdateNavMeshStatus::ignored;
}
bool removed = ::removeTile(mImpl, position);
removed = mEmptyTiles.erase(position) > 0 || removed;
const auto addStatus = addTile(mImpl, navMeshData.mValue.get(), navMeshData.mSize);
if (dtStatusSucceed(addStatus))
{
auto tile = mUsedTiles.find(position);
if (tile == mUsedTiles.end())
{
mUsedTiles.emplace_hint(tile, position,
Tile{ Version{ mVersion.mRevision, 1 }, std::move(cached), std::move(navMeshData) });
}
else
{
++tile->second.mVersion.mRevision;
tile->second.mCached = std::move(cached);
tile->second.mData = std::move(navMeshData);
}
++mVersion.mRevision;
return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult();
}
else
{
if (removed)
{
mUsedTiles.erase(position);
++mVersion.mRevision;
}
return UpdateNavMeshStatusBuilder()
.removed(removed)
.failed((addStatus & DT_OUT_OF_MEMORY) != 0)
.getResult();
}
}
UpdateNavMeshStatus NavMeshCacheItem::removeTile(const TilePosition& position)
{
bool removed = ::removeTile(mImpl, position);
removed = mEmptyTiles.erase(position) > 0 || removed;
if (removed)
{
mUsedTiles.erase(position);
++mVersion.mRevision;
}
return UpdateNavMeshStatusBuilder().removed(removed).getResult();
}
UpdateNavMeshStatus NavMeshCacheItem::markAsEmpty(const TilePosition& position)
{
bool removed = ::removeTile(mImpl, position);
removed = mEmptyTiles.insert(position).second || removed;
if (removed)
{
mUsedTiles.erase(position);
++mVersion.mRevision;
}
return UpdateNavMeshStatusBuilder().removed(removed).getResult();
}
bool NavMeshCacheItem::isEmptyTile(const TilePosition& position) const
{
return mEmptyTiles.find(position) != mEmptyTiles.end();
}
}