#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 #include #include 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(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(); } }