diff options
Diffstat (limited to 'src/tools/mmaps_generator')
| -rw-r--r-- | src/tools/mmaps_generator/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/IntermediateValues.cpp | 252 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/IntermediateValues.h | 78 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/PathCommon.h | 5 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/PathGenerator.cpp | 2 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/TerrainBuilder.cpp | 810 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/TerrainBuilder.h | 130 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/TileBuilder.cpp | 569 | ||||
| -rw-r--r-- | src/tools/mmaps_generator/TileBuilder.h | 100 |
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(), <ris[0], <ris[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(¶ms, &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 |
