Skip to content

Commit

Permalink
Fix navmesh serialization issues
Browse files Browse the repository at this point in the history
  • Loading branch information
tederis committed Oct 18, 2023
1 parent 2ac53b9 commit 1201ab9
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 79 deletions.
40 changes: 15 additions & 25 deletions source/module/Navigation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const char*>(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;
}
Expand All @@ -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<unsigned char> buffer;
buffer.resize(static_cast<std::size_t>(size));

stream.read(reinterpret_cast<char*>(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;
}
Expand Down
78 changes: 45 additions & 33 deletions source/navigation/DynamicNavigationMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,12 @@ class NavigationMeshBuilder
std::vector<std::pair<uint32_t, uint32_t>> 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,
Expand Down Expand Up @@ -514,7 +520,9 @@ std::vector<unsigned char> DynamicNavigationMesh::GetTileData(const Int32Vector2
{
std::vector<unsigned char> ret;
OutputMemoryStream stream(ret);
WriteTiles(stream, tile.x_, tile.y_);

dtCompressedTileRef tiles[TILECACHE_MAXLAYERS];
WriteTiles(stream, tile.x_, tile.y_, tiles);

return ret;
}
Expand Down Expand Up @@ -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_;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -642,13 +650,12 @@ void DynamicNavigationMesh::Dump(DebugMesh& mesh, bool triangulated, const Bound
}
}
}

return true;
}

std::vector<unsigned char> DynamicNavigationMesh::Serialize() const
bool DynamicNavigationMesh::Serialize(OutputStream& stream) const
{
std::vector<unsigned char> ret;
OutputMemoryStream stream(ret);

if (navMesh_ && tileCache_)
{
stream.WriteBoundingBox(boundingBox_);
Expand All @@ -661,24 +668,25 @@ std::vector<unsigned char> 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<unsigned char>& value)
bool DynamicNavigationMesh::Deserialize(InputStream& stream)
{
ReleaseNavigationMesh();

if (value.empty()) {
return;
}

InputMemoryStream stream(value);

boundingBox_ = stream.ReadBoundingBox();
numTilesX_ = stream.ReadInt();
numTilesZ_ = stream.ReadInt();
Expand All @@ -690,14 +698,14 @@ void DynamicNavigationMesh::Deserialize(const std::vector<unsigned char>& value)
if (!navMesh_)
{
spdlog::error("Could not allocate navigation mesh");
return;
return false;
}

if (dtStatusFailed(navMesh_->init(&params)))
{
spdlog::error("Could not initialize navigation mesh");
ReleaseNavigationMesh();
return;
return false;
}

dtTileCacheParams tcParams; // NOLINT(hicpp-member-init)
Expand All @@ -708,16 +716,16 @@ void DynamicNavigationMesh::Deserialize(const std::vector<unsigned char>& 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)
Expand Down Expand Up @@ -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)
Expand Down
13 changes: 6 additions & 7 deletions source/navigation/DynamicNavigationMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,7 +29,6 @@ struct TileCacheData
int dataSize{};
};


class DynamicNavigationMesh : public NavigationMesh, public std::enable_shared_from_this<DynamicNavigationMesh>
{
friend class Obstacle;
Expand Down Expand Up @@ -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<unsigned char> Serialize() const;
bool Serialize(OutputStream& stream) const;

void Deserialize(const std::vector<unsigned char>& value);
bool Deserialize(InputStream& stream);

protected:
// Used by Obstacle class to add itself to the tile cache
Expand All @@ -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.
Expand Down
52 changes: 39 additions & 13 deletions source/utils/DebugMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "../utils/DebugMesh.h"

#include <spdlog/spdlog.h>

namespace WorldAssistant
{

Expand All @@ -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";
Expand All @@ -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<int32_t>(vertices_.size()));
vertices_.push_back(pos);
try {
indices_.push_back(static_cast<int32_t>(vertices_.size()));
vertices_.push_back(pos);
}
catch ([[maybe_unused]] const std::bad_alloc& e) {
spdlog::error("Out of memory");
}
}

std::vector<Vector3F> DebugMesh::GetTriangleList() const
{
if (indices_.size() % 3 != 0) {
return {};
}

std::vector<Vector3F> 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;
Expand Down
8 changes: 7 additions & 1 deletion source/utils/UtilsStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 1201ab9

Please sign in to comment.