diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/Collision/Management/MMapManager.cpp | 79 | ||||
| -rw-r--r-- | src/common/Collision/Management/MMapManager.h | 3 | ||||
| -rw-r--r-- | src/common/Collision/Maps/MMapDefines.h | 51 | ||||
| -rw-r--r-- | src/server/scripts/Commands/cs_mmaps.cpp | 2 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/Info/readme.txt | 6 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/IntermediateValues.cpp | 16 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/IntermediateValues.h | 14 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/MapBuilder.cpp | 44 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/MapBuilder.h | 9 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/PathCommon.h | 110 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/PathGenerator.cpp | 99 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/TerrainBuilder.cpp | 185 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/TerrainBuilder.h | 21 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/TileBuilder.cpp | 31 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/TileBuilder.h | 17 |
15 files changed, 376 insertions, 311 deletions
diff --git a/src/common/Collision/Management/MMapManager.cpp b/src/common/Collision/Management/MMapManager.cpp index c10d64bfd36..d0862a49156 100644 --- a/src/common/Collision/Management/MMapManager.cpp +++ b/src/common/Collision/Management/MMapManager.cpp @@ -26,7 +26,7 @@ namespace MMAP { constexpr char MAP_FILE_NAME_FORMAT[] = "{}mmaps/{:04}.mmap"; - constexpr char TILE_FILE_NAME_FORMAT[] = "{}mmaps/{:04}{:02}{:02}.mmtile"; + constexpr char TILE_FILE_NAME_FORMAT[] = "{}mmaps/{:04}_{:02}_{:02}.mmtile"; using NavMeshPtr = std::unique_ptr<dtNavMesh, decltype(Trinity::unique_ptr_deleter<dtNavMesh*, &::dtFreeNavMesh>())>; using NavMeshQueryPtr = std::unique_ptr<dtNavMeshQuery, decltype(Trinity::unique_ptr_deleter<dtNavMeshQuery*, &::dtFreeNavMeshQuery>())>; @@ -97,6 +97,28 @@ namespace MMAP } // load and init dtNavMesh - read parameters from file + dtNavMeshParams params; + if (LoadResult paramsResult = parseNavMeshParamsFile(basePath, mapId, ¶ms); paramsResult != LoadResult::Success) + return paramsResult; + + NavMeshPtr mesh(dtAllocNavMesh()); + ASSERT(mesh); + if (dtStatusFailed(mesh->init(¶ms))) + { + 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(¶ms, 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(¶ms))) + 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; } @@ -154,7 +189,7 @@ namespace MMAP if (mmap->loadedTileRefs.contains(packedGridPos)) return LoadResult::AlreadyLoaded; - // load this tile :: mmaps/MMMMXXYY.mmtile + // load this tile :: mmaps/MMMM_XX_YY.mmtile std::string fileName = Trinity::StringFormat(TILE_FILE_NAME_FORMAT, basePath, mapId, x, y); auto file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fileName.c_str(), "rb")); if (!file) @@ -177,19 +212,19 @@ namespace MMAP MmapTileHeader fileHeader; if (fread(&fileHeader, sizeof(MmapTileHeader), 1, file.get()) != 1) { - TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header in mmap {:04}{:02}{:02}.mmtile", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header in mmap {:04}_{:02}_{:02}.mmtile", mapId, x, y); return LoadResult::ReadFromFileFailed; } if (fileHeader.mmapMagic != MMAP_MAGIC) { - TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header in mmap {:04}{:02}{:02}.mmtile", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header in mmap {:04}_{:02}_{:02}.mmtile", mapId, x, y); return LoadResult::VersionMismatch; } if (fileHeader.mmapVersion != MMAP_VERSION) { - TC_LOG_ERROR("maps", "MMAP:loadMap: {:04}{:02}{:02}.mmtile was built with generator v{}, expected v{}", + TC_LOG_ERROR("maps", "MMAP:loadMap: {:04}_{:02}_{:02}.mmtile was built with generator v{}, expected v{}", mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION); return LoadResult::VersionMismatch; } @@ -198,7 +233,7 @@ namespace MMAP fseek(file.get(), 0, SEEK_END); if (pos < 0 || static_cast<int32>(fileHeader.size) > ftell(file.get()) - pos) { - TC_LOG_ERROR("maps", "MMAP:loadMap: {:04}{:02}{:02}.mmtile has corrupted data size", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP:loadMap: {:04}_{:02}_{:02}.mmtile has corrupted data size", mapId, x, y); return LoadResult::ReadFromFileFailed; } @@ -210,7 +245,7 @@ namespace MMAP size_t result = fread(data.get(), fileHeader.size, 1, file.get()); if (!result) { - TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header or data in mmap {:04}{:02}{:02}.mmtile", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header or data in mmap {:04}_{:02}_{:02}.mmtile", mapId, x, y); return LoadResult::ReadFromFileFailed; } @@ -227,7 +262,7 @@ namespace MMAP } else { - TC_LOG_ERROR("maps", "MMAP:loadMap: Could not load {:04}{:02}{:02}.mmtile into navmesh", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP:loadMap: Could not load {:04}_{:02}_{:02}.mmtile into navmesh", mapId, x, y); return LoadResult::LibraryError; } } @@ -270,7 +305,7 @@ namespace MMAP if (itr == loadedMMaps.end()) { // file may not exist, therefore not loaded - TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map. {:04}{:02}{:02}.mmtile", mapId, x, y); + TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map. {:04}_{:02}_{:02}.mmtile", mapId, x, y); return false; } @@ -292,7 +327,7 @@ namespace MMAP // this is technically a memory leak // if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used // we cannot recover from this error - assert out - TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:04}{:02}{:02}.mmtile from navmesh", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:04}_{:02}_{:02}.mmtile from navmesh", mapId, x, y); ABORT(); } else @@ -322,7 +357,7 @@ namespace MMAP uint32 x = (tileId >> 16); uint32 y = (tileId & 0x0000FFFF); if (dtStatusFailed(mmap->navMesh->removeTile(tileRef, nullptr, nullptr))) - TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:04}{:02}{:02}.mmtile from navmesh", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:04}_{:02}_{:02}.mmtile from navmesh", mapId, x, y); else { --loadedTiles; diff --git a/src/common/Collision/Management/MMapManager.h b/src/common/Collision/Management/MMapManager.h index 9973f37d51c..b48b773555b 100644 --- a/src/common/Collision/Management/MMapManager.h +++ b/src/common/Collision/Management/MMapManager.h @@ -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); diff --git a/src/common/Collision/Maps/MMapDefines.h b/src/common/Collision/Maps/MMapDefines.h index 368281543a2..522112ebe62 100644 --- a/src/common/Collision/Maps/MMapDefines.h +++ b/src/common/Collision/Maps/MMapDefines.h @@ -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 MmapTileHeader +struct MmapNavMeshHeader { - uint32 mmapMagic; - uint32 dtVersion; - uint32 mmapVersion; - uint32 size; - char usesLiquids; - char padding[3]; + uint32 mmapMagic = MMAP_MAGIC; + uint32 mmapVersion = MMAP_VERSION; + dtNavMeshParams params = { }; + uint32 offmeshConnectionCount = 0; +}; + +static_assert(sizeof(MmapNavMeshHeader) == 40); - MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION), - mmapVersion(MMAP_VERSION), size(0), usesLiquids(true), padding() { } +struct MmapTileHeader +{ + 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__ diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp index d30bfcdc746..b7734a8c505 100644 --- a/src/server/scripts/Commands/cs_mmaps.cpp +++ b/src/server/scripts/Commands/cs_mmaps.cpp @@ -141,7 +141,7 @@ public: // calculate navmesh tile location uint32 terrainMapId = PhasingHandler::GetTerrainMapId(player->GetPhaseShift(), player->GetMapId(), player->GetMap()->GetTerrain(), x, y); - handler->PSendSysMessage("%04u%02i%02i.mmtile", terrainMapId, gx, gy); + handler->PSendSysMessage("%04u_%02i_%02i.mmtile", terrainMapId, gx, gy); handler->PSendSysMessage("tileloc [%i, %i]", gy, gx); dtNavMesh const* navmesh = MMAP::MMapManager::instance()->GetNavMesh(terrainMapId); diff --git a/src/tools/mmaps_generator/Info/readme.txt b/src/tools/mmaps_generator/Info/readme.txt index 5178138c65f..dcd3e635913 100644 --- a/src/tools/mmaps_generator/Info/readme.txt +++ b/src/tools/mmaps_generator/Info/readme.txt @@ -1,5 +1,11 @@ R"(Generator command line args +--input [path] Directory to use for reading maps and vmaps + Default: current_directory + +--output [path] Directory to write generated data to + Default: current_directory + --threads [#] Max number of threads used by the generator Default: Same as CPU cores diff --git a/src/tools/mmaps_generator/IntermediateValues.cpp b/src/tools/mmaps_generator/IntermediateValues.cpp index bb88f1f9c72..858ac25d418 100644 --- a/src/tools/mmaps_generator/IntermediateValues.cpp +++ b/src/tools/mmaps_generator/IntermediateValues.cpp @@ -31,16 +31,16 @@ namespace MMAP rcFreePolyMeshDetail(polyMeshDetail); } - void IntermediateValues::writeIV(uint32 mapID, uint32 tileX, uint32 tileY) + void IntermediateValues::writeIV(boost::filesystem::path const& outputDirectory, std::string_view fileNameSuffix, uint32 mapID, uint32 tileX, uint32 tileY) { TC_LOG_INFO("maps.mmapgen.debug", "[Map {:04}] [{:02},{:02}]: Writing debug output intermediate values...", mapID, tileX, tileY); - auto debugWrite = [&](char const* extension, auto const* data) + auto debugWrite = [=, outputDirectory = outputDirectory.generic_string()](char const* extension, auto const* data) { - std::string fileName = Trinity::StringFormat("meshes/{:04}{:02}{:02}.{}", mapID, tileX, tileY, extension); + std::string fileName = Trinity::StringFormat("{}/meshes/{:04}_{:02}_{:02}{}.{}", outputDirectory, mapID, tileX, tileY, fileNameSuffix, extension); if (auto file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fileName.c_str(), "wb"))) { - this->debugWrite(file.get(), data); + IntermediateValues::debugWrite(file.get(), data); } else TC_LOG_ERROR("maps.mmapgen.debug", "{}: [{:04}-{:02},{:02}] Failed to open {} for writing!", strerror(errno), mapID, tileX, tileY, fileName); @@ -188,10 +188,10 @@ namespace MMAP fwrite(mesh->meshes, sizeof(int), mesh->nmeshes*4, file); } - void IntermediateValues::generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData const& meshData) + void IntermediateValues::generateObjFile(boost::filesystem::path const& outputDirectory, std::string_view fileNameSuffix, uint32 mapID, uint32 tileX, uint32 tileY, MeshData const& meshData) { std::string objFileName; - objFileName = Trinity::StringFormat("meshes/map{:04}{:02}{:02}.obj", mapID, tileX, tileY); + objFileName = Trinity::StringFormat("{}/meshes/map{:04}_{:02}_{:02}{}.obj", outputDirectory.generic_string(), mapID, tileX, tileY, fileNameSuffix); auto objFile = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(objFileName.c_str(), "wb")); if (!objFile) @@ -221,7 +221,7 @@ namespace MMAP TC_LOG_INFO("maps.mmapgen.debug", "[Map {:04}] [{:02},{:02}]: Writing debug output object file...", mapID, tileX, tileY); - objFileName = Trinity::StringFormat("meshes/map{:04}.map", mapID); + objFileName = Trinity::StringFormat("{}/meshes/map{:04}.map", outputDirectory.generic_string(), mapID); objFile.reset(fopen(objFileName.c_str(), "wb")); if (!objFile) @@ -233,7 +233,7 @@ namespace MMAP char b = '\0'; fwrite(&b, sizeof(char), 1, objFile.get()); - objFileName = Trinity::StringFormat("meshes/map{:04}{:02}{:02}.mesh", mapID, tileX, tileY); + objFileName = Trinity::StringFormat("{}/meshes/map{:04}_{:02}_{:02}{}.mesh", outputDirectory.generic_string(), mapID, tileX, tileY, fileNameSuffix); objFile.reset(fopen(objFileName.c_str(), "wb")); if (!objFile) { diff --git a/src/tools/mmaps_generator/IntermediateValues.h b/src/tools/mmaps_generator/IntermediateValues.h index 75ade2259e3..f8578683e61 100644 --- a/src/tools/mmaps_generator/IntermediateValues.h +++ b/src/tools/mmaps_generator/IntermediateValues.h @@ -64,15 +64,15 @@ namespace MMAP return *this; } - void writeIV(uint32 mapID, uint32 tileX, uint32 tileY); + void writeIV(boost::filesystem::path const& outputDirectory, std::string_view fileNameSuffix, uint32 mapID, uint32 tileX, uint32 tileY); - void debugWrite(FILE* file, rcHeightfield const* mesh); - void debugWrite(FILE* file, rcCompactHeightfield const* chf); - void debugWrite(FILE* file, rcContourSet const* cs); - void debugWrite(FILE* file, rcPolyMesh const* mesh); - void debugWrite(FILE* file, rcPolyMeshDetail const* mesh); + static void debugWrite(FILE* file, rcHeightfield const* mesh); + static void debugWrite(FILE* file, rcCompactHeightfield const* chf); + static void debugWrite(FILE* file, rcContourSet const* cs); + static void debugWrite(FILE* file, rcPolyMesh const* mesh); + static void debugWrite(FILE* file, rcPolyMeshDetail const* mesh); - void generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData const& meshData); + void generateObjFile(boost::filesystem::path const& outputDirectory, std::string_view fileNameSuffix, uint32 mapID, uint32 tileX, uint32 tileY, MeshData const& meshData); }; } #endif diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp index f6bc2ed47cb..65a41d7216c 100644 --- a/src/tools/mmaps_generator/MapBuilder.cpp +++ b/src/tools/mmaps_generator/MapBuilder.cpp @@ -37,7 +37,7 @@ namespace MMAP { MapTileBuilder::MapTileBuilder(MapBuilder* mapBuilder, Optional<float> maxWalkableAngle, Optional<float> maxWalkableAngleNotSteep, bool skipLiquid, bool bigBaseUnit, bool debugOutput, std::vector<OffMeshData> const* offMeshConnections) : - TileBuilder(maxWalkableAngle, maxWalkableAngleNotSteep, skipLiquid, bigBaseUnit, debugOutput, offMeshConnections), + TileBuilder(mapBuilder->m_inputDirectory, mapBuilder->m_outputDirectory, maxWalkableAngle, maxWalkableAngleNotSteep, skipLiquid, bigBaseUnit, debugOutput, offMeshConnections), m_mapBuilder(mapBuilder), m_workerThread(&MapTileBuilder::WorkerThread, this) { @@ -59,9 +59,12 @@ namespace MMAP ++m_mapBuilder->m_totalTilesProcessed; } - MapBuilder::MapBuilder(Optional<float> maxWalkableAngle, Optional<float> maxWalkableAngleNotSteep, bool skipLiquid, + MapBuilder::MapBuilder(boost::filesystem::path const& inputDirectory, boost::filesystem::path const& outputDirectory, + Optional<float> maxWalkableAngle, Optional<float> maxWalkableAngleNotSteep, bool skipLiquid, bool skipContinents, bool skipJunkMaps, bool skipBattlegrounds, bool debugOutput, bool bigBaseUnit, int mapid, char const* offMeshFilePath, unsigned int threads) : + m_inputDirectory (inputDirectory), + m_outputDirectory (outputDirectory), m_debugOutput (debugOutput), m_threads (threads), m_skipContinents (skipContinents), @@ -100,7 +103,7 @@ namespace MMAP void MapBuilder::discoverTiles() { boost::filesystem::directory_iterator end; - for (auto itr = boost::filesystem::directory_iterator("maps"); itr != end; ++itr) + for (auto itr = boost::filesystem::directory_iterator(m_inputDirectory / "maps"); itr != end; ++itr) { if (!boost::filesystem::is_regular_file(*itr)) continue; @@ -138,7 +141,7 @@ namespace MMAP } } - for (auto itr = boost::filesystem::directory_iterator("vmaps"); itr != end; ++itr) + for (auto itr = boost::filesystem::directory_iterator(m_inputDirectory / "vmaps"); itr != end; ++itr) { if (!boost::filesystem::is_directory(*itr)) continue; @@ -194,11 +197,11 @@ namespace MMAP OffMeshData offMesh; int32 scanned = sscanf(buf, "%u %u,%u (%f %f %f) (%f %f %f) %f %hhu %hu", &offMesh.MapId, &offMesh.TileX, &offMesh.TileY, &offMesh.From[0], &offMesh.From[1], &offMesh.From[2], &offMesh.To[0], &offMesh.To[1], &offMesh.To[2], - &offMesh.Radius, &offMesh.AreaId, &offMesh.Flags); + &offMesh.Radius, &offMesh.AreaId, reinterpret_cast<std::underlying_type_t<NavTerrainFlag>*>(&offMesh.Flags)); if (scanned < 10) continue; - offMesh.Bidirectional = true; + offMesh.ConnectionFlags = OFFMESH_CONNECTION_FLAG_BIDIRECTIONAL; if (scanned < 12) offMesh.Flags = NAV_GROUND; @@ -278,7 +281,7 @@ namespace MMAP } /**************************************************************************/ - void MapBuilder::buildMeshFromFile(char* name) + void MapBuilder::buildMeshFromFile(char const* name) { auto file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(name, "rb")); if (!file) @@ -444,23 +447,22 @@ 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; } - std::string fileName = Trinity::StringFormat("mmaps/{:04}.mmap", mapID); + std::string fileName = Trinity::StringFormat("{}/mmaps/{:04}.mmap", m_outputDirectory.generic_string(), mapID); auto file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fileName.c_str(), "wb")); if (!file) @@ -471,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()); } /**************************************************************************/ @@ -563,7 +571,7 @@ namespace MMAP /**************************************************************************/ bool MapTileBuilder::shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY) const { - std::string fileName = Trinity::StringFormat("mmaps/{:04}{:02}{:02}.mmtile", mapID, tileX, tileY); + std::string fileName = Trinity::StringFormat("{}/mmaps/{:04}_{:02}_{:02}.mmtile", m_outputDirectory.generic_string(), mapID, tileX, tileY); auto file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fileName.c_str(), "rb")); if (!file) return false; diff --git a/src/tools/mmaps_generator/MapBuilder.h b/src/tools/mmaps_generator/MapBuilder.h index 68ba4c77eff..d475280b577 100644 --- a/src/tools/mmaps_generator/MapBuilder.h +++ b/src/tools/mmaps_generator/MapBuilder.h @@ -23,6 +23,7 @@ #include "ProducerConsumerQueue.h" #include "TerrainBuilder.h" #include "TileBuilder.h" +#include <boost/filesystem/path.hpp> #include <DetourNavMesh.h> #include <atomic> #include <span> @@ -78,7 +79,9 @@ namespace MMAP friend class MapTileBuilder; public: - MapBuilder(Optional<float> maxWalkableAngle, + MapBuilder(boost::filesystem::path const& inputDirectory, + boost::filesystem::path const& outputDirectory, + Optional<float> maxWalkableAngle, Optional<float> maxWalkableAngleNotSteep, bool skipLiquid, bool skipContinents, @@ -92,7 +95,7 @@ namespace MMAP ~MapBuilder(); - void buildMeshFromFile(char* name); + void buildMeshFromFile(char const* name); // builds an mmap tile for the specified map and its mesh void buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY); @@ -122,6 +125,8 @@ namespace MMAP TileList m_tiles; + boost::filesystem::path m_inputDirectory; + boost::filesystem::path m_outputDirectory; bool m_debugOutput; std::vector<OffMeshData> m_offMeshConnections; diff --git a/src/tools/mmaps_generator/PathCommon.h b/src/tools/mmaps_generator/PathCommon.h index b0761cc2d9a..3ab2c43afda 100644 --- a/src/tools/mmaps_generator/PathCommon.h +++ b/src/tools/mmaps_generator/PathCommon.h @@ -15,64 +15,49 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef _MMAP_COMMON_H -#define _MMAP_COMMON_H +#ifndef TRINITYCORE_MMAP_COMMON_H +#define TRINITYCORE_MMAP_COMMON_H -#include "Define.h" -#include <memory> -#include <string> +#include "Common.h" +#include <boost/filesystem/directory.hpp> +#include <string_view> #include <unordered_map> -#include <vector> - -#ifndef _WIN32 - #include <cstddef> - #include <cstring> - #include <dirent.h> -#else - #include <Windows.h> -#endif - -#ifndef _WIN32 - #include <cerrno> -#endif - -namespace VMAP -{ - class VMapManager; -} namespace MMAP { - inline bool matchWildcardFilter(char const* filter, char const* str) + inline bool matchWildcardFilter(std::string_view filter, std::string_view str) { - if (!filter || !str) + if (filter.empty() || str.empty()) return false; + auto filterItr = filter.begin(); + auto filterEnd = filter.end(); + auto strItr = str.begin(); + auto strEnd = str.end(); + // end on null character - while (*filter && *str) + while (filterItr != filterEnd && strItr != strEnd) { - if (*filter == '*') + if (*filterItr == '*') { - if (*++filter == '\0') // wildcard at end of filter means all remaing chars match + if (++filterItr == filterEnd) // wildcard at end of filter means all remaing chars match return true; - for (;;) + while (*filterItr != *strItr) { - if (*filter == *str) - break; - if (*str == '\0') + if (strItr == strEnd) return false; // reached end of string without matching next filter character - str++; + ++strItr; } } - else if (*filter != *str) + else if (*filterItr != *strItr) return false; // mismatch - filter++; - str++; + ++filterItr; + ++strItr; } - return ((*filter == '\0' || (*filter == '*' && *++filter == '\0')) && *str == '\0'); + return (filterItr == filterEnd || (*filterItr == '*' && filterItr + 1 == filterEnd)) && strItr == strEnd; } enum ListFilesResult @@ -81,51 +66,26 @@ namespace MMAP LISTFILE_OK = 1 }; - inline ListFilesResult getDirContents(std::vector<std::string> &fileList, std::string dirpath = ".", std::string filter = "*") + inline ListFilesResult getDirContents(std::vector<std::string>& fileList, boost::filesystem::path const& dirpath, + boost::filesystem::file_type type = boost::filesystem::regular_file, std::string_view filter = "*"sv) { - #ifdef WIN32 - HANDLE hFind; - WIN32_FIND_DATAA findFileInfo; - std::string directory; - - directory = dirpath + "/" + filter; - - hFind = FindFirstFileA(directory.c_str(), &findFileInfo); - - if (hFind == INVALID_HANDLE_VALUE) + boost::system::error_code ec; + boost::filesystem::directory_iterator dirItr(dirpath, ec); + if (ec) return LISTFILE_DIRECTORY_NOT_FOUND; - do - { - if (strcmp(findFileInfo.cFileName, ".") != 0 && strcmp(findFileInfo.cFileName, "..") != 0) - fileList.push_back(std::string(findFileInfo.cFileName)); - } - while (FindNextFileA(hFind, &findFileInfo)); - FindClose(hFind); + for (boost::filesystem::directory_entry const& dirEntry : dirItr) + { + if (dirEntry.status(ec).type() != type || ec) + continue; - #else - const char *p = dirpath.c_str(); - DIR * dirp = opendir(p); - struct dirent * dp; + std::string fileName = dirEntry.path().filename().string(); + if (!matchWildcardFilter(filter, fileName)) + continue; - while (dirp) - { - errno = 0; - if ((dp = readdir(dirp)) != nullptr) - { - if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0 && matchWildcardFilter(filter.c_str(), dp->d_name)) - fileList.push_back(std::string(dp->d_name)); - } - else - break; + fileList.push_back(std::move(fileName)); } - if (dirp) - closedir(dirp); - else - return LISTFILE_DIRECTORY_NOT_FOUND; - #endif - return LISTFILE_OK; } diff --git a/src/tools/mmaps_generator/PathGenerator.cpp b/src/tools/mmaps_generator/PathGenerator.cpp index af4eb816626..452c63855fc 100644 --- a/src/tools/mmaps_generator/PathGenerator.cpp +++ b/src/tools/mmaps_generator/PathGenerator.cpp @@ -87,9 +87,10 @@ void SetupLogging(Trinity::Asio::IoContext* ioContext) log->CreateLoggerFromConfigLine("Logger.maps.mmapgen", "2,Console"); // LOG_LEVEL_DEBUG | Console appender } -bool checkDirectories(bool debugOutput, std::vector<std::string>& dbcLocales) +bool checkDirectories(boost::filesystem::path const& inputDirectory, boost::filesystem::path const& outputDirectory, + bool debugOutput, std::vector<std::string>& dbcLocales) { - if (MMAP::getDirContents(dbcLocales, "dbc") == MMAP::LISTFILE_DIRECTORY_NOT_FOUND || dbcLocales.empty()) + if (MMAP::getDirContents(dbcLocales, inputDirectory / "dbc", boost::filesystem::directory_file) == MMAP::LISTFILE_DIRECTORY_NOT_FOUND || dbcLocales.empty()) { TC_LOG_ERROR("tool.mmapgen", "'dbc' directory is empty or does not exist"); return false; @@ -97,39 +98,32 @@ bool checkDirectories(bool debugOutput, std::vector<std::string>& dbcLocales) std::vector<std::string> dirFiles; - if (MMAP::getDirContents(dirFiles, "maps") == MMAP::LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty()) + if (MMAP::getDirContents(dirFiles, inputDirectory / "maps") == MMAP::LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty()) { TC_LOG_ERROR("tool.mmapgen", "'maps' directory is empty or does not exist"); return false; } dirFiles.clear(); - if (MMAP::getDirContents(dirFiles, "vmaps/0000", "*.vmtree") == MMAP::LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty()) + if (MMAP::getDirContents(dirFiles, inputDirectory / "vmaps" / "0000", boost::filesystem::regular_file, "*.vmtree") == MMAP::LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty()) { TC_LOG_ERROR("tool.mmapgen", "'vmaps' directory is empty or does not exist"); return false; } - dirFiles.clear(); - if (MMAP::getDirContents(dirFiles, "mmaps") == MMAP::LISTFILE_DIRECTORY_NOT_FOUND) + boost::system::error_code ec; + if (!boost::filesystem::create_directories(outputDirectory / "mmaps", ec) && ec) { - if (!boost::filesystem::create_directory("mmaps")) - { - TC_LOG_ERROR("tool.mmapgen", "'mmaps' directory does not exist and failed to create it"); - return false; - } + TC_LOG_ERROR("tool.mmapgen", "'mmaps' directory does not exist and failed to create it"); + return false; } - dirFiles.clear(); if (debugOutput) { - if (MMAP::getDirContents(dirFiles, "meshes") == MMAP::LISTFILE_DIRECTORY_NOT_FOUND) + if (!boost::filesystem::create_directories(outputDirectory / "meshes", ec) && ec) { - if (!boost::filesystem::create_directory("meshes")) - { - TC_LOG_ERROR("tool.mmapgen", "'meshes' directory does not exist and failed to create it (no place to put debugOutput files)"); - return false; - } + TC_LOG_ERROR("tool.mmapgen", "'meshes' directory does not exist and failed to create it (no place to put debugOutput files)"); + return false; } } @@ -144,21 +138,23 @@ int finish(char const* message, int returnValue) } bool handleArgs(int argc, char** argv, - int &mapnum, - int &tileX, - int &tileY, + int& mapnum, + int& tileX, + int& tileY, Optional<float>& maxAngle, Optional<float>& maxAngleNotSteep, - bool &skipLiquid, - bool &skipContinents, - bool &skipJunkMaps, - bool &skipBattlegrounds, - bool &debugOutput, - bool &silent, - bool &bigBaseUnit, - char* &offMeshInputPath, - char* &file, - unsigned int& threads) + bool& skipLiquid, + bool& skipContinents, + bool& skipJunkMaps, + bool& skipBattlegrounds, + bool& debugOutput, + bool& silent, + bool& bigBaseUnit, + char const*& offMeshInputPath, + char const*& file, + unsigned int& threads, + boost::filesystem::path& inputDirectory, + boost::filesystem::path& outputDirectory) { char* param = nullptr; [[maybe_unused]] bool allowDebug = false; @@ -314,6 +310,22 @@ bool handleArgs(int argc, char** argv, offMeshInputPath = param; } + else if (strcmp(argv[i], "--input") == 0) + { + param = argv[++i]; + if (!param) + return false; + + inputDirectory = param; + } + else if (strcmp(argv[i], "--output") == 0) + { + param = argv[++i]; + if (!param) + return false; + + outputDirectory = param; + } else if (strcmp(argv[i], "--allowDebug") == 0) { allowDebug = true; @@ -349,11 +361,11 @@ bool handleArgs(int argc, char** argv, return true; } -std::unordered_map<uint32, uint8> LoadLiquid(std::string const& locale, bool silent, int32 errorExitCode) +std::unordered_map<uint32, uint8> LoadLiquid(boost::filesystem::path const& inputDirectory, std::string const& locale, bool silent, int32 errorExitCode) { DB2FileLoader liquidDb2; std::unordered_map<uint32, uint8> liquidData; - DB2FileSystemSource liquidTypeSource((boost::filesystem::path("dbc") / locale / "LiquidType.db2").string()); + DB2FileSystemSource liquidTypeSource((inputDirectory / "dbc" / locale / "LiquidType.db2").string()); try { liquidDb2.Load(&liquidTypeSource, &LiquidTypeLoadInfo::Instance); @@ -377,10 +389,10 @@ std::unordered_map<uint32, uint8> LoadLiquid(std::string const& locale, bool sil return liquidData; } -void LoadMap(std::string const& locale, bool silent, int32 errorExitCode) +void LoadMap(boost::filesystem::path const& inputDirectory, std::string const& locale, bool silent, int32 errorExitCode) { DB2FileLoader mapDb2; - DB2FileSystemSource mapSource((boost::filesystem::path("dbc") / locale / "Map.db2").string()); + DB2FileSystemSource mapSource((inputDirectory / "dbc" / locale / "Map.db2").string()); try { mapDb2.Load(&mapSource, &MapLoadInfo::Instance); @@ -442,13 +454,16 @@ int main(int argc, char** argv) debugOutput = false, silent = false, bigBaseUnit = false; - char* offMeshInputPath = nullptr; - char* file = nullptr; + char const* offMeshInputPath = nullptr; + char const* file = nullptr; + boost::filesystem::path inputDirectory = boost::filesystem::current_path(); + boost::filesystem::path outputDirectory = boost::filesystem::current_path(); bool validParam = handleArgs(argc, argv, mapnum, tileX, tileY, maxAngle, maxAngleNotSteep, skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds, - debugOutput, silent, bigBaseUnit, offMeshInputPath, file, threads); + debugOutput, silent, bigBaseUnit, offMeshInputPath, file, threads, + inputDirectory, outputDirectory); if (!validParam) return silent ? -1 : finish("You have specified invalid parameters", -1); @@ -466,14 +481,14 @@ int main(int argc, char** argv) } std::vector<std::string> dbcLocales; - if (!checkDirectories(debugOutput, dbcLocales)) + if (!checkDirectories(inputDirectory, outputDirectory, debugOutput, dbcLocales)) return silent ? -3 : finish("Press ENTER to close...", -3); - _liquidTypes = LoadLiquid(dbcLocales[0], silent, -5); + _liquidTypes = LoadLiquid(inputDirectory, dbcLocales[0], silent, -5); - LoadMap(dbcLocales[0], silent, -4); + LoadMap(inputDirectory, dbcLocales[0], silent, -4); - MMAP::MapBuilder builder(maxAngle, maxAngleNotSteep, skipLiquid, skipContinents, skipJunkMaps, + MMAP::MapBuilder builder(inputDirectory, outputDirectory, maxAngle, maxAngleNotSteep, skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds, debugOutput, bigBaseUnit, mapnum, offMeshInputPath, threads); uint32 start = getMSTime(); diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp index 241a724a46f..e93f2b5a57d 100644 --- a/src/tools/mmaps_generator/TerrainBuilder.cpp +++ b/src/tools/mmaps_generator/TerrainBuilder.cpp @@ -29,7 +29,11 @@ namespace MMAP { - TerrainBuilder::TerrainBuilder(bool skipLiquid) : m_skipLiquid (skipLiquid){ } + TerrainBuilder::TerrainBuilder(boost::filesystem::path const& inputDirectory, bool skipLiquid) : + m_inputDirectory(inputDirectory), + m_skipLiquid (skipLiquid) + { + } /**************************************************************************/ void TerrainBuilder::getLoopVars(Spot portion, int& loopStart, int& loopEnd, int& loopInc) @@ -79,7 +83,7 @@ namespace MMAP /**************************************************************************/ bool TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, VMAP::VMapManager* vmapManager, Spot portion) { - std::string mapFileName = Trinity::StringFormat("maps/{:04}_{:02}_{:02}.map", mapID, tileX, tileY); + std::string mapFileName = Trinity::StringFormat("{}/maps/{:04}_{:02}_{:02}.map", m_inputDirectory.generic_string(), mapID, tileX, tileY); auto mapFile = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(mapFileName.c_str(), "rb")); if (!mapFile) @@ -87,7 +91,7 @@ namespace MMAP int32 parentMapId = vmapManager->getParentMapId(mapID); while (!mapFile && parentMapId != -1) { - mapFileName = Trinity::StringFormat("maps/{:04}_{:02}_{:02}.map", parentMapId, tileX, tileY); + mapFileName = Trinity::StringFormat("{}/maps/{:04}_{:02}_{:02}.map", m_inputDirectory.generic_string(), parentMapId, tileX, tileY); mapFile.reset(fopen(mapFileName.c_str(), "rb")); parentMapId = vmapManager->getParentMapId(parentMapId); } @@ -563,7 +567,7 @@ namespace MMAP /**************************************************************************/ bool TerrainBuilder::loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, VMAP::VMapManager* vmapManager) { - VMAP::LoadResult result = vmapManager->loadMap("vmaps", mapID, tileX, tileY); + VMAP::LoadResult result = vmapManager->loadMap((m_inputDirectory / "vmaps").string(), mapID, tileX, tileY); if (result != VMAP::LoadResult::Success) return false; @@ -588,106 +592,109 @@ namespace MMAP // now we have a model to add to the meshdata retval = true; - std::vector<VMAP::GroupModel> const& groupModels = worldModel->getGroupModels(); - - // all M2s need to have triangle indices reversed - bool isM2 = worldModel->IsM2(); - - // transform data - float scale = instance.iScale; - G3D::Matrix3 const& rotation = instance.GetInvRot(); G3D::Vector3 position = instance.iPos; position.x -= 32 * GRID_SIZE; position.y -= 32 * GRID_SIZE; + loadVMapModel(worldModel, position, instance.GetInvRot(), instance.iScale, meshData, vmapManager); + } + + return retval; + } + + void TerrainBuilder::loadVMapModel(VMAP::WorldModel const* worldModel, G3D::Vector3 const& position, G3D::Matrix3 const& rotation, float scale, + MeshData& meshData, VMAP::VMapManager* vmapManager) + { + std::vector<VMAP::GroupModel> const& groupModels = worldModel->getGroupModels(); - for (std::vector<VMAP::GroupModel>::const_iterator it = groupModels.begin(); it != groupModels.end(); ++it) + // all M2s need to have triangle indices reversed + bool isM2 = worldModel->IsM2(); + + // transform data + for (std::vector<VMAP::GroupModel>::const_iterator it = groupModels.begin(); it != groupModels.end(); ++it) + { + // first handle collision mesh + int offset = meshData.solidVerts.size() / 3; + transformVertices(it->GetVertices(), meshData.solidVerts, scale, rotation, position); + copyIndices(it->GetTriangles(), meshData.solidTris, offset, isM2); + + // now handle liquid data + VMAP::WmoLiquid const* liquid = it->GetLiquid(); + if (liquid && liquid->GetFlagsStorage()) { - // first handle collision mesh - int offset = meshData.solidVerts.size() / 3; - transformVertices(it->GetVertices(), meshData.solidVerts, scale, rotation, position); - copyIndices(it->GetTriangles(), meshData.solidTris, offset, isM2); - - // now handle liquid data - VMAP::WmoLiquid const* liquid = it->GetLiquid(); - if (liquid && liquid->GetFlagsStorage()) + uint32 tilesX, tilesY; + G3D::Vector3 corner; + liquid->getPosInfo(tilesX, tilesY, corner); + uint32 vertsX = tilesX + 1; + uint32 vertsY = tilesY + 1; + uint8 const* flags = liquid->GetFlagsStorage(); + float const* data = liquid->GetHeightStorage(); + uint8 type = NAV_AREA_EMPTY; + + // convert liquid type to NavTerrain + EnumFlag<map_liquidHeaderTypeFlags> liquidFlags = map_liquidHeaderTypeFlags(vmapManager->GetLiquidFlagsPtr(liquid->GetType())); + if (liquidFlags.HasFlag(map_liquidHeaderTypeFlags::Water | map_liquidHeaderTypeFlags::Ocean)) + type = NAV_AREA_WATER; + else if (liquidFlags.HasFlag(map_liquidHeaderTypeFlags::Magma | map_liquidHeaderTypeFlags::Slime)) + type = NAV_AREA_MAGMA_SLIME; + + // indexing is weird... + // after a lot of trial and error, this is what works: + // vertex = y*vertsX+x + // tile = x*tilesY+y + // flag = y*tilesY+x + + uint32 liqOffset = meshData.liquidVerts.size() / 3; + meshData.liquidVerts.resize(meshData.liquidVerts.size() + vertsX * vertsY * 3); + float* liquidVerts = meshData.liquidVerts.data(); + for (uint32 x = 0; x < vertsX; ++x) { - uint32 tilesX, tilesY; - G3D::Vector3 corner; - liquid->getPosInfo(tilesX, tilesY, corner); - uint32 vertsX = tilesX + 1; - uint32 vertsY = tilesY + 1; - uint8 const* flags = liquid->GetFlagsStorage(); - float const* data = liquid->GetHeightStorage(); - uint8 type = NAV_AREA_EMPTY; - - // convert liquid type to NavTerrain - EnumFlag<map_liquidHeaderTypeFlags> liquidFlags = map_liquidHeaderTypeFlags(vmapManager->GetLiquidFlagsPtr(liquid->GetType())); - if (liquidFlags.HasFlag(map_liquidHeaderTypeFlags::Water | map_liquidHeaderTypeFlags::Ocean)) - type = NAV_AREA_WATER; - else if (liquidFlags.HasFlag(map_liquidHeaderTypeFlags::Magma | map_liquidHeaderTypeFlags::Slime)) - type = NAV_AREA_MAGMA_SLIME; - - // indexing is weird... - // after a lot of trial and error, this is what works: - // vertex = y*vertsX+x - // tile = x*tilesY+y - // flag = y*tilesY+x - - uint32 liqOffset = meshData.liquidVerts.size() / 3; - meshData.liquidVerts.resize(meshData.liquidVerts.size() + vertsX * vertsY * 3); - float* liquidVerts = meshData.liquidVerts.data(); - for (uint32 x = 0; x < vertsX; ++x) + for (uint32 y = 0; y < vertsY; ++y) { - for (uint32 y = 0; y < vertsY; ++y) - { - G3D::Vector3 vert = G3D::Vector3(corner.x + x * GRID_PART_SIZE, corner.y + y * GRID_PART_SIZE, data[y * vertsX + x]); - vert = vert * rotation * scale + position; - vert.x *= -1.f; - vert.y *= -1.f; - liquidVerts[(liqOffset + x * vertsY + y) * 3 + 0] = vert.y; - liquidVerts[(liqOffset + x * vertsY + y) * 3 + 1] = vert.z; - liquidVerts[(liqOffset + x * vertsY + y) * 3 + 2] = vert.x; - } + G3D::Vector3 vert = G3D::Vector3(corner.x + x * GRID_PART_SIZE, corner.y + y * GRID_PART_SIZE, data[y * vertsX + x]); + vert = vert * rotation * scale + position; + vert.x *= -1.f; + vert.y *= -1.f; + liquidVerts[(liqOffset + x * vertsY + y) * 3 + 0] = vert.y; + liquidVerts[(liqOffset + x * vertsY + y) * 3 + 1] = vert.z; + liquidVerts[(liqOffset + x * vertsY + y) * 3 + 2] = vert.x; } + } - std::size_t liquidSquares = 0; - for (uint32 x = 0; x < tilesX; ++x) + std::size_t liquidSquares = 0; + for (uint32 x = 0; x < tilesX; ++x) + { + for (uint32 y = 0; y < tilesY; ++y) { - for (uint32 y = 0; y < tilesY; ++y) + if ((flags[x + y * tilesX] & 0x0f) != 0x0f) { - if ((flags[x + y * tilesX] & 0x0f) != 0x0f) - { - uint32 square = x * tilesY + y; - int idx1 = square + x; - int idx2 = square + 1 + x; - int idx3 = square + tilesY + 1 + 1 + x; - int idx4 = square + tilesY + 1 + x; - - std::size_t liquidTriOffset = meshData.liquidTris.size(); - meshData.liquidTris.resize(liquidTriOffset + 6); - int* liquidTris = meshData.liquidTris.data() + liquidTriOffset; - - // top triangle - liquidTris[0] = idx2 + liqOffset; - liquidTris[1] = idx1 + liqOffset; - liquidTris[2] = idx3 + liqOffset; - - // bottom triangle - liquidTris[3] = idx3 + liqOffset; - liquidTris[4] = idx1 + liqOffset; - liquidTris[5] = idx4 + liqOffset; - - ++liquidSquares; - } + uint32 square = x * tilesY + y; + int idx1 = square + x; + int idx2 = square + 1 + x; + int idx3 = square + tilesY + 1 + 1 + x; + int idx4 = square + tilesY + 1 + x; + + std::size_t liquidTriOffset = meshData.liquidTris.size(); + meshData.liquidTris.resize(liquidTriOffset + 6); + int* liquidTris = meshData.liquidTris.data() + liquidTriOffset; + + // top triangle + liquidTris[0] = idx2 + liqOffset; + liquidTris[1] = idx1 + liqOffset; + liquidTris[2] = idx3 + liqOffset; + + // bottom triangle + liquidTris[3] = idx3 + liqOffset; + liquidTris[4] = idx1 + liqOffset; + liquidTris[5] = idx4 + liqOffset; + + ++liquidSquares; } } - - meshData.liquidType.resize(meshData.liquidType.size() + liquidSquares * 2, type); } + + meshData.liquidType.resize(meshData.liquidType.size() + liquidSquares * 2, type); } } - - return retval; } /**************************************************************************/ @@ -793,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); diff --git a/src/tools/mmaps_generator/TerrainBuilder.h b/src/tools/mmaps_generator/TerrainBuilder.h index c1c01e2d0db..65ebe4508a9 100644 --- a/src/tools/mmaps_generator/TerrainBuilder.h +++ b/src/tools/mmaps_generator/TerrainBuilder.h @@ -18,8 +18,10 @@ #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> namespace VMAP { @@ -60,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; @@ -93,10 +82,12 @@ namespace MMAP class TerrainBuilder { public: - explicit TerrainBuilder(bool skipLiquid); + explicit TerrainBuilder(boost::filesystem::path const& inputDirectory, bool skipLiquid); void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, VMAP::VMapManager* vmapManager); bool loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, VMAP::VMapManager* vmapManager); + void loadVMapModel(VMAP::WorldModel const* worldModel, G3D::Vector3 const& position, G3D::Matrix3 const& rotation, float scale, + MeshData& meshData, VMAP::VMapManager* vmapManager); void loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, std::vector<OffMeshData> const& offMeshConnections); bool usesLiquids() const { return !m_skipLiquid; } @@ -114,6 +105,8 @@ namespace MMAP /// Sets loop variables for selecting only certain parts of a map's terrain static void getLoopVars(Spot portion, int& loopStart, int& loopEnd, int& loopInc); + boost::filesystem::path m_inputDirectory; + /// Controls whether liquids are loaded bool m_skipLiquid; diff --git a/src/tools/mmaps_generator/TileBuilder.cpp b/src/tools/mmaps_generator/TileBuilder.cpp index eff2f7bcf59..5654a14183c 100644 --- a/src/tools/mmaps_generator/TileBuilder.cpp +++ b/src/tools/mmaps_generator/TileBuilder.cpp @@ -69,13 +69,15 @@ namespace MMAP int TILES_PER_MAP; }; - TileBuilder::TileBuilder(Optional<float> maxWalkableAngle, Optional<float> maxWalkableAngleNotSteep, + TileBuilder::TileBuilder(boost::filesystem::path const& inputDirectory, boost::filesystem::path const& outputDirectory, + Optional<float> maxWalkableAngle, Optional<float> maxWalkableAngleNotSteep, bool skipLiquid, bool bigBaseUnit, bool debugOutput, std::vector<OffMeshData> const* offMeshConnections) : + m_outputDirectory(outputDirectory), m_maxWalkableAngle(maxWalkableAngle), m_maxWalkableAngleNotSteep(maxWalkableAngleNotSteep), m_bigBaseUnit(bigBaseUnit), m_debugOutput(debugOutput), - m_terrainBuilder(skipLiquid), + m_terrainBuilder(inputDirectory, skipLiquid), m_rcContext(false), m_offMeshConnections(offMeshConnections) { @@ -123,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]; @@ -145,7 +146,7 @@ namespace MMAP /**************************************************************************/ TileBuilder::TileResult TileBuilder::buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, float (&bmin)[3], float (&bmax)[3], - dtNavMeshParams const* navMeshParams) + dtNavMeshParams const* navMeshParams, std::string_view fileNameSuffix) { // console output std::string tileString = Trinity::StringFormat("[Map {:04}] [{:02},{:02}]:", mapID, tileX, tileY); @@ -375,7 +376,10 @@ namespace MMAP // will hold final navmesh unsigned char* navData = nullptr; - auto debugOutputWriter = Trinity::make_unique_ptr_with_deleter(m_debugOutput ? &iv : nullptr, [=, borderSize = static_cast<unsigned short>(config.borderSize), &meshData](IntermediateValues* intermediate) + auto debugOutputWriter = Trinity::make_unique_ptr_with_deleter(m_debugOutput ? &iv : nullptr, + [borderSize = static_cast<unsigned short>(config.borderSize), + outputDir = &m_outputDirectory, fileNameSuffix, + mapID, tileX, tileY, &meshData](IntermediateValues* intermediate) { // restore padding so that the debug visualization is correct for (std::ptrdiff_t i = 0; i < intermediate->polyMesh->nverts; ++i) @@ -385,8 +389,8 @@ namespace MMAP v[2] += borderSize; } - intermediate->generateObjFile(mapID, tileX, tileY, meshData); - intermediate->writeIV(mapID, tileX, tileY); + intermediate->generateObjFile(*outputDir, fileNameSuffix, mapID, tileX, tileY, meshData); + intermediate->writeIV(*outputDir, fileNameSuffix, mapID, tileX, tileY); }); // these values are checked within dtCreateNavMeshData - handle them here @@ -438,7 +442,8 @@ namespace MMAP return tileResult; } - void TileBuilder::saveMoveMapTileToFile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh, TileResult const& tileResult) + void TileBuilder::saveMoveMapTileToFile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh, + TileResult const& tileResult, std::string_view fileNameSuffix) { dtTileRef tileRef = 0; auto navMeshTile = Trinity::make_unique_ptr_with_deleter<dtTileRef*>(nullptr, [navMesh](dtTileRef const* ref) @@ -462,7 +467,7 @@ namespace MMAP } // file output - std::string fileName = Trinity::StringFormat("mmaps/{:04}{:02}{:02}.mmtile", mapID, tileX, tileY); + std::string fileName = Trinity::StringFormat("{}/mmaps/{:04}_{:02}_{:02}{}.mmtile", m_outputDirectory.generic_string(), mapID, tileX, tileY, fileNameSuffix); auto file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fileName.c_str(), "wb")); if (!file) { @@ -483,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; diff --git a/src/tools/mmaps_generator/TileBuilder.h b/src/tools/mmaps_generator/TileBuilder.h index f533a3d1afa..f7c4806c515 100644 --- a/src/tools/mmaps_generator/TileBuilder.h +++ b/src/tools/mmaps_generator/TileBuilder.h @@ -18,7 +18,7 @@ #ifndef TRINITYCORE_TILE_BUILDER_H #define TRINITYCORE_TILE_BUILDER_H -#include "Define.h" +#include "Common.h" #include "Memory.h" #include "StringFormat.h" #include "TerrainBuilder.h" @@ -34,7 +34,9 @@ using detour_unique_ptr = std::unique_ptr<unsigned char, decltype(Trinity::uniqu class TileBuilder { public: - TileBuilder(Optional<float> maxWalkableAngle, + TileBuilder(boost::filesystem::path const& inputDirectory, + boost::filesystem::path const& outputDirectory, + Optional<float> maxWalkableAngle, Optional<float> maxWalkableAngleNotSteep, bool skipLiquid, bool bigBaseUnit, @@ -63,14 +65,16 @@ public: MeshData& meshData, float (&bmin)[3], float (&bmax)[3], - dtNavMeshParams const* navMeshParams); + dtNavMeshParams const* navMeshParams, + std::string_view fileNameSuffix = ""sv); - void saveMoveMapTileToFile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh, TileResult const& tileResult); + void saveMoveMapTileToFile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh, + TileResult const& tileResult, std::string_view fileNameSuffix = ""sv); 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; @@ -79,7 +83,8 @@ public: virtual void OnTileDone() { } -private: +protected: + boost::filesystem::path m_outputDirectory; Optional<float> m_maxWalkableAngle; Optional<float> m_maxWalkableAngleNotSteep; bool m_bigBaseUnit; |
