From 1201ab9f8087b7787a3c0c538042490a1687f02f Mon Sep 17 00:00:00 2001 From: TEDERIs Date: Wed, 18 Oct 2023 15:49:11 +0700 Subject: [PATCH] Fix navmesh serialization issues --- source/module/Navigation.cpp | 40 ++++------- source/navigation/DynamicNavigationMesh.cpp | 78 ++++++++++++--------- source/navigation/DynamicNavigationMesh.h | 13 ++-- source/utils/DebugMesh.cpp | 52 ++++++++++---- source/utils/UtilsStream.cpp | 8 ++- 5 files changed, 112 insertions(+), 79 deletions(-) diff --git a/source/module/Navigation.cpp b/source/module/Navigation.cpp index 337fe80..761a91c 100644 --- a/source/module/Navigation.cpp +++ b/source/module/Navigation.cpp @@ -91,14 +91,11 @@ bool Navigation::Save(const std::filesystem::path& path) return false; } - const auto cached = navmesh_->Serialize(); - if (cached.size() > 0) { - std::ofstream stream(path, std::ios::out | std::ios::binary); - if (stream.is_open()) { - stream.write(reinterpret_cast(cached.data()), cached.size()); - return true; - } - } + std::ofstream stream(path, std::ios::out | std::ios::binary); + if (stream.is_open()) { + OutputFileStream output(stream); + return navmesh_->Serialize(output); + } return false; } @@ -109,30 +106,23 @@ bool Navigation::Load(const std::filesystem::path& path) return false; } - std::ifstream stream(path, std::ios::out | std::ios::binary | std::ios::ate); + std::ifstream stream(path, std::ios::in | std::ios::binary); if (stream.is_open()) { - const auto size = stream.tellg(); - stream.seekg(0); - - std::vector buffer; - buffer.resize(static_cast(size)); - - stream.read(reinterpret_cast(buffer.data()), size); - - if (buffer.size() > 0) { - navmesh_->Deserialize(buffer); - return true; - } - } + InputFileStream input(stream); + return navmesh_->Deserialize(input); + } return false; } bool Navigation::Dump(const std::filesystem::path& path) { - if (navmesh_) { - DebugMesh mesh; - navmesh_->Dump(mesh); + if (!navmesh_) { + return false; + } + + DebugMesh mesh; + if (navmesh_->Dump(mesh)) { mesh.Dump(path); return true; } diff --git a/source/navigation/DynamicNavigationMesh.cpp b/source/navigation/DynamicNavigationMesh.cpp index 28b36b2..83044fc 100644 --- a/source/navigation/DynamicNavigationMesh.cpp +++ b/source/navigation/DynamicNavigationMesh.cpp @@ -179,6 +179,12 @@ class NavigationMeshBuilder std::vector> blocks; std::mutex blocksMutex; + // Create temp directory if not exists + if (!std::filesystem::exists(tempDir)) { + spdlog::info("Temp directory was created."); + std::filesystem::create_directories(tempDir); + } + spdlog::info("[MULTITHREADED] Start navigation mesh build! Running {} threads.", pool_.get_thread_count()); pool_.parallelize_loop(0, numTilesX * numTilesZ, @@ -514,7 +520,9 @@ std::vector DynamicNavigationMesh::GetTileData(const Int32Vector2 { std::vector ret; OutputMemoryStream stream(ret); - WriteTiles(stream, tile.x_, tile.y_); + + dtCompressedTileRef tiles[TILECACHE_MAXLAYERS]; + WriteTiles(stream, tile.x_, tile.y_, tiles); return ret; } @@ -565,10 +573,10 @@ std::size_t DynamicNavigationMesh::GetEffectiveTilesCount() const return 0u; } -void DynamicNavigationMesh::Dump(DebugMesh& mesh, bool triangulated, const BoundingBox* bounds) +bool DynamicNavigationMesh::Dump(DebugMesh& mesh, bool triangulated, const BoundingBox* bounds) { if (!navMesh_) { - return; + return false; } const dtNavMesh* navMesh = navMesh_; @@ -614,7 +622,7 @@ void DynamicNavigationMesh::Dump(DebugMesh& mesh, bool triangulated, const Bound navMesh->calcTileLoc(&bounds->max_.x_, &maxTileX, &maxTileY); if (minTileX > maxTileX || minTileY > maxTileY) { - return; + return false; } const int32_t tilesH = maxTileX - minTileX; @@ -642,13 +650,12 @@ void DynamicNavigationMesh::Dump(DebugMesh& mesh, bool triangulated, const Bound } } } + + return true; } -std::vector DynamicNavigationMesh::Serialize() const +bool DynamicNavigationMesh::Serialize(OutputStream& stream) const { - std::vector ret; - OutputMemoryStream stream(ret); - if (navMesh_ && tileCache_) { stream.WriteBoundingBox(boundingBox_); @@ -661,24 +668,25 @@ std::vector DynamicNavigationMesh::Serialize() const const dtTileCacheParams* tcParams = tileCache_->getParams(); stream.Write(tcParams, sizeof(dtTileCacheParams)); - for (int z = 0; z < numTilesZ_; ++z) - for (int x = 0; x < numTilesX_; ++x) - WriteTiles(stream, x, z); + dtCompressedTileRef tiles[TILECACHE_MAXLAYERS]; + + for (int z = 0; z < numTilesZ_; ++z) { + for (int x = 0; x < numTilesX_; ++x) { + if (!WriteTiles(stream, x, z, tiles)) { + spdlog::error("An internal navmesh serialization error. Aborting the serialization process."); + return false; + } + } + } } - return ret; + return true; } -void DynamicNavigationMesh::Deserialize(const std::vector& value) +bool DynamicNavigationMesh::Deserialize(InputStream& stream) { ReleaseNavigationMesh(); - if (value.empty()) { - return; - } - - InputMemoryStream stream(value); - boundingBox_ = stream.ReadBoundingBox(); numTilesX_ = stream.ReadInt(); numTilesZ_ = stream.ReadInt(); @@ -690,14 +698,14 @@ void DynamicNavigationMesh::Deserialize(const std::vector& value) if (!navMesh_) { spdlog::error("Could not allocate navigation mesh"); - return; + return false; } if (dtStatusFailed(navMesh_->init(¶ms))) { spdlog::error("Could not initialize navigation mesh"); ReleaseNavigationMesh(); - return; + return false; } dtTileCacheParams tcParams; // NOLINT(hicpp-member-init) @@ -708,16 +716,16 @@ void DynamicNavigationMesh::Deserialize(const std::vector& value) { spdlog::error("Could not allocate tile cache"); ReleaseNavigationMesh(); - return; + return false; } if (dtStatusFailed(tileCache_->init(&tcParams, allocator_.get(), compressor_.get(), meshProcessor_.get()))) { spdlog::error("Could not initialize tile cache"); ReleaseNavigationMesh(); - return; + return false; } - ReadTiles(stream, true); + return ReadTiles(stream, true); } void DynamicNavigationMesh::AddObstacle(Obstacle* obstacle) @@ -999,20 +1007,24 @@ void DynamicNavigationMesh::ReleaseNavigationMesh() ReleaseTileCache(); } -void DynamicNavigationMesh::WriteTiles(OutputStream& dest, int x, int z) const -{ - dtCompressedTileRef tiles[TILECACHE_MAXLAYERS]; - const int ct = tileCache_->getTilesAt(x, z, tiles, maxLayers_); - for (int i = 0; i < ct; ++i) - { +bool DynamicNavigationMesh::WriteTiles(OutputStream& dest, int x, int z, dtCompressedTileRef* tiles) const +{ + const int ct = tileCache_->getTilesAt(x, z, tiles, TILECACHE_MAXLAYERS); + for (int i = 0; i < ct; ++i) { const dtCompressedTile* tile = tileCache_->getTileByRef(tiles[i]); - if (!tile || !tile->header || !tile->dataSize) + if (!tile || !tile->header || !tile->dataSize) { continue; // Don't write "void-space" tiles - // The header conveniently has the majority of the information required + } + dest.Write(tile->header, sizeof(dtTileCacheLayerHeader)); dest.WriteInt(tile->dataSize); - dest.Write(tile->data, (unsigned)tile->dataSize); + + if (!dest.Write(tile->data, (unsigned)tile->dataSize)) { + return false; + } } + + return true; } bool DynamicNavigationMesh::ReadTiles(InputStream& source, bool silent) diff --git a/source/navigation/DynamicNavigationMesh.h b/source/navigation/DynamicNavigationMesh.h index 30513b8..39b831c 100644 --- a/source/navigation/DynamicNavigationMesh.h +++ b/source/navigation/DynamicNavigationMesh.h @@ -7,10 +7,10 @@ #include "../navigation/NavigationMesh.h" #include "../utils/UtilsStream.h" -class dtTileCache; +#include "DetourTileCache.h" + struct dtTileCacheAlloc; struct dtTileCacheCompressor; -struct dtTileCacheMeshProcess; struct dtTileCacheLayer; struct dtTileCacheContourSet; struct dtTileCachePolyMesh; @@ -29,7 +29,6 @@ struct TileCacheData int dataSize{}; }; - class DynamicNavigationMesh : public NavigationMesh, public std::enable_shared_from_this { friend class Obstacle; @@ -61,11 +60,11 @@ class DynamicNavigationMesh : public NavigationMesh, public std::enable_shared_f // Return actual number of tiles. std::size_t GetEffectiveTilesCount() const; - void Dump(DebugMesh& mesh, bool triangulated = false, const BoundingBox* bounds = {}); + bool Dump(DebugMesh& mesh, bool triangulated = false, const BoundingBox* bounds = {}); - std::vector Serialize() const; + bool Serialize(OutputStream& stream) const; - void Deserialize(const std::vector& value); + bool Deserialize(InputStream& stream); protected: // Used by Obstacle class to add itself to the tile cache @@ -87,7 +86,7 @@ class DynamicNavigationMesh : public NavigationMesh, public std::enable_shared_f private: // Write tiles data. - void WriteTiles(OutputStream& dest, int x, int z) const; + bool WriteTiles(OutputStream& dest, int x, int z, dtCompressedTileRef* tiles) const; // Read tiles data to the navigation mesh. bool ReadTiles(InputStream& source, bool silent); // Free the tile cache. diff --git a/source/utils/DebugMesh.cpp b/source/utils/DebugMesh.cpp index 5a3db6c..e541fab 100644 --- a/source/utils/DebugMesh.cpp +++ b/source/utils/DebugMesh.cpp @@ -2,6 +2,8 @@ #include "../utils/DebugMesh.h" +#include + namespace WorldAssistant { @@ -15,7 +17,11 @@ void DebugMesh::Dump(const std::filesystem::path& path) { // Write to .obj std::ofstream stream(path); - stream << "# WorldAssistant\n"; + if (!stream.is_open()) { + return; + } + + stream << "# MTA:SA Detour\n"; for (const auto& vert : vertices_) { stream << "v " << vert.x_ << " " << vert.y_ << " " << vert.z_ << "\n"; @@ -36,29 +42,49 @@ void DebugMesh::Dump(const std::filesystem::path& path) void DebugMesh::AddLine(const Vector3F& startPos, const Vector3F& endPos) { - lines_.push_back({startPos, endPos}); + try { + lines_.push_back({startPos, endPos}); + } + catch ([[maybe_unused]] const std::bad_alloc& e) { + spdlog::error("Out of memory"); + } } void DebugMesh::AddVertex(const Vector3F& pos) { - indices_.push_back(static_cast(vertices_.size())); - vertices_.push_back(pos); + try { + indices_.push_back(static_cast(vertices_.size())); + vertices_.push_back(pos); + } + catch ([[maybe_unused]] const std::bad_alloc& e) { + spdlog::error("Out of memory"); + } } std::vector DebugMesh::GetTriangleList() const { + if (indices_.size() % 3 != 0) { + return {}; + } + std::vector result; - if (indices_.size() % 3 == 0) { - for (size_t i = 0; i < indices_.size(); i += 3) { - const int32_t a = indices_[i]; - const int32_t b = indices_[i + 1]; - const int32_t c = indices_[i + 2]; + try { + result.reserve(indices_.size() * sizeof(Vector3F)); + } + catch ([[maybe_unused]] const std::bad_alloc& e) { + spdlog::error("Out of memory"); + return {}; + } + + for (size_t i = 0; i < indices_.size(); i += 3) { + const int32_t a = indices_[i]; + const int32_t b = indices_[i + 1]; + const int32_t c = indices_[i + 2]; - result.push_back(vertices_[a]); - result.push_back(vertices_[b]); - result.push_back(vertices_[c]); - } + result.push_back(vertices_[a]); + result.push_back(vertices_[b]); + result.push_back(vertices_[c]); } return result; diff --git a/source/utils/UtilsStream.cpp b/source/utils/UtilsStream.cpp index dc8bb37..e81d06b 100644 --- a/source/utils/UtilsStream.cpp +++ b/source/utils/UtilsStream.cpp @@ -342,7 +342,13 @@ std::size_t OutputMemoryStream::Write(const void* data, std::size_t len) if (len + pos_ > size_) { size_ = len + pos_; - buffer_.resize(size_); + + try { + buffer_.resize(size_); + } + catch (const std::bad_alloc& e) { + return 0u; + } } auto* srcPtr = (unsigned char*)data;