aboutsummaryrefslogtreecommitdiff
path: root/src/tools/mmaps_generator
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/mmaps_generator')
-rw-r--r--src/tools/mmaps_generator/CMakeLists.txt4
-rw-r--r--src/tools/mmaps_generator/IntermediateValues.cpp252
-rw-r--r--src/tools/mmaps_generator/IntermediateValues.h78
-rw-r--r--src/tools/mmaps_generator/PathCommon.h5
-rw-r--r--src/tools/mmaps_generator/PathGenerator.cpp2
-rw-r--r--src/tools/mmaps_generator/TerrainBuilder.cpp810
-rw-r--r--src/tools/mmaps_generator/TerrainBuilder.h130
-rw-r--r--src/tools/mmaps_generator/TileBuilder.cpp569
-rw-r--r--src/tools/mmaps_generator/TileBuilder.h100
9 files changed, 3 insertions, 1947 deletions
diff --git a/src/tools/mmaps_generator/CMakeLists.txt b/src/tools/mmaps_generator/CMakeLists.txt
index 5b3bf0e20d8..10c26c81f69 100644
--- a/src/tools/mmaps_generator/CMakeLists.txt
+++ b/src/tools/mmaps_generator/CMakeLists.txt
@@ -17,10 +17,8 @@ add_executable(mmaps_generator ${PRIVATE_SOURCES})
target_link_libraries(mmaps_generator
PRIVATE
trinity-core-interface
- PUBLIC
extractor_common
- Recast
- zlib)
+ mmaps_common)
CollectIncludeDirectories(
${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/src/tools/mmaps_generator/IntermediateValues.cpp b/src/tools/mmaps_generator/IntermediateValues.cpp
deleted file mode 100644
index 858ac25d418..00000000000
--- a/src/tools/mmaps_generator/IntermediateValues.cpp
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "IntermediateValues.h"
-#include "Log.h"
-#include "Memory.h"
-#include "StringFormat.h"
-
-namespace MMAP
-{
- IntermediateValues::~IntermediateValues()
- {
- rcFreeCompactHeightfield(compactHeightfield);
- rcFreeHeightField(heightfield);
- rcFreeContourSet(contours);
- rcFreePolyMesh(polyMesh);
- rcFreePolyMeshDetail(polyMeshDetail);
- }
-
- 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 = [=, outputDirectory = outputDirectory.generic_string()](char const* extension, auto const* data)
- {
- 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")))
- {
- 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);
- };
-
- if (heightfield)
- debugWrite("hf", heightfield);
- if (compactHeightfield)
- debugWrite("chf", compactHeightfield);
- if (contours)
- debugWrite("cs", contours);
- if (polyMesh)
- debugWrite("pmesh", polyMesh);
- if (polyMeshDetail)
- debugWrite("dmesh", polyMeshDetail);
- }
-
- void IntermediateValues::debugWrite(FILE* file, rcHeightfield const* mesh)
- {
- if (!file || !mesh)
- return;
-
- fwrite(&(mesh->cs), sizeof(float), 1, file);
- fwrite(&(mesh->ch), sizeof(float), 1, file);
- fwrite(&(mesh->width), sizeof(int), 1, file);
- fwrite(&(mesh->height), sizeof(int), 1, file);
- fwrite(mesh->bmin, sizeof(float), 3, file);
- fwrite(mesh->bmax, sizeof(float), 3, file);
-
- for (int y = 0; y < mesh->height; ++y)
- for (int x = 0; x < mesh->width; ++x)
- {
- rcSpan* span = mesh->spans[x+y*mesh->width];
-
- // first, count the number of spans
- int spanCount = 0;
- while (span)
- {
- spanCount++;
- span = span->next;
- }
-
- // write the span count
- fwrite(&spanCount, sizeof(int), 1, file);
-
- // write the spans
- span = mesh->spans[x+y*mesh->width];
- while (span)
- {
- fwrite(span, sizeof(rcSpan), 1, file);
- span = span->next;
- }
- }
- }
-
- void IntermediateValues::debugWrite(FILE* file, rcCompactHeightfield const* chf)
- {
- if (!file | !chf)
- return;
-
- fwrite(&(chf->width), sizeof(chf->width), 1, file);
- fwrite(&(chf->height), sizeof(chf->height), 1, file);
- fwrite(&(chf->spanCount), sizeof(chf->spanCount), 1, file);
-
- fwrite(&(chf->walkableHeight), sizeof(chf->walkableHeight), 1, file);
- fwrite(&(chf->walkableClimb), sizeof(chf->walkableClimb), 1, file);
-
- fwrite(&(chf->maxDistance), sizeof(chf->maxDistance), 1, file);
- fwrite(&(chf->maxRegions), sizeof(chf->maxRegions), 1, file);
-
- fwrite(chf->bmin, sizeof(chf->bmin), 1, file);
- fwrite(chf->bmax, sizeof(chf->bmax), 1, file);
-
- fwrite(&(chf->cs), sizeof(chf->cs), 1, file);
- fwrite(&(chf->ch), sizeof(chf->ch), 1, file);
-
- int tmp = 0;
- if (chf->cells) tmp |= 1;
- if (chf->spans) tmp |= 2;
- if (chf->dist) tmp |= 4;
- if (chf->areas) tmp |= 8;
-
- fwrite(&tmp, sizeof(tmp), 1, file);
-
- if (chf->cells)
- fwrite(chf->cells, sizeof(rcCompactCell), chf->width*chf->height, file);
- if (chf->spans)
- fwrite(chf->spans, sizeof(rcCompactSpan), chf->spanCount, file);
- if (chf->dist)
- fwrite(chf->dist, sizeof(unsigned short), chf->spanCount, file);
- if (chf->areas)
- fwrite(chf->areas, sizeof(unsigned char), chf->spanCount, file);
- }
-
- void IntermediateValues::debugWrite(FILE* file, rcContourSet const* cs)
- {
- if (!file || !cs)
- return;
-
- fwrite(&(cs->cs), sizeof(float), 1, file);
- fwrite(&(cs->ch), sizeof(float), 1, file);
- fwrite(cs->bmin, sizeof(float), 3, file);
- fwrite(cs->bmax, sizeof(float), 3, file);
- fwrite(&(cs->nconts), sizeof(int), 1, file);
- for (int i = 0; i < cs->nconts; ++i)
- {
- fwrite(&cs->conts[i].area, sizeof(unsigned char), 1, file);
- fwrite(&cs->conts[i].reg, sizeof(unsigned short), 1, file);
- fwrite(&cs->conts[i].nverts, sizeof(int), 1, file);
- fwrite(cs->conts[i].verts, sizeof(int), cs->conts[i].nverts*4, file);
- fwrite(&cs->conts[i].nrverts, sizeof(int), 1, file);
- fwrite(cs->conts[i].rverts, sizeof(int), cs->conts[i].nrverts*4, file);
- }
- }
-
- void IntermediateValues::debugWrite(FILE* file, rcPolyMesh const* mesh)
- {
- if (!file || !mesh)
- return;
-
- fwrite(&(mesh->cs), sizeof(float), 1, file);
- fwrite(&(mesh->ch), sizeof(float), 1, file);
- fwrite(&(mesh->nvp), sizeof(int), 1, file);
- fwrite(mesh->bmin, sizeof(float), 3, file);
- fwrite(mesh->bmax, sizeof(float), 3, file);
- fwrite(&(mesh->nverts), sizeof(int), 1, file);
- fwrite(mesh->verts, sizeof(unsigned short), mesh->nverts*3, file);
- fwrite(&(mesh->npolys), sizeof(int), 1, file);
- fwrite(mesh->polys, sizeof(unsigned short), mesh->npolys*mesh->nvp*2, file);
- fwrite(mesh->flags, sizeof(unsigned short), mesh->npolys, file);
- fwrite(mesh->areas, sizeof(unsigned char), mesh->npolys, file);
- fwrite(mesh->regs, sizeof(unsigned short), mesh->npolys, file);
- }
-
- void IntermediateValues::debugWrite(FILE* file, rcPolyMeshDetail const* mesh)
- {
- if (!file || !mesh)
- return;
-
- fwrite(&(mesh->nverts), sizeof(int), 1, file);
- fwrite(mesh->verts, sizeof(float), mesh->nverts*3, file);
- fwrite(&(mesh->ntris), sizeof(int), 1, file);
- fwrite(mesh->tris, sizeof(char), mesh->ntris*4, file);
- fwrite(&(mesh->nmeshes), sizeof(int), 1, file);
- fwrite(mesh->meshes, sizeof(int), mesh->nmeshes*4, file);
- }
-
- 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", outputDirectory.generic_string(), mapID, tileX, tileY, fileNameSuffix);
-
- auto objFile = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(objFileName.c_str(), "wb"));
- if (!objFile)
- {
- TC_LOG_ERROR("maps.mmapgen.debug", "{}: Failed to open {} for writing!", strerror(errno), objFileName);
- return;
- }
-
- std::vector<float> allVerts;
- std::vector<int> allTris;
-
- allTris.insert(allTris.end(), meshData.liquidTris.begin(), meshData.liquidTris.end());
- allVerts.insert(allVerts.end(), meshData.liquidVerts.begin(), meshData.liquidVerts.end());
- TerrainBuilder::copyIndices(meshData.solidTris, allTris, allVerts.size() / 3);
- allVerts.insert(allVerts.end(), meshData.solidVerts.begin(), meshData.solidVerts.end());
-
- float* verts = allVerts.data();
- int vertCount = allVerts.size() / 3;
- int* tris = allTris.data();
- int triCount = allTris.size() / 3;
-
- for (std::size_t i = 0; i < allVerts.size() / 3; i++)
- fprintf(objFile.get(), "v %f %f %f\n", verts[i * 3], verts[i * 3 + 1], verts[i * 3 + 2]);
-
- for (std::size_t i = 0; i < allTris.size() / 3; i++)
- fprintf(objFile.get(), "f %i %i %i\n", tris[i * 3] + 1, tris[i * 3 + 1] + 1, tris[i * 3 + 2] + 1);
-
- 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", outputDirectory.generic_string(), mapID);
-
- objFile.reset(fopen(objFileName.c_str(), "wb"));
- if (!objFile)
- {
- TC_LOG_ERROR("maps.mmapgen.debug", "{}: Failed to open {} for writing!", strerror(errno), objFileName);
- return;
- }
-
- char b = '\0';
- fwrite(&b, sizeof(char), 1, objFile.get());
-
- 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)
- {
- TC_LOG_ERROR("maps.mmapgen.debug", "{}: Failed to open {} for writing!", strerror(errno), objFileName);
- return;
- }
-
- fwrite(&vertCount, sizeof(int), 1, objFile.get());
- fwrite(verts, sizeof(float), vertCount*3, objFile.get());
- fflush(objFile.get());
-
- fwrite(&triCount, sizeof(int), 1, objFile.get());
- fwrite(tris, sizeof(int), triCount*3, objFile.get());
- fflush(objFile.get());
- }
-}
diff --git a/src/tools/mmaps_generator/IntermediateValues.h b/src/tools/mmaps_generator/IntermediateValues.h
deleted file mode 100644
index f8578683e61..00000000000
--- a/src/tools/mmaps_generator/IntermediateValues.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef _INTERMEDIATE_VALUES_H
-#define _INTERMEDIATE_VALUES_H
-
-#include "TerrainBuilder.h"
-#include <Recast.h>
-#include <utility>
-
-namespace MMAP
-{
- // this class gathers all debug info holding and output
- struct IntermediateValues
- {
- rcHeightfield* heightfield;
- rcCompactHeightfield* compactHeightfield;
- rcContourSet* contours;
- rcPolyMesh* polyMesh;
- rcPolyMeshDetail* polyMeshDetail;
-
- IntermediateValues() : heightfield(nullptr), compactHeightfield(nullptr),
- contours(nullptr), polyMesh(nullptr), polyMeshDetail(nullptr) {}
-
- IntermediateValues(IntermediateValues const&) = delete;
-
- IntermediateValues(IntermediateValues&& other) noexcept :
- heightfield(std::exchange(other.heightfield, nullptr)),
- compactHeightfield(std::exchange(other.compactHeightfield, nullptr)),
- contours(std::exchange(other.contours, nullptr)),
- polyMesh(std::exchange(other.polyMesh, nullptr)),
- polyMeshDetail(std::exchange(other.polyMeshDetail, nullptr))
- {
- }
-
- ~IntermediateValues();
-
- IntermediateValues& operator=(IntermediateValues const&) = delete;
-
- IntermediateValues& operator=(IntermediateValues&& other) noexcept
- {
- if (this != std::addressof(other))
- {
- heightfield = std::exchange(other.heightfield, nullptr);
- compactHeightfield = std::exchange(other.compactHeightfield, nullptr);
- contours = std::exchange(other.contours, nullptr);
- polyMesh = std::exchange(other.polyMesh, nullptr);
- polyMeshDetail = std::exchange(other.polyMeshDetail, nullptr);
- }
- return *this;
- }
-
- void writeIV(boost::filesystem::path const& outputDirectory, std::string_view fileNameSuffix, uint32 mapID, uint32 tileX, uint32 tileY);
-
- 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(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/PathCommon.h b/src/tools/mmaps_generator/PathCommon.h
index 3ab2c43afda..f348b53aa1f 100644
--- a/src/tools/mmaps_generator/PathCommon.h
+++ b/src/tools/mmaps_generator/PathCommon.h
@@ -98,11 +98,6 @@ namespace MMAP
};
extern std::unordered_map<uint32, MapEntry> sMapStore;
-
- namespace VMapFactory
- {
- std::unique_ptr<VMAP::VMapManager> CreateVMapManager(uint32 mapId);
-}
}
#endif
diff --git a/src/tools/mmaps_generator/PathGenerator.cpp b/src/tools/mmaps_generator/PathGenerator.cpp
index 452c63855fc..7204da980a3 100644
--- a/src/tools/mmaps_generator/PathGenerator.cpp
+++ b/src/tools/mmaps_generator/PathGenerator.cpp
@@ -488,6 +488,8 @@ int main(int argc, char** argv)
LoadMap(inputDirectory, dbcLocales[0], silent, -4);
+ MMAP::CreateVMapManager = &MMAP::VMapFactory::CreateVMapManager;
+
MMAP::MapBuilder builder(inputDirectory, outputDirectory, maxAngle, maxAngleNotSteep, skipLiquid, skipContinents, skipJunkMaps,
skipBattlegrounds, debugOutput, bigBaseUnit, mapnum, offMeshInputPath, threads);
diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp
deleted file mode 100644
index e93f2b5a57d..00000000000
--- a/src/tools/mmaps_generator/TerrainBuilder.cpp
+++ /dev/null
@@ -1,810 +0,0 @@
-/*
- * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "TerrainBuilder.h"
-#include "Log.h"
-#include "MapDefines.h"
-#include "MapTree.h"
-#include "MMapDefines.h"
-#include "Memory.h"
-#include "ModelInstance.h"
-#include "StringFormat.h"
-#include "Util.h"
-#include "VMapManager.h"
-#include <unordered_map>
-
-namespace MMAP
-{
- 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)
- {
- switch (portion)
- {
- case ENTIRE:
- loopStart = 0;
- loopEnd = V8_SIZE_SQ;
- loopInc = 1;
- break;
- case TOP:
- loopStart = 0;
- loopEnd = V8_SIZE;
- loopInc = 1;
- break;
- case LEFT:
- loopStart = 0;
- loopEnd = V8_SIZE_SQ - V8_SIZE + 1;
- loopInc = V8_SIZE;
- break;
- case RIGHT:
- loopStart = V8_SIZE - 1;
- loopEnd = V8_SIZE_SQ;
- loopInc = V8_SIZE;
- break;
- case BOTTOM:
- loopStart = V8_SIZE_SQ - V8_SIZE;
- loopEnd = V8_SIZE_SQ;
- loopInc = 1;
- break;
- }
- }
-
- /**************************************************************************/
- void TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, VMAP::VMapManager* vmapManager)
- {
- if (loadMap(mapID, tileX, tileY, meshData, vmapManager, ENTIRE))
- {
- loadMap(mapID, tileX, tileY+1, meshData, vmapManager, LEFT);
- loadMap(mapID, tileX, tileY-1, meshData, vmapManager, RIGHT);
- loadMap(mapID, tileX+1, tileY, meshData, vmapManager, TOP);
- loadMap(mapID, tileX-1, tileY, meshData, vmapManager, BOTTOM);
- }
- }
-
- /**************************************************************************/
- 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", m_inputDirectory.generic_string(), mapID, tileX, tileY);
-
- auto mapFile = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(mapFileName.c_str(), "rb"));
- if (!mapFile)
- {
- int32 parentMapId = vmapManager->getParentMapId(mapID);
- while (!mapFile && parentMapId != -1)
- {
- 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);
- }
- }
-
- if (!mapFile)
- return false;
-
- map_fileheader fheader;
- if (fread(&fheader, sizeof(map_fileheader), 1, mapFile.get()) != 1 ||
- fheader.versionMagic != MapVersionMagic)
- {
- TC_LOG_ERROR("maps.mmapgen", "{} is the wrong version, please extract new .map files", mapFileName);
- return false;
- }
-
- map_heightHeader hheader;
- fseek(mapFile.get(), fheader.heightMapOffset, SEEK_SET);
-
- bool haveTerrain = false;
- bool haveLiquid = false;
- if (fread(&hheader, sizeof(map_heightHeader), 1, mapFile.get()) == 1)
- {
- haveTerrain = !hheader.flags.HasFlag(map_heightHeaderFlags::NoHeight);
- haveLiquid = fheader.liquidMapOffset && !m_skipLiquid;
- }
-
- // no data in this map file
- if (!haveTerrain && !haveLiquid)
- return false;
-
- // data used later
- uint8 holes[16][16][8] = { };
- uint16 liquid_entry[16][16] = { };
- map_liquidHeaderTypeFlags liquid_flags[16][16] = { };
- std::vector<int> ltriangles;
- std::vector<int> ttriangles;
-
- // terrain data
- if (haveTerrain)
- {
- float heightMultiplier;
- float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ];
- size_t expected = V9_SIZE_SQ + V8_SIZE_SQ;
-
- if (hheader.flags.HasFlag(map_heightHeaderFlags::HeightAsInt8))
- {
- uint8 v9[V9_SIZE_SQ];
- uint8 v8[V8_SIZE_SQ];
- size_t count = 0;
- count += fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile.get());
- count += fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile.get());
- if (count != expected)
- TC_LOG_ERROR("maps.mmapgen", "TerrainBuilder::loadMap: Failed to read {} height data expected {}, read {}", mapFileName, expected, count);
-
- heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255;
-
- for (int i = 0; i < V9_SIZE_SQ; ++i)
- V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
-
- for (int i = 0; i < V8_SIZE_SQ; ++i)
- V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
- }
- else if (hheader.flags.HasFlag(map_heightHeaderFlags::HeightAsInt16))
- {
- uint16 v9[V9_SIZE_SQ];
- uint16 v8[V8_SIZE_SQ];
- size_t count = 0;
- count += fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile.get());
- count += fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile.get());
- if (count != expected)
- TC_LOG_ERROR("maps.mmapgen", "TerrainBuilder::loadMap: Failed to read {} height data expected {}, read {}", mapFileName, expected, count);
-
- heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535;
-
- for (int i = 0; i < V9_SIZE_SQ; ++i)
- V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
-
- for (int i = 0; i < V8_SIZE_SQ; ++i)
- V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
- }
- else
- {
- size_t count = 0;
- count += fread(V9, sizeof(float), V9_SIZE_SQ, mapFile.get());
- count += fread(V8, sizeof(float), V8_SIZE_SQ, mapFile.get());
- if (count != expected)
- TC_LOG_ERROR("maps.mmapgen", "TerrainBuilder::loadMap: Failed to read {} height data expected {}, read {}", mapFileName, expected, count);
- }
-
- // hole data
- if (fheader.holesSize != 0)
- {
- memset(holes, 0, fheader.holesSize);
- fseek(mapFile.get(), fheader.holesOffset, SEEK_SET);
- if (fread(holes, fheader.holesSize, 1, mapFile.get()) != 1)
- TC_LOG_ERROR("maps.mmapgen", "TerrainBuilder::loadMap: Failed to read {} holes data expected {}, read {}", mapFileName, 1, 0);
- }
-
- int count = meshData.solidVerts.size() / 3;
- meshData.solidVerts.resize(meshData.solidVerts.size() + (V9_SIZE_SQ + V8_SIZE_SQ) * 3);
- float* solidVerts = meshData.solidVerts.data() + count * 3;
-
- float xoffset = (float(tileX)-32)*GRID_SIZE;
- float yoffset = (float(tileY)-32)*GRID_SIZE;
-
- for (int i = 0; i < V9_SIZE_SQ; ++i)
- {
- getHeightCoord(i, GRID_V9, xoffset, yoffset, solidVerts, V9);
- solidVerts += 3;
- }
-
- for (int i = 0; i < V8_SIZE_SQ; ++i)
- {
- getHeightCoord(i, GRID_V8, xoffset, yoffset, solidVerts, V8);
- solidVerts += 3;
- }
-
- int loopStart = 0, loopEnd = 0, loopInc = 0;
- getLoopVars(portion, loopStart, loopEnd, loopInc);
- for (int i = loopStart; i < loopEnd; i+=loopInc)
- {
- std::size_t trianglesOffset = ttriangles.size();
- ttriangles.resize(ttriangles.size() + 12);
- getHeightTriangle(i, TOP, ttriangles.data() + trianglesOffset + 0, count);
- getHeightTriangle(i, RIGHT, ttriangles.data() + trianglesOffset + 3, count);
- getHeightTriangle(i, LEFT, ttriangles.data() + trianglesOffset + 6, count);
- getHeightTriangle(i, BOTTOM, ttriangles.data() + trianglesOffset + 9, count);
- }
- }
-
- // liquid data
- if (haveLiquid)
- {
- map_liquidHeader lheader;
- fseek(mapFile.get(), fheader.liquidMapOffset, SEEK_SET);
- if (fread(&lheader, sizeof(map_liquidHeader), 1, mapFile.get()) != 1)
- TC_LOG_ERROR("maps.mmapgen", "TerrainBuilder::loadMap: Failed to read {} liquid header expected {}, read {}", mapFileName, 1, 0);
-
- std::unique_ptr<float[]> liquid_map;
-
- if (!lheader.flags.HasFlag(map_liquidHeaderFlags::NoType))
- {
- if (fread(liquid_entry, sizeof(liquid_entry), 1, mapFile.get()) != 1)
- TC_LOG_ERROR("maps.mmapgen", "TerrainBuilder::loadMap: Failed to read {} liquid id expected {}, read {}", mapFileName, 1, 0);
- if (fread(liquid_flags, sizeof(liquid_flags), 1, mapFile.get()) != 1)
- TC_LOG_ERROR("maps.mmapgen", "TerrainBuilder::loadMap: Failed to read {} liquid flags expected {}, read {}", mapFileName, 1, 0);
- }
- else
- {
- std::fill_n(&liquid_entry[0][0], 16 * 16, lheader.liquidType);
- std::fill_n(&liquid_flags[0][0], 16 * 16, lheader.liquidFlags);
- }
-
- if (!lheader.flags.HasFlag(map_liquidHeaderFlags::NoHeight))
- {
- uint32 toRead = lheader.width * lheader.height;
- liquid_map = std::make_unique<float[]>(toRead);
- if (fread(liquid_map.get(), sizeof(float), toRead, mapFile.get()) != toRead)
- {
- TC_LOG_ERROR("maps.mmapgen", "TerrainBuilder::loadMap: Failed to read {} liquid header expected {}, read {}", mapFileName, toRead, 0);
- liquid_map = nullptr;
- }
- }
-
- int count = meshData.liquidVerts.size() / 3;
- meshData.liquidVerts.resize(meshData.liquidVerts.size() + V9_SIZE_SQ * 3);
- float* liquidVerts = meshData.liquidVerts.data();
- float xoffset = (float(tileX)-32)*GRID_SIZE;
- float yoffset = (float(tileY)-32)*GRID_SIZE;
-
- int row, col;
-
- // generate coordinates
- if (!lheader.flags.HasFlag(map_liquidHeaderFlags::NoHeight))
- {
- int j = 0;
- for (int i = 0; i < V9_SIZE_SQ; ++i)
- {
- row = i / V9_SIZE;
- col = i % V9_SIZE;
-
- if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height ||
- col < lheader.offsetX || col >= lheader.offsetX + lheader.width)
- {
- // dummy vert using invalid height
- liquidVerts[(count + i) * 3 + 0] = (yoffset + col * GRID_PART_SIZE) * -1;
- liquidVerts[(count + i) * 3 + 1] = INVALID_MAP_LIQ_HEIGHT;
- liquidVerts[(count + i) * 3 + 2] = (xoffset + row * GRID_PART_SIZE) * -1;
- continue;
- }
-
- getLiquidCoord(i, j, xoffset, yoffset, &liquidVerts[(count + i) * 3], liquid_map.get());
- j++;
- }
- }
- else
- {
- for (int i = 0; i < V9_SIZE_SQ; ++i)
- {
- row = i / V9_SIZE;
- col = i % V9_SIZE;
- liquidVerts[(count + i) * 3 + 0] = (yoffset + col * GRID_PART_SIZE) * -1;
- liquidVerts[(count + i) * 3 + 1] = lheader.liquidLevel;
- liquidVerts[(count + i) * 3 + 2] = (xoffset + row * GRID_PART_SIZE) * -1;
- }
- }
-
- int loopStart = 0, loopEnd = 0, loopInc = 0;
- getLoopVars(portion, loopStart, loopEnd, loopInc);
-
- // generate triangles
- for (int i = loopStart; i < loopEnd; i += loopInc)
- {
- std::size_t trianglesOffset = ltriangles.size();
- ltriangles.resize(ltriangles.size() + 6);
- getHeightTriangle(i, TOP, ltriangles.data() + trianglesOffset + 0, count, true);
- getHeightTriangle(i, BOTTOM, ltriangles.data() + trianglesOffset + 3, count, true);
- }
- }
-
- // now that we have gathered the data, we can figure out which parts to keep:
- // liquid above ground, ground above liquid
- int loopStart = 0, loopEnd = 0, loopInc = 0, tTriCount = 4;
- bool useTerrain, useLiquid;
-
- float* lverts = meshData.liquidVerts.data();
- int* ltris = ltriangles.data();
-
- float* tverts = meshData.solidVerts.data();
- int* ttris = ttriangles.data();
-
- if ((ltriangles.size() + ttriangles.size()) == 0)
- return false;
-
- // make a copy of liquid vertices
- // used to pad right-bottom frame due to lost vertex data at extraction
- std::unique_ptr<float[]> lverts_copy;
- if (!meshData.liquidVerts.empty())
- {
- lverts_copy = std::make_unique<float[]>(meshData.liquidVerts.size());
- memcpy(lverts_copy.get(), lverts, sizeof(float)* meshData.liquidVerts.size());
- }
-
- getLoopVars(portion, loopStart, loopEnd, loopInc);
- for (int i = loopStart; i < loopEnd; i+=loopInc)
- {
- for (int j = 0; j < 2; ++j)
- {
- // default is true, will change to false if needed
- useTerrain = true;
- useLiquid = true;
- EnumFlag<map_liquidHeaderTypeFlags> liquidType = map_liquidHeaderTypeFlags::NoWater;
- uint8 navLiquidType = NAV_AREA_EMPTY;
-
- // if there is no liquid, don't use liquid
- if (meshData.liquidVerts.empty() || ltriangles.empty())
- useLiquid = false;
- else
- {
- liquidType = getLiquidType(i, liquid_flags);
- if (liquidType.HasFlag(map_liquidHeaderTypeFlags::DarkWater))
- {
- // players should not be here, so logically neither should creatures
- useTerrain = false;
- useLiquid = false;
- }
- else if (liquidType.HasFlag(map_liquidHeaderTypeFlags::Water | map_liquidHeaderTypeFlags::Ocean))
- navLiquidType = NAV_AREA_WATER;
- else if (liquidType.HasFlag(map_liquidHeaderTypeFlags::Magma | map_liquidHeaderTypeFlags::Slime))
- navLiquidType = NAV_AREA_MAGMA_SLIME;
- else
- useLiquid = false;
- }
-
- // if there is no terrain, don't use terrain
- if (ttriangles.empty())
- useTerrain = false;
-
- // while extracting ADT data we are losing right-bottom vertices
- // this code adds fair approximation of lost data
- if (useLiquid)
- {
- float quadHeight = 0;
- uint32 validCount = 0;
- for(uint32 idx = 0; idx < 3; idx++)
- {
- float h = lverts_copy[ltris[idx]*3 + 1];
- if (h != INVALID_MAP_LIQ_HEIGHT && h < INVALID_MAP_LIQ_HEIGHT_MAX)
- {
- quadHeight += h;
- validCount++;
- }
- }
-
- // update vertex height data
- if (validCount > 0 && validCount < 3)
- {
- quadHeight /= validCount;
- for(uint32 idx = 0; idx < 3; idx++)
- {
- float h = lverts[ltris[idx]*3 + 1];
- if (h == INVALID_MAP_LIQ_HEIGHT || h > INVALID_MAP_LIQ_HEIGHT_MAX)
- lverts[ltris[idx]*3 + 1] = quadHeight;
- }
- }
-
- // no valid vertexes - don't use this poly at all
- if (validCount == 0)
- useLiquid = false;
- }
-
- // if there is a hole here, don't use the terrain
- if (useTerrain && fheader.holesSize != 0)
- useTerrain = !isHole(i, holes);
-
- // we use only one terrain kind per quad - pick higher one
- if (useTerrain && useLiquid)
- {
- float minLLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
- float maxLLevel = INVALID_MAP_LIQ_HEIGHT;
- for(uint32 x = 0; x < 3; x++)
- {
- float h = lverts[ltris[x]*3 + 1];
- if (minLLevel > h)
- minLLevel = h;
-
- if (maxLLevel < h)
- maxLLevel = h;
- }
-
- float maxTLevel = INVALID_MAP_LIQ_HEIGHT;
- float minTLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
- for(uint32 x = 0; x < 6; x++)
- {
- float h = tverts[ttris[x]*3 + 1];
- if (maxTLevel < h)
- maxTLevel = h;
-
- if (minTLevel > h)
- minTLevel = h;
- }
-
- // terrain under the liquid?
- if (minLLevel > maxTLevel)
- useTerrain = false;
-
- //liquid under the terrain?
- if (minTLevel > maxLLevel)
- useLiquid = false;
- }
-
- // store the result
- if (useLiquid)
- {
- meshData.liquidTris.insert(meshData.liquidTris.end(), &ltris[0], &ltris[3]);
- meshData.liquidType.push_back(navLiquidType);
- }
-
- if (useTerrain)
- meshData.solidTris.insert(meshData.solidTris.end(), &ttris[0], &ttris[3 * tTriCount / 2]);
-
- // advance to next set of triangles
- ltris += 3;
- ttris += 3*tTriCount/2;
- }
- }
-
- return !meshData.solidTris.empty() || !meshData.liquidTris.empty();
- }
-
- /**************************************************************************/
- inline void TerrainBuilder::getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v)
- {
- // wow coords: x, y, height
- // coord is mirroed about the horizontal axes
- switch (grid)
- {
- case GRID_V9:
- coord[0] = (yOffset + (int)(index % V9_SIZE) * GRID_PART_SIZE) * -1.f;
- coord[1] = v[index];
- coord[2] = (xOffset + (int)(index / (V9_SIZE)) * GRID_PART_SIZE) * -1.f;
- break;
- case GRID_V8:
- coord[0] = (yOffset + (int)(index % V8_SIZE) * GRID_PART_SIZE + GRID_PART_SIZE / 2.f) * -1.f;
- coord[1] = v[index];
- coord[2] = (xOffset + (int)(index / (V8_SIZE)) * GRID_PART_SIZE + GRID_PART_SIZE / 2.f) * -1.f;
- break;
- }
- }
-
- /**************************************************************************/
- inline void TerrainBuilder::getHeightTriangle(int square, Spot triangle, int* indices, int offset, bool liquid/* = false*/)
- {
- int rowOffset = square/V8_SIZE;
- if (!liquid)
- switch (triangle)
- {
- case TOP:
- indices[0] = V9_SIZE_SQ + square + offset; // 0-----1 .... 128
- indices[1] = square + 1 + rowOffset + offset; // |\ T /|
- indices[2] = square + rowOffset + offset; // | \ / |
- break; // |L 0 R| .. 127
- case LEFT: // | / \ |
- indices[0] = square + V9_SIZE + rowOffset + offset; // |/ B \|
- indices[1] = V9_SIZE_SQ + square + offset; // 129---130 ... 386
- indices[2] = square + rowOffset + offset; // |\ /|
- break; // | \ / |
- case RIGHT: // | 128 | .. 255
- indices[0] = V9_SIZE_SQ + square + offset; // | / \ |
- indices[1] = square + V9_SIZE + 1 + rowOffset + offset; // |/ \|
- indices[2] = square + 1 + rowOffset + offset; // 258---259 ... 515
- break;
- case BOTTOM:
- indices[0] = square + V9_SIZE + rowOffset + offset;
- indices[1] = square + V9_SIZE + 1 + rowOffset + offset;
- indices[2] = V9_SIZE_SQ + square + offset;
- break;
- default: break;
- }
- else
- switch (triangle)
- { // 0-----1 .... 128
- case TOP: // |\ |
- indices[0] = square + V9_SIZE + 1 + rowOffset + offset; // | \ T |
- indices[1] = square + 1 + rowOffset + offset; // | \ |
- indices[2] = square + rowOffset + offset; // | B \ |
- break; // | \|
- case BOTTOM: // 129---130 ... 386
- indices[0] = square + V9_SIZE + rowOffset + offset; // |\ |
- indices[1] = square + V9_SIZE + 1 + rowOffset + offset; // | \ |
- indices[2] = square + rowOffset + offset; // | \ |
- break; // | \ |
- default: break; // | \|
- } // 258---259 ... 515
-
- }
-
- /**************************************************************************/
- inline void TerrainBuilder::getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v)
- {
- // wow coords: x, y, height
- // coord is mirroed about the horizontal axes
- coord[0] = (yOffset + (int)(index % V9_SIZE) * GRID_PART_SIZE) * -1.f;
- coord[1] = v[index2];
- coord[2] = (xOffset + (int)(index / (V9_SIZE)) * GRID_PART_SIZE) * -1.f;
- }
-
- /**************************************************************************/
- inline bool TerrainBuilder::isHole(int square, uint8 const (&holes)[16][16][8])
- {
- int row = square / 128;
- int col = square % 128;
- int cellRow = row / 8; // 8 squares per cell
- int cellCol = col / 8;
- int holeRow = row % 8;
- int holeCol = col % 8;
-
- return (holes[cellRow][cellCol][holeRow] & (1 << holeCol)) != 0;
- }
-
- /**************************************************************************/
- inline map_liquidHeaderTypeFlags TerrainBuilder::getLiquidType(int square, map_liquidHeaderTypeFlags const (&liquid_type)[16][16])
- {
- int row = square / 128;
- int col = square % 128;
- int cellRow = row / 8; // 8 squares per cell
- int cellCol = col / 8;
-
- return liquid_type[cellRow][cellCol];
- }
-
- /**************************************************************************/
- bool TerrainBuilder::loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, VMAP::VMapManager* vmapManager)
- {
- VMAP::LoadResult result = vmapManager->loadMap((m_inputDirectory / "vmaps").string(), mapID, tileX, tileY);
- if (result != VMAP::LoadResult::Success)
- return false;
-
- auto vmapTile = Trinity::make_unique_ptr_with_deleter(vmapManager, [=](VMAP::VMapManager* mgr)
- {
- mgr->unloadMap(mapID, tileX, tileY);
- });
-
- std::span<VMAP::ModelInstance const> models = vmapManager->getModelsOnMap(mapID);
- if (models.empty())
- return false;
-
- bool retval = false;
-
- for (VMAP::ModelInstance const& instance : models)
- {
- // model instances exist in tree even though there are instances of that model in this tile
- VMAP::WorldModel const* worldModel = instance.getWorldModel();
- if (!worldModel)
- continue;
-
- // now we have a model to add to the meshdata
- retval = true;
-
- 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();
-
- // 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())
- {
- 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)
- {
- 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)
- {
- for (uint32 y = 0; y < tilesY; ++y)
- {
- 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;
- }
- }
- }
-
- meshData.liquidType.resize(meshData.liquidType.size() + liquidSquares * 2, type);
- }
- }
- }
-
- /**************************************************************************/
- void TerrainBuilder::transformVertices(std::vector<G3D::Vector3> const& source, std::vector<float>& dest, float scale, G3D::Matrix3 const& rotation, G3D::Vector3 const& position)
- {
- std::size_t offset = dest.size();
- dest.resize(dest.size() + source.size() * 3);
- float* d = dest.data();
- for (std::size_t i = 0; i < source.size(); ++i)
- {
- // apply tranform, then mirror along the horizontal axes
- G3D::Vector3 v(source[i] * rotation * scale + position);
- v.x *= -1.f;
- v.y *= -1.f;
- d[offset + i * 3 + 0] = v.y;
- d[offset + i * 3 + 1] = v.z;
- d[offset + i * 3 + 2] = v.x;
- }
- }
-
- /**************************************************************************/
- void TerrainBuilder::copyIndices(std::vector<VMAP::MeshTriangle> const& source, std::vector<int>& dest, int offset, bool flip)
- {
- std::size_t destOffset = dest.size();
- dest.resize(dest.size() + source.size() * 3);
- int* d = dest.data();
- if (flip)
- {
- for (VMAP::MeshTriangle const& triangle : source)
- {
- d[destOffset++] = triangle.idx2 + offset;
- d[destOffset++] = triangle.idx1 + offset;
- d[destOffset++] = triangle.idx0 + offset;
- }
- }
- else
- {
- static_assert(sizeof(VMAP::MeshTriangle) == 3 * sizeof(uint32));
- std::ranges::transform(reinterpret_cast<uint32 const*>(source.data()), reinterpret_cast<uint32 const*>(source.data() + source.size()),
- dest.data() + destOffset, [&](int src) { return src + offset; });
- }
- }
-
- /**************************************************************************/
- void TerrainBuilder::copyIndices(std::vector<int> const& source, std::vector<int>& dest, int offset)
- {
- std::size_t destOffset = dest.size();
- dest.resize(destOffset + source.size());
- std::ranges::transform(source, dest.data() + destOffset, [&](int src) { return src + offset; });
- }
-
- /**************************************************************************/
- void TerrainBuilder::cleanVertices(std::vector<float>& verts, std::vector<int>& tris)
- {
- std::unordered_map<int, int> vertMap;
- vertMap.reserve(tris.size());
-
- int* t = tris.data();
- float* v = verts.data();
-
- std::vector<float> cleanVerts;
- cleanVerts.reserve(verts.size());
- int count = 0;
- // collect all the vertex indices from triangle
- for (std::size_t i = 0; i < tris.size(); ++i)
- {
- auto [vertItr, isNew] = vertMap.try_emplace(t[i], count);
- if (!isNew)
- continue;
-
- std::ptrdiff_t index = t[i];
-
- cleanVerts.insert(cleanVerts.end(), &v[index * 3], &v[index * 3 + 3]);
- count++;
- }
-
- verts = std::move(cleanVerts);
-
- // update triangles to use new indices
- for (std::size_t i = 0; i < tris.size(); ++i)
- {
- auto it = vertMap.find(t[i]);
- if (it == vertMap.end())
- continue;
-
- t[i] = it->second;
- }
- }
-
- /**************************************************************************/
- void TerrainBuilder::loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, std::vector<OffMeshData> const& offMeshConnections)
- {
- for (OffMeshData const& offMeshConnection : offMeshConnections)
- {
- if (mapID != offMeshConnection.MapId || tileX != offMeshConnection.TileX || tileY != offMeshConnection.TileY)
- continue;
-
- meshData.offMeshConnections.push_back(offMeshConnection.From[1]);
- meshData.offMeshConnections.push_back(offMeshConnection.From[2]);
- meshData.offMeshConnections.push_back(offMeshConnection.From[0]);
-
- meshData.offMeshConnections.push_back(offMeshConnection.To[1]);
- meshData.offMeshConnections.push_back(offMeshConnection.To[2]);
- meshData.offMeshConnections.push_back(offMeshConnection.To[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);
- meshData.offMeshConnectionsFlags.push_back(offMeshConnection.Flags);
- }
- }
-}
diff --git a/src/tools/mmaps_generator/TerrainBuilder.h b/src/tools/mmaps_generator/TerrainBuilder.h
deleted file mode 100644
index 65ebe4508a9..00000000000
--- a/src/tools/mmaps_generator/TerrainBuilder.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#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
-{
-class VMapManager;
-}
-
-enum class map_liquidHeaderTypeFlags : uint8;
-
-namespace MMAP
-{
- enum Spot
- {
- TOP = 1,
- RIGHT = 2,
- LEFT = 3,
- BOTTOM = 4,
- ENTIRE = 5
- };
-
- enum Grid
- {
- GRID_V8,
- GRID_V9
- };
-
- static const int V9_SIZE = 129;
- static const int V9_SIZE_SQ = V9_SIZE*V9_SIZE;
- static const int V8_SIZE = 128;
- static const int V8_SIZE_SQ = V8_SIZE*V8_SIZE;
- static const float GRID_SIZE = 533.3333f;
- static const float GRID_PART_SIZE = GRID_SIZE/V8_SIZE;
-
- // see contrib/extractor/system.cpp, CONF_use_minHeight
- static const float INVALID_MAP_LIQ_HEIGHT = -2000.f;
- static const float INVALID_MAP_LIQ_HEIGHT_MAX = 5000.0f;
-
- // see following files:
- // contrib/extractor/system.cpp
- // src/game/Map.cpp
-
- struct MeshData
- {
- std::vector<float> solidVerts;
- std::vector<int> solidTris;
-
- std::vector<float> liquidVerts;
- std::vector<int> liquidTris;
- std::vector<uint8> liquidType;
-
- // offmesh connection data
- std::vector<float> offMeshConnections; // [p0y,p0z,p0x,p1y,p1z,p1x] - per connection
- std::vector<float> offMeshConnectionRads;
- std::vector<unsigned char> offMeshConnectionDirs;
- std::vector<unsigned char> offMeshConnectionsAreas;
- std::vector<unsigned short> offMeshConnectionsFlags;
- };
-
- class TerrainBuilder
- {
- public:
- 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; }
-
- // vert and triangle methods
- static void transformVertices(std::vector<G3D::Vector3> const& source, std::vector<float>& dest,
- float scale, G3D::Matrix3 const& rotation, G3D::Vector3 const& position);
- static void copyIndices(std::vector<VMAP::MeshTriangle> const& source, std::vector<int>& dest, int offset, bool flip);
- static void copyIndices(std::vector<int> const& source, std::vector<int>& dest, int offset);
- static void cleanVertices(std::vector<float>& verts, std::vector<int>& tris);
- private:
- /// Loads a portion of a map's terrain
- bool loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, VMAP::VMapManager* vmapManager, Spot portion);
-
- /// 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;
-
- /// Get the vector coordinate for a specific position
- static void getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v);
-
- /// Get the triangle's vector indices for a specific position
- static void getHeightTriangle(int square, Spot triangle, int* indices, int offset, bool liquid = false);
-
- /// Determines if the specific position's triangles should be rendered
- static bool isHole(int square, uint8 const (&holes)[16][16][8]);
-
- /// Get the liquid vector coordinate for a specific position
- static void getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v);
-
- /// Get the liquid type for a specific position
- static map_liquidHeaderTypeFlags getLiquidType(int square, map_liquidHeaderTypeFlags const (&liquid_type)[16][16]);
- };
-}
-
-#endif
diff --git a/src/tools/mmaps_generator/TileBuilder.cpp b/src/tools/mmaps_generator/TileBuilder.cpp
deleted file mode 100644
index 5654a14183c..00000000000
--- a/src/tools/mmaps_generator/TileBuilder.cpp
+++ /dev/null
@@ -1,569 +0,0 @@
-/*
- * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "TileBuilder.h"
-#include "IntermediateValues.h"
-#include "Log.h"
-#include "MMapDefines.h"
-#include "Memory.h"
-#include "PathCommon.h"
-#include "StringFormat.h"
-#include "VMapManager.h"
-#include <DetourNavMeshBuilder.h>
-
-namespace
-{
- struct Tile
- {
- Tile() : chf(nullptr), solid(nullptr), cset(nullptr), pmesh(nullptr), dmesh(nullptr) {}
- ~Tile()
- {
- rcFreeCompactHeightfield(chf);
- rcFreeContourSet(cset);
- rcFreeHeightField(solid);
- rcFreePolyMesh(pmesh);
- rcFreePolyMeshDetail(dmesh);
- }
- rcCompactHeightfield* chf;
- rcHeightfield* solid;
- rcContourSet* cset;
- rcPolyMesh* pmesh;
- rcPolyMeshDetail* dmesh;
- };
-}
-
-namespace MMAP
-{
- struct TileConfig
- {
- TileConfig(bool bigBaseUnit)
- {
- // these are WORLD UNIT based metrics
- // this are basic unit dimentions
- // value have to divide GRID_SIZE(533.3333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc )
- BASE_UNIT_DIM = bigBaseUnit ? 0.5333333f : 0.2666666f;
-
- // All are in UNIT metrics!
- VERTEX_PER_MAP = int(GRID_SIZE / BASE_UNIT_DIM + 0.5f);
- VERTEX_PER_TILE = bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP
- TILES_PER_MAP = VERTEX_PER_MAP / VERTEX_PER_TILE;
- }
-
- float BASE_UNIT_DIM;
- int VERTEX_PER_MAP;
- int VERTEX_PER_TILE;
- int TILES_PER_MAP;
- };
-
- 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(inputDirectory, skipLiquid),
- m_rcContext(false),
- m_offMeshConnections(offMeshConnections)
- {
- }
-
- TileBuilder::~TileBuilder() = default;
-
- /**************************************************************************/
- void TileBuilder::buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh)
- {
- if (shouldSkipTile(mapID, tileX, tileY))
- {
- OnTileDone();
- return;
- }
-
- TC_LOG_INFO("maps.mmapgen", "{} [Map {:04}] Building tile [{:02},{:02}]", GetProgressText(), mapID, tileX, tileY);
-
- MeshData meshData;
-
- std::unique_ptr<VMAP::VMapManager> vmapManager = VMapFactory::CreateVMapManager(mapID);
-
- // get heightmap data
- m_terrainBuilder.loadMap(mapID, tileX, tileY, meshData, vmapManager.get());
-
- // get model data
- m_terrainBuilder.loadVMap(mapID, tileX, tileY, meshData, vmapManager.get());
-
- // if there is no data, give up now
- if (meshData.solidVerts.empty() && meshData.liquidVerts.empty())
- {
- OnTileDone();
- return;
- }
-
- // remove unused vertices
- TerrainBuilder::cleanVertices(meshData.solidVerts, meshData.solidTris);
- TerrainBuilder::cleanVertices(meshData.liquidVerts, meshData.liquidTris);
-
- if (meshData.liquidVerts.empty() && meshData.solidVerts.empty())
- {
- OnTileDone();
- return;
- }
-
- // gather all mesh data for final data check, and bounds calculation
- std::vector<float> allVerts(meshData.liquidVerts.size() + meshData.solidVerts.size());
- 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];
- getTileBounds(tileX, tileY, allVerts.data(), allVerts.size() / 3, bmin, bmax);
-
- if (m_offMeshConnections)
- m_terrainBuilder.loadOffMeshConnections(mapID, tileX, tileY, meshData, *m_offMeshConnections);
-
- // build navmesh tile
- TileResult tileResult = buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh->getParams());
- if (tileResult.data)
- saveMoveMapTileToFile(mapID, tileX, tileY, navMesh, tileResult);
-
- OnTileDone();
- }
-
- /**************************************************************************/
- TileBuilder::TileResult TileBuilder::buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY,
- MeshData& meshData, float (&bmin)[3], float (&bmax)[3],
- dtNavMeshParams const* navMeshParams, std::string_view fileNameSuffix)
- {
- // console output
- std::string tileString = Trinity::StringFormat("[Map {:04}] [{:02},{:02}]:", mapID, tileX, tileY);
- TC_LOG_INFO("maps.mmapgen", "{} Building movemap tile...", tileString);
-
- TileResult tileResult;
-
- IntermediateValues iv;
-
- float* tVerts = meshData.solidVerts.data();
- int tVertCount = meshData.solidVerts.size() / 3;
- int* tTris = meshData.solidTris.data();
- int tTriCount = meshData.solidTris.size() / 3;
-
- float* lVerts = meshData.liquidVerts.data();
- int lVertCount = meshData.liquidVerts.size() / 3;
- int* lTris = meshData.liquidTris.data();
- int lTriCount = meshData.liquidTris.size() / 3;
- uint8* lTriFlags = meshData.liquidType.data();
-
- const TileConfig tileConfig = TileConfig(m_bigBaseUnit);
- int TILES_PER_MAP = tileConfig.TILES_PER_MAP;
- float BASE_UNIT_DIM = tileConfig.BASE_UNIT_DIM;
- rcConfig config = GetMapSpecificConfig(mapID, bmin, bmax, tileConfig);
-
- // this sets the dimensions of the heightfield - should maybe happen before border padding
- rcCalcGridSize(config.bmin, config.bmax, config.cs, &config.width, &config.height);
-
- // allocate subregions : tiles
- std::unique_ptr<Tile[]> tiles = std::make_unique<Tile[]>(TILES_PER_MAP * TILES_PER_MAP);
-
- // Initialize per tile config.
- rcConfig tileCfg = config;
- tileCfg.width = config.tileSize + config.borderSize * 2;
- tileCfg.height = config.tileSize + config.borderSize * 2;
-
- // merge per tile poly and detail meshes
- std::unique_ptr<rcPolyMesh*[]> pmmerge = std::make_unique<rcPolyMesh*[]>(TILES_PER_MAP * TILES_PER_MAP);
- std::unique_ptr<rcPolyMeshDetail*[]> dmmerge = std::make_unique<rcPolyMeshDetail*[]>(TILES_PER_MAP * TILES_PER_MAP);
- int nmerge = 0;
- // build all tiles
- for (int y = 0; y < TILES_PER_MAP; ++y)
- {
- for (int x = 0; x < TILES_PER_MAP; ++x)
- {
- Tile& tile = tiles[x + y * TILES_PER_MAP];
-
- // Calculate the per tile bounding box.
- tileCfg.bmin[0] = config.bmin[0] + x * float(config.tileSize * config.cs);
- tileCfg.bmin[2] = config.bmin[2] + y * float(config.tileSize * config.cs);
- tileCfg.bmax[0] = config.bmin[0] + (x + 1) * float(config.tileSize * config.cs);
- tileCfg.bmax[2] = config.bmin[2] + (y + 1) * float(config.tileSize * config.cs);
-
- tileCfg.bmin[0] -= tileCfg.borderSize * tileCfg.cs;
- tileCfg.bmin[2] -= tileCfg.borderSize * tileCfg.cs;
- tileCfg.bmax[0] += tileCfg.borderSize * tileCfg.cs;
- tileCfg.bmax[2] += tileCfg.borderSize * tileCfg.cs;
-
- // build heightfield
- tile.solid = rcAllocHeightfield();
- if (!tile.solid || !rcCreateHeightfield(&m_rcContext, *tile.solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch))
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Failed building heightfield!", tileString);
- continue;
- }
-
- // mark all walkable tiles, both liquids and solids
-
- /* we want to have triangles with slope less than walkableSlopeAngleNotSteep (<= 55) to have NAV_AREA_GROUND
- * and with slope between walkableSlopeAngleNotSteep and walkableSlopeAngle (55 < .. <= 70) to have NAV_AREA_GROUND_STEEP.
- * we achieve this using recast API: memset everything to NAV_AREA_GROUND_STEEP, call rcClearUnwalkableTriangles with 70 so
- * any area above that will get RC_NULL_AREA (unwalkable), then call rcMarkWalkableTriangles with 55 to set NAV_AREA_GROUND
- * on anything below 55 . Players and idle Creatures can use NAV_AREA_GROUND, while Creatures in combat can use NAV_AREA_GROUND_STEEP.
- */
- std::unique_ptr<unsigned char[]> triFlags = std::make_unique<unsigned char[]>(tTriCount);
- memset(triFlags.get(), NAV_AREA_GROUND_STEEP, tTriCount * sizeof(unsigned char));
- rcClearUnwalkableTriangles(&m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags.get());
- rcMarkWalkableTriangles(&m_rcContext, tileCfg.walkableSlopeAngleNotSteep, tVerts, tVertCount, tTris, tTriCount, triFlags.get(), NAV_AREA_GROUND);
- rcRasterizeTriangles(&m_rcContext, tVerts, tVertCount, tTris, triFlags.get(), tTriCount, *tile.solid, config.walkableClimb);
-
- rcFilterLowHangingWalkableObstacles(&m_rcContext, config.walkableClimb, *tile.solid);
- rcFilterLedgeSpans(&m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid);
- rcFilterWalkableLowHeightSpans(&m_rcContext, tileCfg.walkableHeight, *tile.solid);
-
- // add liquid triangles
- rcRasterizeTriangles(&m_rcContext, lVerts, lVertCount, lTris, lTriFlags, lTriCount, *tile.solid, config.walkableClimb);
-
- // compact heightfield spans
- tile.chf = rcAllocCompactHeightfield();
- if (!tile.chf || !rcBuildCompactHeightfield(&m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid, *tile.chf))
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Failed compacting heightfield!", tileString);
- continue;
- }
-
- // build polymesh intermediates
- if (!rcErodeWalkableArea(&m_rcContext, config.walkableRadius, *tile.chf))
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Failed eroding area!", tileString);
- continue;
- }
-
- if (!rcMedianFilterWalkableArea(&m_rcContext, *tile.chf))
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Failed filtering area!", tileString);
- continue;
- }
-
- if (!rcBuildDistanceField(&m_rcContext, *tile.chf))
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Failed building distance field!", tileString);
- continue;
- }
-
- if (!rcBuildRegions(&m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea))
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Failed building regions!", tileString);
- continue;
- }
-
- tile.cset = rcAllocContourSet();
- if (!tile.cset || !rcBuildContours(&m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset))
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Failed building contours!", tileString);
- continue;
- }
-
- // build polymesh
- tile.pmesh = rcAllocPolyMesh();
- if (!tile.pmesh || !rcBuildPolyMesh(&m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh))
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Failed building polymesh!", tileString);
- continue;
- }
-
- tile.dmesh = rcAllocPolyMeshDetail();
- if (!tile.dmesh || !rcBuildPolyMeshDetail(&m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *tile.dmesh))
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Failed building polymesh detail!", tileString);
- continue;
- }
-
- // free those up
- // we may want to keep them in the future for debug
- // but right now, we don't have the code to merge them
- rcFreeHeightField(tile.solid);
- tile.solid = nullptr;
- rcFreeCompactHeightfield(tile.chf);
- tile.chf = nullptr;
- rcFreeContourSet(tile.cset);
- tile.cset = nullptr;
-
- pmmerge[nmerge] = tile.pmesh;
- dmmerge[nmerge] = tile.dmesh;
- nmerge++;
- }
- }
-
- iv.polyMesh = rcAllocPolyMesh();
- if (!iv.polyMesh)
- {
- TC_LOG_ERROR("maps.mmapgen", "{} alloc iv.polyMesh FAILED!", tileString);
- return tileResult;
- }
- rcMergePolyMeshes(&m_rcContext, pmmerge.get(), nmerge, *iv.polyMesh);
-
- iv.polyMeshDetail = rcAllocPolyMeshDetail();
- if (!iv.polyMeshDetail)
- {
- TC_LOG_ERROR("maps.mmapgen", "{} alloc m_dmesh FAILED!", tileString);
- return tileResult;
- }
- rcMergePolyMeshDetails(&m_rcContext, dmmerge.get(), nmerge, *iv.polyMeshDetail);
-
- // free things up
- pmmerge = nullptr;
- dmmerge = nullptr;
- tiles = nullptr;
-
- // set polygons as walkable
- // TODO: special flags for DYNAMIC polygons, ie surfaces that can be turned on and off
- for (int i = 0; i < iv.polyMesh->npolys; ++i)
- {
- if (uint8 area = iv.polyMesh->areas[i] & NAV_AREA_ALL_MASK)
- {
- if (area >= NAV_AREA_MIN_VALUE)
- iv.polyMesh->flags[i] = 1 << (NAV_AREA_MAX_VALUE - area);
- else
- iv.polyMesh->flags[i] = NAV_GROUND; // TODO: these will be dynamic in future
- }
- }
-
- // setup mesh parameters
- dtNavMeshCreateParams params = {};
- params.verts = iv.polyMesh->verts;
- params.vertCount = iv.polyMesh->nverts;
- params.polys = iv.polyMesh->polys;
- params.polyAreas = iv.polyMesh->areas;
- params.polyFlags = iv.polyMesh->flags;
- params.polyCount = iv.polyMesh->npolys;
- params.nvp = iv.polyMesh->nvp;
- params.detailMeshes = iv.polyMeshDetail->meshes;
- params.detailVerts = iv.polyMeshDetail->verts;
- params.detailVertsCount = iv.polyMeshDetail->nverts;
- params.detailTris = iv.polyMeshDetail->tris;
- params.detailTriCount = iv.polyMeshDetail->ntris;
-
- params.offMeshConVerts = meshData.offMeshConnections.data();
- params.offMeshConCount = meshData.offMeshConnections.size() / 6;
- params.offMeshConRad = meshData.offMeshConnectionRads.data();
- params.offMeshConDir = meshData.offMeshConnectionDirs.data();
- params.offMeshConAreas = meshData.offMeshConnectionsAreas.data();
- params.offMeshConFlags = meshData.offMeshConnectionsFlags.data();
-
- params.walkableHeight = BASE_UNIT_DIM * config.walkableHeight; // agent height
- params.walkableRadius = BASE_UNIT_DIM * config.walkableRadius; // agent radius
- params.walkableClimb = BASE_UNIT_DIM * config.walkableClimb; // keep less that walkableHeight (aka agent height)!
- params.tileX = (((bmin[0] + bmax[0]) / 2) - navMeshParams->orig[0]) / GRID_SIZE;
- params.tileY = (((bmin[2] + bmax[2]) / 2) - navMeshParams->orig[2]) / GRID_SIZE;
- rcVcopy(params.bmin, bmin);
- rcVcopy(params.bmax, bmax);
- params.cs = config.cs;
- params.ch = config.ch;
- params.tileLayer = 0;
- params.buildBvTree = true;
-
- // 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),
- 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)
- {
- unsigned short* v = &intermediate->polyMesh->verts[i * 3];
- v[0] += borderSize;
- v[2] += borderSize;
- }
-
- intermediate->generateObjFile(*outputDir, fileNameSuffix, mapID, tileX, tileY, meshData);
- intermediate->writeIV(*outputDir, fileNameSuffix, mapID, tileX, tileY);
- });
-
- // these values are checked within dtCreateNavMeshData - handle them here
- // so we have a clear error message
- if (params.nvp > DT_VERTS_PER_POLYGON)
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Invalid verts-per-polygon value!", tileString);
- return tileResult;
- }
-
- if (params.vertCount >= 0xffff)
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Too many vertices!", tileString);
- return tileResult;
- }
-
- if (!params.vertCount || !params.verts)
- {
- // occurs mostly when adjacent tiles have models
- // loaded but those models don't span into this tile
-
- // message is an annoyance
- //TC_LOG_ERROR("maps.mmapgen", "{} No vertices to build tile!", tileString);
- return tileResult;
- }
-
- if (!params.polyCount || !params.polys)
- {
- // we have flat tiles with no actual geometry - don't build those, its useless
- // keep in mind that we do output those into debug info
- TC_LOG_ERROR("maps.mmapgen", "{} No polygons to build on tile!", tileString);
- return tileResult;
- }
-
- if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
- {
- TC_LOG_ERROR("maps.mmapgen", "{} No detail mesh to build tile!", tileString);
- return tileResult;
- }
-
- TC_LOG_DEBUG("maps.mmapgen", "{} Building navmesh tile...", tileString);
- if (!dtCreateNavMeshData(&params, &navData, &tileResult.size))
- {
- TC_LOG_ERROR("maps.mmapgen", "{} Failed building navmesh tile!", tileString);
- return tileResult;
- }
-
- tileResult.data.reset(navData);
- return 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)
- {
- navMesh->removeTile(*ref, nullptr, nullptr);
- });
-
- if (navMesh)
- {
- TC_LOG_DEBUG("maps.mmapgen", "[Map {:04}] [{:02},{:02}]: Adding tile to navmesh...", mapID, tileX, tileY);
- // DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
- // is removed via removeTile()
- dtStatus dtResult = navMesh->addTile(tileResult.data.get(), tileResult.size, 0, 0, &tileRef);
- if (!tileRef || !dtStatusSucceed(dtResult))
- {
- TC_LOG_ERROR("maps.mmapgen", "[Map {:04}] [{:02},{:02}]: Failed adding tile to navmesh!", mapID, tileX, tileY);
- return;
- }
-
- navMeshTile.reset(&tileRef);
- }
-
- // file output
- 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)
- {
- TC_LOG_ERROR("maps.mmapgen", "[Map {:04}] [{:02},{:02}]: {}: Failed to open {} for writing!", mapID, tileX, tileY, strerror(errno), fileName);
- return;
- }
-
- TC_LOG_DEBUG("maps.mmapgen", "[Map {:04}] [{:02},{:02}]: Writing to file...", mapID, tileX, tileY);
-
- // write header
- MmapTileHeader header;
- header.usesLiquids = m_terrainBuilder.usesLiquids();
- header.size = uint32(tileResult.size);
- fwrite(&header, sizeof(MmapTileHeader), 1, file.get());
-
- // write data
- fwrite(tileResult.data.get(), sizeof(unsigned char), tileResult.size, file.get());
- }
-
- /**************************************************************************/
- 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, int(vertCount), bmin, bmax);
- else
- {
- bmin[1] = FLT_MIN;
- bmax[1] = FLT_MAX;
- }
-
- // this is for width and depth
- bmax[0] = (32 - int(tileY)) * GRID_SIZE;
- bmax[2] = (32 - int(tileX)) * GRID_SIZE;
- bmin[0] = bmax[0] - GRID_SIZE;
- bmin[2] = bmax[2] - GRID_SIZE;
- }
-
- /**************************************************************************/
- bool TileBuilder::shouldSkipTile(uint32 /*mapID*/, uint32 /*tileX*/, uint32 /*tileY*/) const
- {
- if (m_debugOutput)
- return false;
-
- return true;
- }
-
- rcConfig TileBuilder::GetMapSpecificConfig(uint32 mapID, float const (&bmin)[3], float const (&bmax)[3], TileConfig const& tileConfig) const
- {
- rcConfig config { };
-
- rcVcopy(config.bmin, bmin);
- rcVcopy(config.bmax, bmax);
-
- config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
- config.cs = tileConfig.BASE_UNIT_DIM;
- config.ch = tileConfig.BASE_UNIT_DIM;
- // Keeping these 2 slope angles the same reduces a lot the number of polys.
- // 55 should be the minimum, maybe 70 is ok (keep in mind blink uses mmaps), 85 is too much for players
- config.walkableSlopeAngle = m_maxWalkableAngle.value_or(55.0f);
- config.walkableSlopeAngleNotSteep = m_maxWalkableAngleNotSteep.value_or(55.0f);
- config.tileSize = tileConfig.VERTEX_PER_TILE;
- config.walkableRadius = m_bigBaseUnit ? 1 : 2;
- config.borderSize = config.walkableRadius + 3;
- config.maxEdgeLen = tileConfig.VERTEX_PER_TILE + 1; // anything bigger than tileSize
- config.walkableHeight = m_bigBaseUnit ? 3 : 6;
- // a value >= 3|6 allows npcs to walk over some fences
- // a value >= 4|8 allows npcs to walk over all fences
- config.walkableClimb = m_bigBaseUnit ? 3 : 6;
- config.minRegionArea = rcSqr(60);
- config.mergeRegionArea = rcSqr(50);
- config.maxSimplificationError = 1.8f; // eliminates most jagged edges (tiny polygons)
- config.detailSampleDist = config.cs * 16;
- config.detailSampleMaxError = config.ch * 1;
-
- switch (mapID)
- {
- // Blade's Edge Arena
- case 562:
- // This allows to walk on the ropes to the pillars
- config.walkableRadius = 0;
- break;
- // Blackfathom Deeps
- case 48:
- // Reduce the chance to have underground levels
- config.ch *= 2;
- break;
- default:
- break;
- }
-
- return config;
- }
-
- std::string TileBuilder::GetProgressText() const
- {
- return "";
- }
-}
diff --git a/src/tools/mmaps_generator/TileBuilder.h b/src/tools/mmaps_generator/TileBuilder.h
deleted file mode 100644
index f7c4806c515..00000000000
--- a/src/tools/mmaps_generator/TileBuilder.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef TRINITYCORE_TILE_BUILDER_H
-#define TRINITYCORE_TILE_BUILDER_H
-
-#include "Common.h"
-#include "Memory.h"
-#include "StringFormat.h"
-#include "TerrainBuilder.h"
-#include <DetourNavMesh.h>
-#include <Recast.h>
-
-namespace MMAP
-{
-struct TileConfig;
-
-using detour_unique_ptr = std::unique_ptr<unsigned char, decltype(Trinity::unique_ptr_deleter<unsigned char*, &::dtFree>())>;
-
-class TileBuilder
-{
-public:
- 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);
-
- TileBuilder(TileBuilder const&) = delete;
- TileBuilder(TileBuilder&&) = delete;
-
- TileBuilder& operator=(TileBuilder const&) = delete;
- TileBuilder& operator=(TileBuilder&&) = delete;
-
- virtual ~TileBuilder();
-
- void buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh);
-
- // move map building
- struct TileResult
- {
- detour_unique_ptr data;
- int size = 0;
- };
- TileResult buildMoveMapTile(uint32 mapID,
- uint32 tileX,
- uint32 tileY,
- MeshData& meshData,
- float (&bmin)[3],
- float (&bmax)[3],
- dtNavMeshParams const* navMeshParams,
- std::string_view fileNameSuffix = ""sv);
-
- 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 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;
-
- virtual std::string GetProgressText() const;
-
- virtual void OnTileDone() { }
-
-protected:
- boost::filesystem::path m_outputDirectory;
- Optional<float> m_maxWalkableAngle;
- Optional<float> m_maxWalkableAngleNotSteep;
- bool m_bigBaseUnit;
- bool m_debugOutput;
-
- TerrainBuilder m_terrainBuilder;
- // build performance - not really used for now
- rcContext m_rcContext;
- std::vector<OffMeshData> const* m_offMeshConnections;
-};
-}
-
-#endif // TRINITYCORE_TILE_BUILDER_H