Tools/mmaps_generator: Save offmesh connections input together with navmesh params for each map

This commit is contained in:
Shauren
2025-11-05 18:55:45 +01:00
parent 1e56367b30
commit 145bbde231
8 changed files with 108 additions and 55 deletions

View File

@@ -97,6 +97,28 @@ namespace MMAP
}
// load and init dtNavMesh - read parameters from file
dtNavMeshParams params;
if (LoadResult paramsResult = parseNavMeshParamsFile(basePath, mapId, &params); paramsResult != LoadResult::Success)
return paramsResult;
NavMeshPtr mesh(dtAllocNavMesh());
ASSERT(mesh);
if (dtStatusFailed(mesh->init(&params)))
{
TC_LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap {:04}", mapId);
return LoadResult::LibraryError;
}
TC_LOG_DEBUG("maps", "MMAP:loadMapData: Loaded {:04}.mmap", mapId);
// store inside our map list
itr->second.reset(new MMapData(std::move(mesh)));
return LoadResult::Success;
}
LoadResult MMapManager::parseNavMeshParamsFile(std::string_view basePath, uint32 mapId, dtNavMeshParams* params,
std::vector<OffMeshData>* offmeshConnections /*= nullptr*/)
{
std::string fileName = Trinity::StringFormat(MAP_FILE_NAME_FORMAT, basePath, mapId);
auto file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fileName.c_str(), "rb"));
if (!file)
@@ -105,26 +127,39 @@ namespace MMAP
return LoadResult::FileNotFound;
}
dtNavMeshParams params;
uint32 count = uint32(fread(&params, sizeof(dtNavMeshParams), 1, file.get()));
if (count != 1)
MmapNavMeshHeader fileHeader;
if (fread(&fileHeader, sizeof(MmapNavMeshHeader), 1, file.get()) != 1)
{
TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not read params from file '{}'", fileName);
return LoadResult::ReadFromFileFailed;
}
NavMeshPtr mesh(dtAllocNavMesh());
ASSERT(mesh);
if (dtStatusFailed(mesh->init(&params)))
if (fileHeader.mmapMagic != MMAP_MAGIC)
{
TC_LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap {:04} from file {}", mapId, fileName);
return LoadResult::LibraryError;
TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header in mmap {:04}.mmap", mapId);
return LoadResult::VersionMismatch;
}
TC_LOG_DEBUG("maps", "MMAP:loadMapData: Loaded {:04}.mmap", mapId);
if (fileHeader.mmapVersion != MMAP_VERSION)
{
TC_LOG_ERROR("maps", "MMAP:loadMap: {:04}.mmap was built with generator v{}, expected v{}",
mapId, fileHeader.mmapVersion, MMAP_VERSION);
return LoadResult::VersionMismatch;
}
memcpy(params, &fileHeader.params, sizeof(dtNavMeshParams));
if (offmeshConnections)
{
offmeshConnections->resize(fileHeader.offmeshConnectionCount);
if (fread(offmeshConnections->data(), sizeof(OffMeshData), offmeshConnections->size(), file.get()) != offmeshConnections->size())
{
offmeshConnections->clear();
TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not read offmesh connections from file '{}'", fileName);
return LoadResult::ReadFromFileFailed;
}
}
// store inside our map list
itr->second.reset(new MMapData(std::move(mesh)));
return LoadResult::Success;
}

View File

@@ -19,7 +19,7 @@
#define _MMAP_MANAGER_H
#include "Define.h"
#include <DetourNavMesh.h>
#include "MMapDefines.h"
#include <DetourNavMeshQuery.h>
#include <memory>
#include <string_view>
@@ -58,6 +58,7 @@ namespace MMAP
static MMapManager* instance();
void InitializeThreadUnsafe(std::unordered_map<uint32, std::vector<uint32>> const& mapData);
static LoadResult parseNavMeshParamsFile(std::string_view basePath, uint32 mapId, dtNavMeshParams* params, std::vector<OffMeshData>* offmeshConnections = nullptr);
LoadResult loadMap(std::string_view basePath, uint32 mapId, int32 x, int32 y);
bool loadMapInstance(std::string_view basePath, uint32 meshMapId, uint32 instanceMapId, uint32 instanceId);
bool unloadMap(uint32 mapId, int32 x, int32 y);

View File

@@ -19,22 +19,29 @@
#define MMapDefines_h__
#include "Define.h"
#include "DetourNavMesh.h"
#include <DetourNavMesh.h>
const uint32 MMAP_MAGIC = 0x4d4d4150; // 'MMAP'
#define MMAP_VERSION 15
inline uint32 constexpr MMAP_MAGIC = 0x4d4d4150; // 'MMAP'
inline uint32 constexpr MMAP_VERSION = 16;
struct MmapNavMeshHeader
{
uint32 mmapMagic = MMAP_MAGIC;
uint32 mmapVersion = MMAP_VERSION;
dtNavMeshParams params = { };
uint32 offmeshConnectionCount = 0;
};
static_assert(sizeof(MmapNavMeshHeader) == 40);
struct MmapTileHeader
{
uint32 mmapMagic;
uint32 dtVersion;
uint32 mmapVersion;
uint32 size;
char usesLiquids;
char padding[3];
MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
mmapVersion(MMAP_VERSION), size(0), usesLiquids(true), padding() { }
uint32 mmapMagic = MMAP_MAGIC;
uint32 dtVersion = DT_NAVMESH_VERSION;
uint32 mmapVersion = MMAP_VERSION;
uint32 size = 0;
char usesLiquids = true;
char padding[3] = { };
};
// All padding fields must be handled and initialized to ensure mmaps_generator will produce binary-identical *.mmtile files
@@ -60,7 +67,7 @@ enum NavArea
NAV_AREA_ALL_MASK = 0x3F // max allowed value
};
enum NavTerrainFlag
enum NavTerrainFlag : uint16
{
NAV_EMPTY = 0x00,
NAV_GROUND = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_GROUND),
@@ -69,4 +76,22 @@ enum NavTerrainFlag
NAV_MAGMA_SLIME = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_MAGMA_SLIME)
};
enum OffMeshConnectionFlag : uint8
{
OFFMESH_CONNECTION_FLAG_BIDIRECTIONAL = 0x01
};
struct OffMeshData
{
uint32 MapId;
uint32 TileX;
uint32 TileY;
float From[3];
float To[3];
float Radius;
OffMeshConnectionFlag ConnectionFlags;
uint8 AreaId;
NavTerrainFlag Flags;
};
#endif // MMapDefines_h__

View File

@@ -201,7 +201,7 @@ namespace MMAP
if (scanned < 10)
continue;
offMesh.Bidirectional = true;
offMesh.ConnectionFlags = OFFMESH_CONNECTION_FLAG_BIDIRECTIONAL;
if (scanned < 12)
offMesh.Flags = NAV_GROUND;
@@ -447,17 +447,16 @@ namespace MMAP
/*** now create the navmesh ***/
// navmesh creation params
dtNavMeshParams navMeshParams;
memset(&navMeshParams, 0, sizeof(dtNavMeshParams));
navMeshParams.tileWidth = GRID_SIZE;
navMeshParams.tileHeight = GRID_SIZE;
rcVcopy(navMeshParams.orig, bmin);
navMeshParams.maxTiles = maxTiles;
navMeshParams.maxPolys = maxPolysPerTile;
MmapNavMeshHeader fileHeader;
fileHeader.params.tileWidth = GRID_SIZE;
fileHeader.params.tileHeight = GRID_SIZE;
rcVcopy(fileHeader.params.orig, bmin);
fileHeader.params.maxTiles = maxTiles;
fileHeader.params.maxPolys = maxPolysPerTile;
navMesh = dtAllocNavMesh();
TC_LOG_INFO("maps.mmapgen", "[Map {:04}] Creating navMesh...", mapID);
if (!navMesh->init(&navMeshParams))
if (!navMesh->init(&fileHeader.params))
{
TC_LOG_ERROR("maps.mmapgen", "[Map {:04}] Failed creating navmesh!", mapID);
return;
@@ -474,8 +473,14 @@ namespace MMAP
return;
}
std::vector<OffMeshData> offMeshConnections;
std::ranges::copy_if(m_offMeshConnections, std::back_inserter(offMeshConnections), [mapID](OffMeshData const& offMeshData) { return offMeshData.MapId == mapID; });
fileHeader.offmeshConnectionCount = offMeshConnections.size();
// now that we know navMesh params are valid, we can write them to file
fwrite(&navMeshParams, sizeof(dtNavMeshParams), 1, file.get());
fwrite(&fileHeader, sizeof(MmapNavMeshHeader), 1, file.get());
fwrite(offMeshConnections.data(), sizeof(OffMeshData), offMeshConnections.size(), file.get());
}
/**************************************************************************/

View File

@@ -800,7 +800,7 @@ namespace MMAP
meshData.offMeshConnections.push_back(offMeshConnection.To[2]);
meshData.offMeshConnections.push_back(offMeshConnection.To[0]);
meshData.offMeshConnectionDirs.push_back(offMeshConnection.Bidirectional ? 1 : 0);
meshData.offMeshConnectionDirs.push_back(offMeshConnection.ConnectionFlags & OFFMESH_CONNECTION_FLAG_BIDIRECTIONAL);
meshData.offMeshConnectionRads.push_back(offMeshConnection.Radius); // agent size equivalent
// can be used same way as polygon flags
meshData.offMeshConnectionsAreas.push_back(offMeshConnection.AreaId);

View File

@@ -18,6 +18,7 @@
#ifndef _MMAP_TERRAIN_BUILDER_H
#define _MMAP_TERRAIN_BUILDER_H
#include "MMapDefines.h"
#include "WorldModel.h"
#include <G3D/Vector3.h>
#include <boost/filesystem/path.hpp>
@@ -61,19 +62,6 @@ namespace MMAP
// contrib/extractor/system.cpp
// src/game/Map.cpp
struct OffMeshData
{
uint32 MapId;
uint32 TileX;
uint32 TileY;
float From[3];
float To[3];
bool Bidirectional;
float Radius;
uint8 AreaId;
uint16 Flags;
};
struct MeshData
{
std::vector<float> solidVerts;

View File

@@ -125,9 +125,8 @@ namespace MMAP
// gather all mesh data for final data check, and bounds calculation
std::vector<float> allVerts(meshData.liquidVerts.size() + meshData.solidVerts.size());
auto allVertsOutput = allVerts.begin();
allVertsOutput = std::ranges::copy(meshData.liquidVerts, allVertsOutput).out;
allVertsOutput = std::ranges::copy(meshData.solidVerts, allVertsOutput).out;
std::ranges::copy(meshData.liquidVerts, allVerts.begin());
std::ranges::copy(meshData.solidVerts, allVerts.begin() + std::ssize(meshData.liquidVerts));
// get bounds of current tile
float bmin[3], bmax[3];
@@ -489,11 +488,11 @@ namespace MMAP
}
/**************************************************************************/
void TileBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax)
void TileBuilder::getTileBounds(uint32 tileX, uint32 tileY, float const* verts, std::size_t vertCount, float* bmin, float* bmax)
{
// this is for elevation
if (verts && vertCount)
rcCalcBounds(verts, vertCount, bmin, bmax);
rcCalcBounds(verts, int(vertCount), bmin, bmax);
else
{
bmin[1] = FLT_MIN;

View File

@@ -74,7 +74,7 @@ public:
virtual bool shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY) const;
static void getTileBounds(uint32 tileX, uint32 tileY,
float* verts, int vertCount,
float const* verts, std::size_t vertCount,
float* bmin, float* bmax);
rcConfig GetMapSpecificConfig(uint32 mapID, float const (&bmin)[3], float const (&bmax)[3], TileConfig const& tileConfig) const;