diff options
-rw-r--r-- | src/tools/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/tools/mmap_extractor/CMakeLists.txt | 68 | ||||
-rw-r--r-- | src/tools/mmap_extractor/Info/readme.txt | 66 | ||||
-rw-r--r-- | src/tools/mmap_extractor/IntermediateValues.cpp | 277 | ||||
-rw-r--r-- | src/tools/mmap_extractor/IntermediateValues.h | 53 | ||||
-rw-r--r-- | src/tools/mmap_extractor/MapBuilder.cpp | 894 | ||||
-rw-r--r-- | src/tools/mmap_extractor/MapBuilder.h | 129 | ||||
-rw-r--r-- | src/tools/mmap_extractor/PathCommon.h | 128 | ||||
-rw-r--r-- | src/tools/mmap_extractor/PathGenerator.cpp | 273 | ||||
-rw-r--r-- | src/tools/mmap_extractor/TerrainBuilder.cpp | 854 | ||||
-rw-r--r-- | src/tools/mmap_extractor/TerrainBuilder.h | 138 | ||||
-rw-r--r-- | src/tools/mmap_extractor/VMapExtensions.cpp | 72 |
12 files changed, 2953 insertions, 0 deletions
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index a956655ffaa..f4c5f695b26 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory(map_extractor) add_subdirectory(vmap4_assembler) add_subdirectory(vmap4_extractor) +add_subdirectory(mmaps_extractor) diff --git a/src/tools/mmap_extractor/CMakeLists.txt b/src/tools/mmap_extractor/CMakeLists.txt new file mode 100644 index 00000000000..c7c1a954371 --- /dev/null +++ b/src/tools/mmap_extractor/CMakeLists.txt @@ -0,0 +1,68 @@ +# Copyright (C) 2008-2011 Trinity <http://www.trinitycore.org/> +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +file(GLOB_RECURSE sources *.cpp *.h) + +# definitions +add_definitions(-DNO_CORE_FUNCS) +add_definitions(-DDEBUG) +add_definitions(-DNO_vsnprintf) + +include_directories( + ${CMAKE_BINARY_DIR} + ${ACE_INCLUDE_DIR} + ${MYSQL_INCLUDE_DIR} + ${CMAKE_SOURCE_DIR}/dep/libmpq + ${CMAKE_SOURCE_DIR}/dep/zlib + ${CMAKE_SOURCE_DIR}/dep/bzip2 + ${CMAKE_SOURCE_DIR}/dep/acelite + ${CMAKE_SOURCE_DIR}/dep/g3dlite/include + ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast + ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour + ${CMAKE_SOURCE_DIR}/src/server/shared + ${CMAKE_SOURCE_DIR}/src/server/shared/Database + ${CMAKE_SOURCE_DIR}/src/server/shared/Database/Implementation + ${CMAKE_SOURCE_DIR}/src/server/shared/Threading + ${CMAKE_SOURCE_DIR}/src/server/shared/Logging + ${CMAKE_SOURCE_DIR}/src/server/shared/Utilities + ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic + ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic/LinkedReference + ${CMAKE_SOURCE_DIR}/src/server/game/Maps + ${CMAKE_SOURCE_DIR}/src/server/game/DataStores + ${CMAKE_SOURCE_DIR}/src/server/game/Movement/Waypoints + ${CMAKE_SOURCE_DIR}/src/server/game/Grids + ${CMAKE_SOURCE_DIR}/src/server/game/Grids/Cells + ${CMAKE_SOURCE_DIR}/src/server/game/Miscellaneous + ${CMAKE_SOURCE_DIR}/src/server/game/Conditions + ${CMAKE_SOURCE_DIR}/src/server/collision + ${CMAKE_SOURCE_DIR}/src/server/collision/Management + ${CMAKE_SOURCE_DIR}/src/server/collision/Maps + ${CMAKE_SOURCE_DIR}/src/server/collision/Models +) + +add_executable(movements_extractor ${sources}) + +target_link_libraries(movements_extractor + ${MYSQL_LIBRARY} + ${ACE_LIBRARY} + ${BZIP2_LIBRARIES} + ${ZLIB_LIBRARIES} + Recast + Detour + collision + g3dlib + shared +) + +if( UNIX ) + install(TARGETS movements_extractor DESTINATION bin) +elseif( WIN32 ) + install(TARGETS movements_extractor DESTINATION "${CMAKE_INSTALL_PREFIX}") +endif() diff --git a/src/tools/mmap_extractor/Info/readme.txt b/src/tools/mmap_extractor/Info/readme.txt new file mode 100644 index 00000000000..8d7c4f9d2e0 --- /dev/null +++ b/src/tools/mmap_extractor/Info/readme.txt @@ -0,0 +1,66 @@ +Generator command line args + +--offMeshInput [file.*] Path to file containing off mesh connections data. + Format must be: (see offmesh_example.txt) + "map_id tile_x,tile_y (start_x start_y start_z) (end_x end_y end_z) size //optional comments" + Single mesh connection per line. + +--silent Make us script friendly. Do not wait for user input + on error or completion. + +--bigBaseUnit [true|false] Generate tile/map using bigger basic unit. + Use this option only if you have unexpected gaps. + + false: use normal metrics (default) + +--maxAngle [#] Max walkable inclination angle + + float between 45 and 90 degrees (default 60) + +--skipLiquid liquid data for maps + + false: include liquid data (default) + +--skipContinents [true|false] continents are maps 0 (Eastern Kingdoms), + 1 (Kalimdor), 530 (Outlands), 571 (Northrend) + + false: build continents (default) + +--skipJunkMaps [true|false] junk maps include some unused + maps, transport maps, and some other + + true: skip junk maps (default) + +--skipBattlegrounds [true|false] does not include PVP arenas + + false: skip battlegrounds (default) + +--debugOutput [true|false] create debugging files for use with RecastDemo + if you are only creating mmaps for use with MaNGOS, + you don't want debugging files + + false: don't create debugging files (default) + +--tile [#,#] Build the specified tile + seperate number with a comma ',' + must specify a map number (see below) + if this option is not used, all tiles are built + + [#] Build only the map specified by # + this command will build the map regardless of --skip* option settings + if you do not specify a map number, builds all maps that pass the filters specified by --skip* options + + +examples: + +movement_extractor +builds maps using the default settings (see above for defaults) + +movement_extractor --skipContinents true +builds the default maps, except continents + +movement_extractor 0 +builds all tiles of map 0 + +movement_extractor 0 --tile 34,46 +builds only tile 34,46 of map 0 (this is the southern face of blackrock mountain)
\ No newline at end of file diff --git a/src/tools/mmap_extractor/IntermediateValues.cpp b/src/tools/mmap_extractor/IntermediateValues.cpp new file mode 100644 index 00000000000..9eefb1e65f0 --- /dev/null +++ b/src/tools/mmap_extractor/IntermediateValues.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/> + * + * 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" + +namespace MMAP +{ + IntermediateValues::~IntermediateValues() + { + rcFreeCompactHeightfield(compactHeightfield); + rcFreeHeightField(heightfield); + rcFreeContourSet(contours); + rcFreePolyMesh(polyMesh); + rcFreePolyMeshDetail(polyMeshDetail); + } + + void IntermediateValues::writeIV(uint32 mapID, uint32 tileX, uint32 tileY) + { + char fileName[255]; + char tileString[25]; + sprintf(tileString, "[%02u,%02u]: ", tileX, tileY); + + printf("%sWriting debug output... \r", tileString); + + string name("meshes/%03u%02i%02i."); + +#define DEBUG_WRITE(fileExtension,data) \ + do { \ + sprintf(fileName, (name + fileExtension).c_str(), mapID, tileY, tileX); \ + FILE* file = fopen(fileName, "wb"); \ + if (!file) \ + { \ + char message[1024]; \ + sprintf(message, "%sFailed to open %s for writing!\n", tileString, fileName); \ + perror(message); \ + } \ + else \ + debugWrite(file, data); \ + if (file) fclose(file); \ + printf("%sWriting debug output... \r", tileString); \ + } while (false) + + if (heightfield) + DEBUG_WRITE("hf", heightfield); + if (compactHeightfield) + DEBUG_WRITE("chf", compactHeightfield); + if (contours) + DEBUG_WRITE("cs", contours); + if (polyMesh) + DEBUG_WRITE("pmesh", polyMesh); + if (polyMeshDetail) + DEBUG_WRITE("dmesh", polyMeshDetail); + +#undef DEBUG_WRITE + } + + void IntermediateValues::debugWrite(FILE* file, const rcHeightfield* 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, const rcCompactHeightfield* 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, const rcContourSet* 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, const rcPolyMesh* 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, const rcPolyMeshDetail* 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(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData) + { + char objFileName[255]; + sprintf(objFileName, "meshes/map%03u.obj", mapID); + + FILE* objFile = fopen(objFileName, "wb"); + if (!objFile) + { + char message[1024]; + sprintf(message, "Failed to open %s for writing!\n", objFileName); + perror(message); + return; + } + + G3D::Array<float> allVerts; + G3D::Array<int> allTris; + + allTris.append(meshData.liquidTris); + allVerts.append(meshData.liquidVerts); + TerrainBuilder::copyIndices(meshData.solidTris, allTris, allVerts.size() / 3); + allVerts.append(meshData.solidVerts); + + float* verts = allVerts.getCArray(); + int vertCount = allVerts.size() / 3; + int* tris = allTris.getCArray(); + int triCount = allTris.size() / 3; + + for (int i = 0; i < allVerts.size() / 3; i++) + fprintf(objFile, "v %f %f %f\n", verts[i*3], verts[i*3 + 1], verts[i*3 + 2]); + + for (int i = 0; i < allTris.size() / 3; i++) + fprintf(objFile, "f %i %i %i\n", tris[i*3] + 1, tris[i*3 + 1] + 1, tris[i*3 + 2] + 1); + + fclose(objFile); + + + char tileString[25]; + sprintf(tileString, "[%02u,%02u]: ", tileY, tileX); + printf("%sWriting debug output... \r", tileString); + + sprintf(objFileName, "meshes/%03u.map", mapID); + + objFile = fopen(objFileName, "wb"); + if (!objFile) + { + char message[1024]; + sprintf(message, "Failed to open %s for writing!\n", objFileName); + perror(message); + return; + } + + char b = '\0'; + fwrite(&b, sizeof(char), 1, objFile); + fclose(objFile); + + sprintf(objFileName, "meshes/%03u%02u%02u.mesh", mapID, tileY, tileX); + objFile = fopen(objFileName, "wb"); + if (!objFile) + { + char message[1024]; + sprintf(message, "Failed to open %s for writing!\n", objFileName); + perror(message); + return; + } + + fwrite(&vertCount, sizeof(int), 1, objFile); + fwrite(verts, sizeof(float), vertCount*3, objFile); + fflush(objFile); + + fwrite(&triCount, sizeof(int), 1, objFile); + fwrite(tris, sizeof(int), triCount*3, objFile); + fflush(objFile); + + fclose(objFile); + } +} diff --git a/src/tools/mmap_extractor/IntermediateValues.h b/src/tools/mmap_extractor/IntermediateValues.h new file mode 100644 index 00000000000..a267a0f6412 --- /dev/null +++ b/src/tools/mmap_extractor/IntermediateValues.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/> + * + * 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 "PathCommon.h" +#include "TerrainBuilder.h" +#include "Recast.h" +#include "DetourNavMesh.h" + +namespace MMAP +{ + // this class gathers all debug info holding and output + struct IntermediateValues + { + rcHeightfield* heightfield; + rcCompactHeightfield* compactHeightfield; + rcContourSet* contours; + rcPolyMesh* polyMesh; + rcPolyMeshDetail* polyMeshDetail; + + IntermediateValues() : compactHeightfield(NULL), heightfield(NULL), + contours(NULL), polyMesh(NULL), polyMeshDetail(NULL) {} + ~IntermediateValues(); + + void writeIV(uint32 mapID, uint32 tileX, uint32 tileY); + + void debugWrite(FILE* file, const rcHeightfield* mesh); + void debugWrite(FILE* file, const rcCompactHeightfield* chf); + void debugWrite(FILE* file, const rcContourSet* cs); + void debugWrite(FILE* file, const rcPolyMesh* mesh); + void debugWrite(FILE* file, const rcPolyMeshDetail* mesh); + + void generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData); + }; +} +#endif diff --git a/src/tools/mmap_extractor/MapBuilder.cpp b/src/tools/mmap_extractor/MapBuilder.cpp new file mode 100644 index 00000000000..3ca158f0127 --- /dev/null +++ b/src/tools/mmap_extractor/MapBuilder.cpp @@ -0,0 +1,894 @@ +/* + * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/> + * + * 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 "PathCommon.h" +#include "MapBuilder.h" + +#include "MapTree.h" +#include "ModelInstance.h" +#include "LoginDatabase.h" + +#include "DetourNavMeshBuilder.h" +#include "DetourCommon.h" + +// These make the linker happy. +LoginDatabaseWorkerPool LoginDatabase; +#include "DisableMgr.h" +namespace DisableMgr +{ + bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags) + { + return 0; + } +} + +using namespace VMAP; + +namespace MMAP +{ + MapBuilder::MapBuilder(float maxWalkableAngle, bool skipLiquid, + bool skipContinents, bool skipJunkMaps, bool skipBattlegrounds, + bool debugOutput, bool bigBaseUnit, const char* offMeshFilePath) : + m_terrainBuilder(NULL), + m_debugOutput (debugOutput), + m_skipContinents (skipContinents), + m_skipJunkMaps (skipJunkMaps), + m_skipBattlegrounds (skipBattlegrounds), + m_maxWalkableAngle (maxWalkableAngle), + m_bigBaseUnit (bigBaseUnit), + m_rcContext (NULL), + m_offMeshFilePath (offMeshFilePath) + { + m_terrainBuilder = new TerrainBuilder(skipLiquid); + + m_rcContext = new rcContext(false); + + discoverTiles(); + } + + /**************************************************************************/ + MapBuilder::~MapBuilder() + { + for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it) + { + (*it).second->clear(); + delete (*it).second; + } + + delete m_terrainBuilder; + delete m_rcContext; + } + + /**************************************************************************/ + void MapBuilder::discoverTiles() + { + vector<string> files; + uint32 mapID, tileX, tileY, tileID, count = 0; + char filter[12]; + + printf("Discovering maps... "); + getDirContents(files, "maps"); + for (uint32 i = 0; i < files.size(); ++i) + { + mapID = uint32(atoi(files[i].substr(0,3).c_str())); + if (m_tiles.find(mapID) == m_tiles.end()) + { + m_tiles.insert(pair<uint32,set<uint32>*>(mapID, new set<uint32>)); + count++; + } + } + + files.clear(); + getDirContents(files, "vmaps", "*.vmtree"); + for (uint32 i = 0; i < files.size(); ++i) + { + mapID = uint32(atoi(files[i].substr(0,3).c_str())); + m_tiles.insert(pair<uint32,set<uint32>*>(mapID, new set<uint32>)); + count++; + } + printf("found %u.\n", count); + + count = 0; + printf("Discovering tiles... "); + for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr) + { + set<uint32>* tiles = (*itr).second; + mapID = (*itr).first; + + sprintf(filter, "%03u*.vmtile", mapID); + files.clear(); + getDirContents(files, "vmaps", filter); + for (uint32 i = 0; i < files.size(); ++i) + { + tileX = uint32(atoi(files[i].substr(7,2).c_str())); + tileY = uint32(atoi(files[i].substr(4,2).c_str())); + tileID = StaticMapTree::packTileID(tileY, tileX); + + tiles->insert(tileID); + count++; + } + + sprintf(filter, "%03u*", mapID); + files.clear(); + getDirContents(files, "maps", filter); + for (uint32 i = 0; i < files.size(); ++i) + { + tileY = uint32(atoi(files[i].substr(3,2).c_str())); + tileX = uint32(atoi(files[i].substr(5,2).c_str())); + tileID = StaticMapTree::packTileID(tileX, tileY); + + if (tiles->insert(tileID).second) + count++; + } + } + printf("found %u.\n\n", count); + } + + /**************************************************************************/ + set<uint32>* MapBuilder::getTileList(uint32 mapID) + { + TileList::iterator itr = m_tiles.find(mapID); + if (itr != m_tiles.end()) + return (*itr).second; + + set<uint32>* tiles = new set<uint32>(); + m_tiles.insert(pair<uint32, set<uint32>*>(mapID, tiles)); + return tiles; + } + + /**************************************************************************/ + void MapBuilder::buildAllMaps() + { + for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it) + { + uint32 mapID = (*it).first; + if (!shouldSkipMap(mapID)) + buildMap(mapID); + } + } + + /**************************************************************************/ + void MapBuilder::getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY) + { + maxX = INT_MAX; + maxY = INT_MAX; + minX = INT_MIN; + minY = INT_MIN; + + float bmin[3], bmax[3], lmin[3], lmax[3]; + MeshData meshData; + + // make sure we process maps which don't have tiles + // initialize the static tree, which loads WDT models + if (!m_terrainBuilder->loadVMap(mapID, 64, 64, meshData)) + return; + + // get the coord bounds of the model data + if (meshData.solidVerts.size() + meshData.liquidVerts.size() == 0) + return; + + // get the coord bounds of the model data + if (meshData.solidVerts.size() && meshData.liquidVerts.size()) + { + rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax); + rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax); + rcVmin(bmin, lmin); + rcVmax(bmax, lmax); + } + else if (meshData.solidVerts.size()) + rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax); + else + rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax); + + // convert coord bounds to grid bounds + maxX = 32 - bmin[0] / GRID_SIZE; + maxY = 32 - bmin[2] / GRID_SIZE; + minX = 32 - bmax[0] / GRID_SIZE; + minY = 32 - bmax[2] / GRID_SIZE; + } + + /**************************************************************************/ + void MapBuilder::buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY) + { + dtNavMesh* navMesh = NULL; + buildNavMesh(mapID, navMesh); + if (!navMesh) + { + printf("Failed creating navmesh! \n"); + return; + } + + buildTile(mapID, tileX, tileY, navMesh); + dtFreeNavMesh(navMesh); + } + + /**************************************************************************/ + void MapBuilder::buildMap(uint32 mapID) + { + printf("Building map %03u:\n", mapID); + + set<uint32>* tiles = getTileList(mapID); + + // make sure we process maps which don't have tiles + if (!tiles->size()) + { + // convert coord bounds to grid bounds + uint32 minX, minY, maxX, maxY; + getGridBounds(mapID, minX, minY, maxX, maxY); + + // add all tiles within bounds to tile list. + for (uint32 i = minX; i <= maxX; ++i) + for (uint32 j = minY; j <= maxY; ++j) + tiles->insert(StaticMapTree::packTileID(i, j)); + } + + if (!tiles->size()) + return; + + // build navMesh + dtNavMesh* navMesh = NULL; + buildNavMesh(mapID, navMesh); + if (!navMesh) + { + printf("Failed creating navmesh! \n"); + return; + } + + // now start building mmtiles for each tile + printf("We have %u tiles. \n", (unsigned int)tiles->size()); + for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it) + { + uint32 tileX, tileY; + + // unpack tile coords + StaticMapTree::unpackTileID((*it), tileX, tileY); + + if (shouldSkipTile(mapID, tileX, tileY)) + continue; + + buildTile(mapID, tileX, tileY, navMesh); + } + + dtFreeNavMesh(navMesh); + + printf("Complete! \n\n"); + } + + /**************************************************************************/ + void MapBuilder::buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh) + { + printf("Building map %03u, tile [%02u,%02u]\n", mapID, tileX, tileY); + + MeshData meshData; + + // get heightmap data + m_terrainBuilder->loadMap(mapID, tileX, tileY, meshData); + + // get model data + m_terrainBuilder->loadVMap(mapID, tileY, tileX, meshData); + + // if there is no data, give up now + if (!meshData.solidVerts.size() && !meshData.liquidVerts.size()) + return; + + // remove unused vertices + TerrainBuilder::cleanVertices(meshData.solidVerts, meshData.solidTris); + TerrainBuilder::cleanVertices(meshData.liquidVerts, meshData.liquidTris); + + // gather all mesh data for final data check, and bounds calculation + G3D::Array<float> allVerts; + allVerts.append(meshData.liquidVerts); + allVerts.append(meshData.solidVerts); + + if (!allVerts.size()) + return; + + // get bounds of current tile + float bmin[3], bmax[3]; + getTileBounds(tileX, tileY, allVerts.getCArray(), allVerts.size() / 3, bmin, bmax); + + m_terrainBuilder->loadOffMeshConnections(mapID, tileX, tileY, meshData, m_offMeshFilePath); + + // build navmesh tile + buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh); + } + + /**************************************************************************/ + void MapBuilder::buildNavMesh(uint32 mapID, dtNavMesh* &navMesh) + { + set<uint32>* tiles = getTileList(mapID); + + // old code for non-statically assigned bitmask sizes: + ///*** calculate number of bits needed to store tiles & polys ***/ + //int tileBits = dtIlog2(dtNextPow2(tiles->size())); + //if (tileBits < 1) tileBits = 1; // need at least one bit! + //int polyBits = sizeof(dtPolyRef)*8 - SALT_MIN_BITS - tileBits; + + int tileBits = STATIC_TILE_BITS; + int polyBits = STATIC_POLY_BITS; + + int maxTiles = tiles->size(); + int maxPolysPerTile = 1 << polyBits; + + /*** calculate bounds of map ***/ + + uint32 tileXMin = 64, tileYMin = 64, tileXMax = 0, tileYMax = 0, tileX, tileY; + for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it) + { + StaticMapTree::unpackTileID((*it), tileX, tileY); + + if (tileX > tileXMax) + tileXMax = tileX; + else if (tileX < tileXMin) + tileXMin = tileX; + + if (tileY > tileYMax) + tileYMax = tileY; + else if (tileY < tileYMin) + tileYMin = tileY; + } + + // use Max because '32 - tileX' is negative for values over 32 + float bmin[3], bmax[3]; + getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax); + + /*** now create the navmesh ***/ + + // navmesh creation params + dtNavMeshParams navMeshParams; + memset(&navMeshParams, 0, sizeof(dtNavMeshParams)); + navMeshParams.tileWidth = GRID_SIZE; + navMeshParams.tileHeight = GRID_SIZE; + rcVcopy(navMeshParams.orig, bmin); + navMeshParams.maxTiles = maxTiles; + navMeshParams.maxPolys = maxPolysPerTile; + + navMesh = dtAllocNavMesh(); + printf("Creating navMesh... \r"); + if (!navMesh->init(&navMeshParams)) + { + printf("Failed creating navmesh! \n"); + return; + } + + char fileName[25]; + sprintf(fileName, "mmaps/%03u.mmap", mapID); + + FILE* file = fopen(fileName, "wb"); + if (!file) + { + dtFreeNavMesh(navMesh); + char message[1024]; + sprintf(message, "Failed to open %s for writing!\n", fileName); + perror(message); + return; + } + + // now that we know navMesh params are valid, we can write them to file + fwrite(&navMeshParams, sizeof(dtNavMeshParams), 1, file); + fclose(file); + } + + /**************************************************************************/ + void MapBuilder::buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY, + MeshData &meshData, float bmin[3], float bmax[3], + dtNavMesh* navMesh) + { + // console output + char tileString[10]; + sprintf(tileString, "[%02i,%02i]: ", tileX, tileY); + printf("%s Building movemap tiles... \r", tileString); + + IntermediateValues iv; + + float* tVerts = meshData.solidVerts.getCArray(); + int tVertCount = meshData.solidVerts.size() / 3; + int* tTris = meshData.solidTris.getCArray(); + int tTriCount = meshData.solidTris.size() / 3; + + float* lVerts = meshData.liquidVerts.getCArray(); + int lVertCount = meshData.liquidVerts.size() / 3; + int* lTris = meshData.liquidTris.getCArray(); + int lTriCount = meshData.liquidTris.size() / 3; + uint8* lTriFlags = meshData.liquidType.getCArray(); + + // these are WORLD UNIT based metrics + // this are basic unit dimentions + // value have to divide GRID_SIZE(533.33333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc ) + const static float BASE_UNIT_DIM = m_bigBaseUnit ? 0.533333f : 0.266666f; + + // All are in UNIT metrics! + const static int VERTEX_PER_MAP = int(GRID_SIZE/BASE_UNIT_DIM + 0.5f); + const static int VERTEX_PER_TILE = m_bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP + const static int TILES_PER_MAP = VERTEX_PER_MAP/VERTEX_PER_TILE; + + rcConfig config; + memset(&config, 0, sizeof(rcConfig)); + + rcVcopy(config.bmin, bmin); + rcVcopy(config.bmax, bmax); + + config.maxVertsPerPoly = DT_VERTS_PER_POLYGON; + config.cs = BASE_UNIT_DIM; + config.ch = BASE_UNIT_DIM; + config.walkableSlopeAngle = m_maxWalkableAngle; + config.tileSize = VERTEX_PER_TILE; + config.walkableRadius = m_bigBaseUnit ? 1 : 2; + config.borderSize = config.walkableRadius + 3; + config.maxEdgeLen = VERTEX_PER_TILE + 1; //anything bigger than tileSize + config.walkableHeight = m_bigBaseUnit ? 3 : 6; + config.walkableClimb = m_bigBaseUnit ? 2 : 4; // keep less than walkableHeight + config.minRegionArea = rcSqr(60); + config.mergeRegionArea = rcSqr(50); + config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons) + config.detailSampleDist = config.cs * 64; + config.detailSampleMaxError = config.ch * 2; + + // 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 + Tile* tiles = new Tile[TILES_PER_MAP * TILES_PER_MAP]; + + // Initialize per tile config. + rcConfig tileCfg; + memcpy(&tileCfg, &config, sizeof(rcConfig)); + tileCfg.width = config.tileSize + config.borderSize*2; + tileCfg.height = config.tileSize + config.borderSize*2; + + // 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*config.tileSize - config.borderSize)*config.cs; + tileCfg.bmin[2] = config.bmin[2] + (y*config.tileSize - config.borderSize)*config.cs; + tileCfg.bmax[0] = config.bmin[0] + ((x+1)*config.tileSize + config.borderSize)*config.cs; + tileCfg.bmax[2] = config.bmin[2] + ((y+1)*config.tileSize + config.borderSize)*config.cs; + + float tbmin[2], tbmax[2]; + tbmin[0] = tileCfg.bmin[0]; + tbmin[1] = tileCfg.bmin[2]; + tbmax[0] = tileCfg.bmax[0]; + tbmax[1] = tileCfg.bmax[2]; + + // 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)) + { + printf("%sFailed building heightfield! \n", tileString); + continue; + } + + // mark all walkable tiles, both liquids and solids + unsigned char* triFlags = new unsigned char[tTriCount]; + memset(triFlags, NAV_GROUND, tTriCount*sizeof(unsigned char)); + rcClearUnwalkableTriangles(m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags); + rcRasterizeTriangles(m_rcContext, tVerts, tVertCount, tTris, triFlags, tTriCount, *tile.solid, config.walkableClimb); + delete [] triFlags; + + rcFilterLowHangingWalkableObstacles(m_rcContext, config.walkableClimb, *tile.solid); + rcFilterLedgeSpans(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid); + rcFilterWalkableLowHeightSpans(m_rcContext, tileCfg.walkableHeight, *tile.solid); + + 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)) + { + printf("%sFailed compacting heightfield! \n", tileString); + continue; + } + + // build polymesh intermediates + if (!rcErodeWalkableArea(m_rcContext, config.walkableRadius, *tile.chf)) + { + printf("%sFailed eroding area! \n", tileString); + continue; + } + + if (!rcBuildDistanceField(m_rcContext, *tile.chf)) + { + printf("%sFailed building distance field! \n", tileString); + continue; + } + + if (!rcBuildRegions(m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea)) + { + printf("%sFailed building regions! \n", tileString); + continue; + } + + tile.cset = rcAllocContourSet(); + if (!tile.cset || !rcBuildContours(m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset)) + { + printf("%sFailed building contours! \n", tileString); + continue; + } + + // build polymesh + tile.pmesh = rcAllocPolyMesh(); + if (!tile.pmesh || !rcBuildPolyMesh(m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh)) + { + printf("%sFailed building polymesh! \n", tileString); + continue; + } + + tile.dmesh = rcAllocPolyMeshDetail(); + if (!tile.dmesh || !rcBuildPolyMeshDetail(m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg .detailSampleMaxError, *tile.dmesh)) + { + printf("%sFailed building polymesh detail! \n", 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 = NULL; + rcFreeCompactHeightfield(tile.chf); + tile.chf = NULL; + rcFreeContourSet(tile.cset); + tile.cset = NULL; + } + } + + // merge per tile poly and detail meshes + rcPolyMesh** pmmerge = new rcPolyMesh*[TILES_PER_MAP * TILES_PER_MAP]; + if (!pmmerge) + { + printf("%s alloc pmmerge FIALED! \r", tileString); + return; + } + + rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[TILES_PER_MAP * TILES_PER_MAP]; + if (!dmmerge) + { + printf("%s alloc dmmerge FIALED! \r", tileString); + return; + } + + int nmerge = 0; + 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]; + if (tile.pmesh) + { + pmmerge[nmerge] = tile.pmesh; + dmmerge[nmerge] = tile.dmesh; + nmerge++; + } + } + } + + iv.polyMesh = rcAllocPolyMesh(); + if (!iv.polyMesh) + { + printf("%s alloc iv.polyMesh FIALED! \r", tileString); + return; + } + rcMergePolyMeshes(m_rcContext, pmmerge, nmerge, *iv.polyMesh); + + iv.polyMeshDetail = rcAllocPolyMeshDetail(); + if (!iv.polyMeshDetail) + { + printf("%s alloc m_dmesh FIALED! \r", tileString); + return; + } + rcMergePolyMeshDetails(m_rcContext, dmmerge, nmerge, *iv.polyMeshDetail); + + // free things up + delete [] pmmerge; + delete [] dmmerge; + + delete [] tiles; + + // remove padding for extraction + for (int i = 0; i < iv.polyMesh->nverts; ++i) + { + unsigned short* v = &iv.polyMesh->verts[i*3]; + v[0] -= (unsigned short)config.borderSize; + v[2] -= (unsigned short)config.borderSize; + } + + // 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 (iv.polyMesh->areas[i] & RC_WALKABLE_AREA) + iv.polyMesh->flags[i] = iv.polyMesh->areas[i]; + + // setup mesh parameters + dtNavMeshCreateParams params; + memset(¶ms, 0, sizeof(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.getCArray(); + params.offMeshConCount = meshData.offMeshConnections.size()/6; + params.offMeshConRad = meshData.offMeshConnectionRads.getCArray(); + params.offMeshConDir = meshData.offMeshConnectionDirs.getCArray(); + params.offMeshConAreas = meshData.offMeshConnectionsAreas.getCArray(); + params.offMeshConFlags = meshData.offMeshConnectionsFlags.getCArray(); + + 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) - navMesh->getParams()->orig[0]) / GRID_SIZE; + params.tileY = (((bmin[2] + bmax[2]) / 2) - navMesh->getParams()->orig[2]) / GRID_SIZE; + rcVcopy(params.bmin, bmin); + rcVcopy(params.bmax, bmax); + params.cs = config.cs; + params.ch = config.ch; + params.tileSize = VERTEX_PER_MAP; + + // will hold final navmesh + unsigned char* navData = NULL; + int navDataSize = 0; + + do + { + // these values are checked within dtCreateNavMeshData - handle them here + // so we have a clear error message + if (params.nvp > DT_VERTS_PER_POLYGON) + { + printf("%s Invalid verts-per-polygon value! \n", tileString); + continue; + } + if (params.vertCount >= 0xffff) + { + printf("%s Too many vertices! \n", tileString); + continue; + } + 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 + //printf("%sNo vertices to build tile! \n", tileString); + continue; + } + if (!params.polyCount || !params.polys || + TILES_PER_MAP*TILES_PER_MAP == params.polyCount) + { + // 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 + // drop tiles with only exact count - some tiles may have geometry while having less tiles + printf("%s No polygons to build on tile! \n", tileString); + continue; + } + if (!params.detailMeshes || !params.detailVerts || !params.detailTris) + { + printf("%s No detail mesh to build tile! \n", tileString); + continue; + } + + printf("%s Building navmesh tile... \r", tileString); + if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) + { + printf("%s Failed building navmesh tile! \n", tileString); + continue; + } + + dtTileRef tileRef = 0; + printf("%s Adding tile to navmesh... \r", tileString); + // DT_TILE_FREE_DATA tells detour to unallocate memory when the tile + // is removed via removeTile() + dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef); + if (!tileRef || dtResult != DT_SUCCESS) + { + printf("%s Failed adding tile to navmesh! \n", tileString); + continue; + } + + // file output + char fileName[255]; + sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX); + FILE* file = fopen(fileName, "wb"); + if (!file) + { + char message[1024]; + sprintf(message, "Failed to open %s for writing!\n", fileName); + perror(message); + navMesh->removeTile(tileRef, NULL, NULL); + continue; + } + + printf("%s Writing to file... \r", tileString); + + // write header + MmapTileHeader header; + header.usesLiquids = m_terrainBuilder->usesLiquids(); + header.size = uint32(navDataSize); + fwrite(&header, sizeof(MmapTileHeader), 1, file); + + // write data + fwrite(navData, sizeof(unsigned char), navDataSize, file); + fclose(file); + + // now that tile is written to disk, we can unload it + navMesh->removeTile(tileRef, NULL, NULL); + } + while (0); + + if (m_debugOutput) + { + // restore padding so that the debug visualization is correct + for (int i = 0; i < iv.polyMesh->nverts; ++i) + { + unsigned short* v = &iv.polyMesh->verts[i*3]; + v[0] += (unsigned short)config.borderSize; + v[2] += (unsigned short)config.borderSize; + } + + iv.generateObjFile(mapID, tileX, tileY, meshData); + iv.writeIV(mapID, tileX, tileY); + } + } + + /**************************************************************************/ + void MapBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax) + { + // this is for elevation + if (verts && vertCount) + rcCalcBounds(verts, vertCount, bmin, bmax); + else + { + bmin[1] = FLT_MIN; + bmax[1] = FLT_MAX; + } + + // this is for width and depth + bmax[0] = (32 - int(tileX)) * GRID_SIZE; + bmax[2] = (32 - int(tileY)) * GRID_SIZE; + bmin[0] = bmax[0] - GRID_SIZE; + bmin[2] = bmax[2] - GRID_SIZE; + } + + /**************************************************************************/ + bool MapBuilder::shouldSkipMap(uint32 mapID) + { + if (m_skipContinents) + switch (mapID) + { + case 0: + case 1: + case 530: + case 571: + return true; + default: + break; + } + + if (m_skipJunkMaps) + switch (mapID) + { + case 13: // test.wdt + case 25: // ScottTest.wdt + case 29: // Test.wdt + case 42: // Colin.wdt + case 169: // EmeraldDream.wdt (unused, and very large) + case 451: // development.wdt + case 573: // ExteriorTest.wdt + case 597: // CraigTest.wdt + case 605: // development_nonweighted.wdt + case 606: // QA_DVD.wdt + return true; + default: + if (isTransportMap(mapID)) + return true; + break; + } + + if (m_skipBattlegrounds) + switch (mapID) + { + case 30: // AV + case 37: // ? + case 489: // WSG + case 529: // AB + case 566: // EotS + case 607: // SotA + case 628: // IoC + return true; + default: + break; + } + + return false; + } + + /**************************************************************************/ + bool MapBuilder::isTransportMap(uint32 mapID) + { + switch (mapID) + { + // transport maps + case 582: + case 584: + case 586: + case 587: + case 588: + case 589: + case 590: + case 591: + case 592: + case 593: + case 594: + case 596: + case 610: + case 612: + case 613: + case 614: + case 620: + case 621: + case 622: + case 623: + case 641: + case 642: + case 647: + case 672: + case 673: + case 712: + case 713: + case 718: + return true; + default: + return false; + } + } + + /**************************************************************************/ + bool MapBuilder::shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY) + { + char fileName[255]; + sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX); + FILE* file = fopen(fileName, "rb"); + if (!file) + return false; + + MmapTileHeader header; + fread(&header, sizeof(MmapTileHeader), 1, file); + fclose(file); + + if (header.mmapMagic != MMAP_MAGIC || header.dtVersion != DT_NAVMESH_VERSION) + return false; + + if (header.mmapVersion != MMAP_VERSION) + return false; + + return true; + } + +} diff --git a/src/tools/mmap_extractor/MapBuilder.h b/src/tools/mmap_extractor/MapBuilder.h new file mode 100644 index 00000000000..d0f33ce9a79 --- /dev/null +++ b/src/tools/mmap_extractor/MapBuilder.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/> + * + * 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 _MAP_BUILDER_H +#define _MAP_BUILDER_H + +#include <vector> +#include <set> +#include <map> + +#include "TerrainBuilder.h" +#include "IntermediateValues.h" + +#include "IVMapManager.h" +#include "WorldModel.h" + +#include "Recast.h" +#include "DetourNavMesh.h" + +using namespace std; +using namespace VMAP; + +// G3D namespace typedefs conflicts with ACE typedefs + +namespace MMAP +{ + typedef map<uint32,set<uint32>*> TileList; + struct Tile + { + Tile() : chf(NULL), solid(NULL), cset(NULL), pmesh(NULL), dmesh(NULL) {} + ~Tile() + { + rcFreeCompactHeightfield(chf); + rcFreeContourSet(cset); + rcFreeHeightField(solid); + rcFreePolyMesh(pmesh); + rcFreePolyMeshDetail(dmesh); + } + rcCompactHeightfield* chf; + rcHeightfield* solid; + rcContourSet* cset; + rcPolyMesh* pmesh; + rcPolyMeshDetail* dmesh; + }; + + class MapBuilder + { + public: + MapBuilder(float maxWalkableAngle = 60.f, + bool skipLiquid = false, + bool skipContinents = false, + bool skipJunkMaps = true, + bool skipBattlegrounds = false, + bool debugOutput = false, + bool bigBaseUnit = false, + const char* offMeshFilePath = NULL); + + ~MapBuilder(); + + // builds all mmap tiles for the specified map id (ignores skip settings) + void buildMap(uint32 mapID); + + // builds an mmap tile for the specified map and its mesh + void buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY); + + // builds list of maps, then builds all of mmap tiles (based on the skip settings) + void buildAllMaps(); + + private: + // detect maps and tiles + void discoverTiles(); + set<uint32>* getTileList(uint32 mapID); + + void buildNavMesh(uint32 mapID, dtNavMesh* &navMesh); + + void buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh); + + // move map building + void buildMoveMapTile(uint32 mapID, + uint32 tileX, + uint32 tileY, + MeshData &meshData, + float bmin[3], + float bmax[3], + dtNavMesh* navMesh); + + void getTileBounds(uint32 tileX, uint32 tileY, + float* verts, int vertCount, + float* bmin, float* bmax); + void getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY); + + bool shouldSkipMap(uint32 mapID); + bool isTransportMap(uint32 mapID); + bool shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY); + + TerrainBuilder* m_terrainBuilder; + TileList m_tiles; + + bool m_debugOutput; + + const char* m_offMeshFilePath; + bool m_skipContinents; + bool m_skipJunkMaps; + bool m_skipBattlegrounds; + + float m_maxWalkableAngle; + bool m_bigBaseUnit; + + // build performance - not really used for now + rcContext* m_rcContext; + }; +} + +#endif diff --git a/src/tools/mmap_extractor/PathCommon.h b/src/tools/mmap_extractor/PathCommon.h new file mode 100644 index 00000000000..fd02ec02d50 --- /dev/null +++ b/src/tools/mmap_extractor/PathCommon.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/> + * + * 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_COMMON_H +#define _MMAP_COMMON_H + +#include <string> +#include <vector> + +#include "Define.h" + +#ifndef _WIN32 + #include <stddef.h> + #include <dirent.h> +#endif + +#ifdef __linux__ + #include <errno.h> +#endif + +using namespace std; + +namespace MMAP +{ + inline bool matchWildcardFilter(const char* filter, const char* str) + { + if (!filter || !str) + return false; + + // end on null character + while (*filter && *str) + { + if (*filter == '*') + { + if (*++filter == '\0') // wildcard at end of filter means all remaing chars match + return true; + + while (true) + { + if (*filter == *str) + break; + if (*str == '\0') + return false; // reached end of string without matching next filter character + str++; + } + } + else if (*filter != *str) + return false; // mismatch + + filter++; + str++; + } + + return ((*filter == '\0' || (*filter == '*' && *++filter == '\0')) && *str == '\0'); + } + + enum ListFilesResult + { + LISTFILE_DIRECTORY_NOT_FOUND = 0, + LISTFILE_OK = 1 + }; + + inline ListFilesResult getDirContents(vector<string> &fileList, string dirpath = ".", string filter = "*", bool includeSubDirs = false) + { + #ifdef WIN32 + HANDLE hFind; + WIN32_FIND_DATA findFileInfo; + string directory; + + directory = dirpath + "/" + filter; + + hFind = FindFirstFile(directory.c_str(), &findFileInfo); + + if (hFind == INVALID_HANDLE_VALUE) + return LISTFILE_DIRECTORY_NOT_FOUND; + do + { + if (includeSubDirs || (findFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + fileList.push_back(string(findFileInfo.cFileName)); + } + while (FindNextFile(hFind, &findFileInfo)); + + FindClose(hFind); + + #else + const char *p = dirpath.c_str(); + DIR * dirp = opendir(p); + struct dirent * dp; + dirp = opendir(p); + + while (dirp) + { + errno = 0; + if ((dp = readdir(dirp)) != NULL) + { + if (matchWildcardFilter(filter.c_str(), dp->d_name)) + fileList.push_back(string(dp->d_name)); + } + else + break; + } + + if (dirp) + closedir(dirp); + else + return LISTFILE_DIRECTORY_NOT_FOUND; + #endif + + return LISTFILE_OK; + } +} + +#endif diff --git a/src/tools/mmap_extractor/PathGenerator.cpp b/src/tools/mmap_extractor/PathGenerator.cpp new file mode 100644 index 00000000000..2eb2c6545c4 --- /dev/null +++ b/src/tools/mmap_extractor/PathGenerator.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PathCommon.h" +#include "MapBuilder.h" + +using namespace MMAP; + +bool checkDirectories(bool debugOutput) +{ + vector<string> dirFiles; + + if (getDirContents(dirFiles, "maps") == LISTFILE_DIRECTORY_NOT_FOUND || !dirFiles.size()) + { + printf("'maps' directory is empty or does not exist\n"); + return false; + } + + dirFiles.clear(); + if (getDirContents(dirFiles, "vmaps", "*.vmtree") == LISTFILE_DIRECTORY_NOT_FOUND || !dirFiles.size()) + { + printf("'vmaps' directory is empty or does not exist\n"); + return false; + } + + dirFiles.clear(); + if (getDirContents(dirFiles, "mmaps") == LISTFILE_DIRECTORY_NOT_FOUND) + { + printf("'mmaps' directory does not exist\n"); + return false; + } + + dirFiles.clear(); + if (debugOutput) + { + if (getDirContents(dirFiles, "meshes") == LISTFILE_DIRECTORY_NOT_FOUND) + { + printf("'meshes' directory does not exist (no place to put debugOutput files)\n"); + return false; + } + } + + return true; +} + +bool handleArgs(int argc, char** argv, + int &mapnum, + int &tileX, + int &tileY, + float &maxAngle, + bool &skipLiquid, + bool &skipContinents, + bool &skipJunkMaps, + bool &skipBattlegrounds, + bool &debugOutput, + bool &silent, + bool &bigBaseUnit, + char* &offMeshInputPath) +{ + char* param = NULL; + for (int i = 1; i < argc; ++i) + { + if (strcmp(argv[i], "--maxAngle") == 0) + { + param = argv[++i]; + if (!param) + return false; + + float maxangle = atof(param); + if (maxangle <= 90.f && maxangle >= 45.f) + maxAngle = maxangle; + else + printf("invalid option for '--maxAngle', using default\n"); + } + else if (strcmp(argv[i], "--tile") == 0) + { + param = argv[++i]; + if (!param) + return false; + + char* stileX = strtok(param, ","); + char* stileY = strtok(NULL, ","); + int tilex = atoi(stileX); + int tiley = atoi(stileY); + + if ((tilex > 0 && tilex < 64) || (tilex == 0 && strcmp(stileX, "0") == 0)) + tileX = tilex; + if ((tiley > 0 && tiley < 64) || (tiley == 0 && strcmp(stileY, "0") == 0)) + tileY = tiley; + + if (tileX < 0 || tileY < 0) + { + printf("invalid tile coords.\n"); + return false; + } + } + else if (strcmp(argv[i], "--skipLiquid") == 0) + { + param = argv[++i]; + if (!param) + return false; + + if (strcmp(param, "true") == 0) + skipLiquid = true; + else if (strcmp(param, "false") == 0) + skipLiquid = false; + else + printf("invalid option for '--skipLiquid', using default\n"); + } + else if (strcmp(argv[i], "--skipContinents") == 0) + { + param = argv[++i]; + if (!param) + return false; + + if (strcmp(param, "true") == 0) + skipContinents = true; + else if (strcmp(param, "false") == 0) + skipContinents = false; + else + printf("invalid option for '--skipContinents', using default\n"); + } + else if (strcmp(argv[i], "--skipJunkMaps") == 0) + { + param = argv[++i]; + if (!param) + return false; + + if (strcmp(param, "true") == 0) + skipJunkMaps = true; + else if (strcmp(param, "false") == 0) + skipJunkMaps = false; + else + printf("invalid option for '--skipJunkMaps', using default\n"); + } + else if (strcmp(argv[i], "--skipBattlegrounds") == 0) + { + param = argv[++i]; + if (!param) + return false; + + if (strcmp(param, "true") == 0) + skipBattlegrounds = true; + else if (strcmp(param, "false") == 0) + skipBattlegrounds = false; + else + printf("invalid option for '--skipBattlegrounds', using default\n"); + } + else if (strcmp(argv[i], "--debugOutput") == 0) + { + param = argv[++i]; + if (!param) + return false; + + if (strcmp(param, "true") == 0) + debugOutput = true; + else if (strcmp(param, "false") == 0) + debugOutput = false; + else + printf("invalid option for '--debugOutput', using default true\n"); + } + else if (strcmp(argv[i], "--silent") == 0) + { + silent = true; + } + else if (strcmp(argv[i], "--bigBaseUnit") == 0) + { + param = argv[++i]; + if (!param) + return false; + + if (strcmp(param, "true") == 0) + bigBaseUnit = true; + else if (strcmp(param, "false") == 0) + bigBaseUnit = false; + else + printf("invalid option for '--bigBaseUnit', using default false\n"); + } + else if (strcmp(argv[i], "--offMeshInput") == 0) + { + param = argv[++i]; + if (!param) + return false; + + offMeshInputPath = param; + } + else + { + int map = atoi(argv[i]); + if (map > 0 || (map == 0 && (strcmp(argv[i], "0") == 0))) + mapnum = map; + else + { + printf("invalid map id\n"); + return false; + } + } + } + + return true; +} + +int finish(const char* message, int returnValue) +{ + printf("%s", message); + getchar(); + return returnValue; +} + +int main(int argc, char** argv) +{ + int mapnum = -1; + float maxAngle = 60.0f; + int tileX = -1, tileY = -1; + bool skipLiquid = false, + skipContinents = false, + skipJunkMaps = true, + skipBattlegrounds = false, + debugOutput = false, + silent = false, + bigBaseUnit = false; + char* offMeshInputPath = NULL; + + bool validParam = handleArgs(argc, argv, mapnum, + tileX, tileY, maxAngle, + skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds, + debugOutput, silent, bigBaseUnit, offMeshInputPath); + + if (!validParam) + return silent ? -1 : finish("You have specified invalid parameters", -1); + + if (mapnum == -1 && debugOutput) + { + if (silent) + return -2; + + printf("You have specifed debug output, but didn't specify a map to generate.\n"); + printf("This will generate debug output for ALL maps.\n"); + printf("Are you sure you want to continue? (y/n) "); + if (getchar() != 'y') + return 0; + } + + if (!checkDirectories(debugOutput)) + return silent ? -3 : finish("Press any key to close...", -3); + + MapBuilder builder(maxAngle, skipLiquid, skipContinents, skipJunkMaps, + skipBattlegrounds, debugOutput, bigBaseUnit, offMeshInputPath); + + if (tileX > -1 && tileY > -1 && mapnum >= 0) + builder.buildSingleTile(mapnum, tileX, tileY); + else if (mapnum >= 0) + builder.buildMap(uint32(mapnum)); + else + builder.buildAllMaps(); + + return silent ? 1 : finish("Movemap build is complete!", 1); +} diff --git a/src/tools/mmap_extractor/TerrainBuilder.cpp b/src/tools/mmap_extractor/TerrainBuilder.cpp new file mode 100644 index 00000000000..c696f6017a5 --- /dev/null +++ b/src/tools/mmap_extractor/TerrainBuilder.cpp @@ -0,0 +1,854 @@ +/* + * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/> + * + * 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 "PathCommon.h" +#include "MapBuilder.h" + +#include "VMapManager2.h" +#include "MapTree.h" +#include "ModelInstance.h" + +namespace MMAP +{ + TerrainBuilder::TerrainBuilder(bool skipLiquid) : m_skipLiquid (skipLiquid){ } + TerrainBuilder::~TerrainBuilder() { } + + /**************************************************************************/ + 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) + { + if (loadMap(mapID, tileX, tileY, meshData, ENTIRE)) + { + loadMap(mapID, tileX+1, tileY, meshData, LEFT); + loadMap(mapID, tileX-1, tileY, meshData, RIGHT); + loadMap(mapID, tileX, tileY+1, meshData, TOP); + loadMap(mapID, tileX, tileY-1, meshData, BOTTOM); + } + } + + /**************************************************************************/ + bool TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion) + { + char mapFileName[255]; + sprintf(mapFileName, "maps/%03u%02u%02u.map", mapID, tileY, tileX); + + FILE* mapFile = fopen(mapFileName, "rb"); + if (!mapFile) + return false; + + map_fileheader fheader; + fread(&fheader, sizeof(map_fileheader), 1, mapFile); + + if (fheader.versionMagic != *((uint32 const*)(MAP_VERSION_MAGIC))) + { + fclose(mapFile); + printf("%s is the wrong version, please extract new .map files\n", mapFileName); + return false; + } + + map_heightHeader hheader; + fseek(mapFile, fheader.heightMapOffset, SEEK_SET); + fread(&hheader, sizeof(map_heightHeader), 1, mapFile); + + bool haveTerrain = !(hheader.flags & MAP_HEIGHT_NO_HEIGHT); + bool haveLiquid = fheader.liquidMapOffset && !m_skipLiquid; + + // no data in this map file + if (!haveTerrain && !haveLiquid) + { + fclose(mapFile); + return false; + } + + // data used later + uint16 holes[16][16]; + memset(holes, 0, sizeof(holes)); + uint8 liquid_type[16][16]; + memset(liquid_type, 0, sizeof(liquid_type)); + G3D::Array<int> ltriangles; + G3D::Array<int> ttriangles; + + // terrain data + if (haveTerrain) + { + int i; + float heightMultiplier; + float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ]; + + if (hheader.flags & MAP_HEIGHT_AS_INT8) + { + uint8 v9[V9_SIZE_SQ]; + uint8 v8[V8_SIZE_SQ]; + fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile); + fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile); + heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255; + + for (i = 0; i < V9_SIZE_SQ; ++i) + V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight; + + for (i = 0; i < V8_SIZE_SQ; ++i) + V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight; + } + else if (hheader.flags & MAP_HEIGHT_AS_INT16) + { + uint16 v9[V9_SIZE_SQ]; + uint16 v8[V8_SIZE_SQ]; + fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile); + fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile); + heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535; + + for (i = 0; i < V9_SIZE_SQ; ++i) + V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight; + + for (i = 0; i < V8_SIZE_SQ; ++i) + V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight; + } + else + { + fread (V9, sizeof(float), V9_SIZE_SQ, mapFile); + fread(V8, sizeof(float), V8_SIZE_SQ, mapFile); + } + + // hole data + memset(holes, 0, fheader.holesSize); + fseek(mapFile, fheader.holesOffset, SEEK_SET); + fread(holes, fheader.holesSize, 1, mapFile); + + int count = meshData.solidVerts.size() / 3; + float xoffset = (float(tileX)-32)*GRID_SIZE; + float yoffset = (float(tileY)-32)*GRID_SIZE; + + float coord[3]; + + for (i = 0; i < V9_SIZE_SQ; ++i) + { + getHeightCoord(i, GRID_V9, xoffset, yoffset, coord, V9); + meshData.solidVerts.append(coord[0]); + meshData.solidVerts.append(coord[2]); + meshData.solidVerts.append(coord[1]); + } + + for (i = 0; i < V8_SIZE_SQ; ++i) + { + getHeightCoord(i, GRID_V8, xoffset, yoffset, coord, V8); + meshData.solidVerts.append(coord[0]); + meshData.solidVerts.append(coord[2]); + meshData.solidVerts.append(coord[1]); + } + + int j, indices[3], loopStart, loopEnd, loopInc; + getLoopVars(portion, loopStart, loopEnd, loopInc); + for (i = loopStart; i < loopEnd; i+=loopInc) + for (j = TOP; j <= BOTTOM; j+=1) + { + getHeightTriangle(i, Spot(j), indices); + ttriangles.append(indices[2] + count); + ttriangles.append(indices[1] + count); + ttriangles.append(indices[0] + count); + } + } + + // liquid data + if (haveLiquid) + { + map_liquidHeader lheader; + fseek(mapFile, fheader.liquidMapOffset, SEEK_SET); + fread(&lheader, sizeof(map_liquidHeader), 1, mapFile); + + float* liquid_map = NULL; + + if (!(lheader.flags & MAP_LIQUID_NO_TYPE)) + fread(liquid_type, sizeof(liquid_type), 1, mapFile); + + if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT)) + { + liquid_map = new float [lheader.width*lheader.height]; + fread(liquid_map, sizeof(float), lheader.width*lheader.height, mapFile); + } + + if (liquid_type && liquid_map) + { + int count = meshData.liquidVerts.size() / 3; + float xoffset = (float(tileX)-32)*GRID_SIZE; + float yoffset = (float(tileY)-32)*GRID_SIZE; + + float coord[3]; + int row, col; + + // generate coordinates + if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT)) + { + 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 + meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, INVALID_MAP_LIQ_HEIGHT, (yoffset+row*GRID_PART_SIZE)*-1); + continue; + } + + getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map); + meshData.liquidVerts.append(coord[0]); + meshData.liquidVerts.append(coord[2]); + meshData.liquidVerts.append(coord[1]); + j++; + } + } + else + { + for (int i = 0; i < V9_SIZE_SQ; ++i) + { + row = i / V9_SIZE; + col = i % V9_SIZE; + meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, lheader.liquidLevel, (yoffset+row*GRID_PART_SIZE)*-1); + } + } + + delete [] liquid_map; + + int indices[3], loopStart, loopEnd, loopInc, triInc; + getLoopVars(portion, loopStart, loopEnd, loopInc); + triInc = BOTTOM-TOP; + + // generate triangles + for (int i = loopStart; i < loopEnd; i+=loopInc) + for (int j = TOP; j <= BOTTOM; j+= triInc) + { + getHeightTriangle(i, Spot(j), indices, true); + ltriangles.append(indices[2] + count); + ltriangles.append(indices[1] + count); + ltriangles.append(indices[0] + count); + } + } + } + + fclose(mapFile); + + // now that we have gathered the data, we can figure out which parts to keep: + // liquid above ground, ground above liquid + int loopStart, loopEnd, loopInc, tTriCount = 4; + bool useTerrain, useLiquid; + + float* lverts = meshData.liquidVerts.getCArray(); + int* ltris = ltriangles.getCArray(); + + float* tverts = meshData.solidVerts.getCArray(); + int* ttris = ttriangles.getCArray(); + + 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 + float* lverts_copy = NULL; + if (meshData.liquidVerts.size()) + { + lverts_copy = new float[meshData.liquidVerts.size()]; + memcpy(lverts_copy, 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; + uint8 liquidType = MAP_LIQUID_TYPE_NO_WATER; + + // if there is no liquid, don't use liquid + if (!liquid_type || !meshData.liquidVerts.size() || !ltriangles.size()) + useLiquid = false; + else + { + liquidType = getLiquidType(i, liquid_type); + switch (liquidType) + { + default: + useLiquid = false; + break; + case MAP_LIQUID_TYPE_WATER: + case MAP_LIQUID_TYPE_OCEAN: + // merge different types of water + liquidType = NAV_WATER; + break; + case MAP_LIQUID_TYPE_MAGMA: + liquidType = NAV_MAGMA; + break; + case MAP_LIQUID_TYPE_SLIME: + liquidType = NAV_SLIME; + break; + case MAP_LIQUID_TYPE_DARK_WATER: + // players should not be here, so logically neither should creatures + useTerrain = false; + useLiquid = false; + break; + } + } + + // if there is no terrain, don't use terrain + if (!ttriangles.size()) + 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) + 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.liquidType.append(liquidType); + for (int k = 0; k < 3; ++k) + meshData.liquidTris.append(ltris[k]); + } + + if (useTerrain) + for (int k = 0; k < 3*tTriCount/2; ++k) + meshData.solidTris.append(ttris[k]); + + // advance to next set of triangles + ltris += 3; + ttris += 3*tTriCount/2; + } + } + + if (lverts_copy) + delete [] lverts_copy; + + return meshData.solidTris.size() || meshData.liquidTris.size(); + } + + /**************************************************************************/ + 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] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f; + coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f; + coord[2] = v[index]; + break; + case GRID_V8: + coord[0] = (xOffset + index%(V8_SIZE)*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f; + coord[1] = (yOffset + (int)(index/(V8_SIZE))*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f; + coord[2] = v[index]; + break; + } + } + + /**************************************************************************/ + void TerrainBuilder::getHeightTriangle(int square, Spot triangle, int* indices, bool liquid/* = false*/) + { + int rowOffset = square/V8_SIZE; + if (!liquid) + switch (triangle) + { + case TOP: + indices[0] = square+rowOffset; // 0-----1 .... 128 + indices[1] = square+1+rowOffset; // |\ T /| + indices[2] = (V9_SIZE_SQ)+square; // | \ / | + break; // |L 0 R| .. 127 + case LEFT: // | / \ | + indices[0] = square+rowOffset; // |/ B \| + indices[1] = (V9_SIZE_SQ)+square; // 129---130 ... 386 + indices[2] = square+V9_SIZE+rowOffset; // |\ /| + break; // | \ / | + case RIGHT: // | 128 | .. 255 + indices[0] = square+1+rowOffset; // | / \ | + indices[1] = square+V9_SIZE+1+rowOffset; // |/ \| + indices[2] = (V9_SIZE_SQ)+square; // 258---259 ... 515 + break; + case BOTTOM: + indices[0] = (V9_SIZE_SQ)+square; + indices[1] = square+V9_SIZE+1+rowOffset; + indices[2] = square+V9_SIZE+rowOffset; + break; + default: break; + } + else + switch (triangle) + { // 0-----1 .... 128 + case TOP: // |\ | + indices[0] = square+rowOffset; // | \ T | + indices[1] = square+1+rowOffset; // | \ | + indices[2] = square+V9_SIZE+1+rowOffset; // | B \ | + break; // | \| + case BOTTOM: // 129---130 ... 386 + indices[0] = square+rowOffset; // |\ | + indices[1] = square+V9_SIZE+1+rowOffset; // | \ | + indices[2] = square+V9_SIZE+rowOffset; // | \ | + break; // | \ | + default: break; // | \| + } // 258---259 ... 515 + + } + + /**************************************************************************/ + 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] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f; + coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f; + coord[2] = v[index2]; + } + + static uint16 holetab_h[4] = {0x1111, 0x2222, 0x4444, 0x8888}; + static uint16 holetab_v[4] = {0x000F, 0x00F0, 0x0F00, 0xF000}; + + /**************************************************************************/ + bool TerrainBuilder::isHole(int square, const uint16 holes[16][16]) + { + int row = square / 128; + int col = square % 128; + int cellRow = row / 8; // 8 squares per cell + int cellCol = col / 8; + int holeRow = row % 8 / 2; + int holeCol = (square - (row * 128 + cellCol * 8)) / 2; + + uint16 hole = holes[cellRow][cellCol]; + + return (hole & holetab_h[holeCol] & holetab_v[holeRow]) != 0; + } + + /**************************************************************************/ + uint8 TerrainBuilder::getLiquidType(int square, const uint8 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) + { + IVMapManager* vmapManager = new VMapManager2(); + int result = vmapManager->loadMap("vmaps", mapID, tileX, tileY); + bool retval = false; + + do + { + if (result == VMAP_LOAD_RESULT_ERROR) + break; + + InstanceTreeMap instanceTrees; + ((VMapManager2*)vmapManager)->getInstanceMapTree(instanceTrees); + + if (!instanceTrees[mapID]) + break; + + ModelInstance* models = NULL; + uint32 count = 0; + instanceTrees[mapID]->getModelInstances(models, count); + + if (!models) + break; + + for (uint32 i = 0; i < count; ++i) + { + ModelInstance instance = models[i]; + + // model instances exist in tree even though there are instances of that model in this tile + WorldModel* worldModel = instance.getWorldModel(); + if (!worldModel) + continue; + + // now we have a model to add to the meshdata + retval = true; + + vector<GroupModel> groupModels; + worldModel->getGroupModels(groupModels); + + // all M2s need to have triangle indices reversed + bool isM2 = instance.name.find(".m2") != instance.name.npos || instance.name.find(".M2") != instance.name.npos; + + // transform data + float scale = instance.iScale; + G3D::Matrix3 rotation = G3D::Matrix3::fromEulerAnglesXYZ(G3D::pi()*instance.iRot.z/-180.f, G3D::pi()*instance.iRot.x/-180.f, G3D::pi()*instance.iRot.y/-180.f); + Vector3 position = instance.iPos; + position.x -= 32*GRID_SIZE; + position.y -= 32*GRID_SIZE; + + for (vector<GroupModel>::iterator it = groupModels.begin(); it != groupModels.end(); ++it) + { + vector<Vector3> tempVertices; + vector<Vector3> transformedVertices; + vector<MeshTriangle> tempTriangles; + WmoLiquid* liquid = NULL; + + (*it).getMeshData(tempVertices, tempTriangles, liquid); + + // first handle collision mesh + transform(tempVertices, transformedVertices, scale, rotation, position); + + int offset = meshData.solidVerts.size() / 3; + + copyVertices(transformedVertices, meshData.solidVerts); + copyIndices(tempTriangles, meshData.solidTris, offset, isM2); + + // now handle liquid data + if (liquid) + { + vector<Vector3> liqVerts; + vector<int> liqTris; + uint32 tilesX, tilesY, vertsX, vertsY; + Vector3 corner; + liquid->getPosInfo(tilesX, tilesY, corner); + vertsX = tilesX + 1; + vertsY = tilesY + 1; + uint8* flags = liquid->GetFlagsStorage(); + float* data = liquid->GetHeightStorage(); + uint8 type = NAV_EMPTY; + + // convert liquid type to NavTerrain + switch (liquid->GetType()) + { + case 0: + case 1: + type = NAV_WATER; + break; + case 2: + type = NAV_MAGMA; + break; + case 3: + type = NAV_SLIME; + break; + } + + // 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 + + Vector3 vert; + for (uint32 x = 0; x < vertsX; ++x) + for (uint32 y = 0; y < vertsY; ++y) + { + vert = 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; + liqVerts.push_back(vert); + } + + int idx1, idx2, idx3, idx4; + uint32 square; + for (uint32 x = 0; x < tilesX; ++x) + for (uint32 y = 0; y < tilesY; ++y) + if ((flags[x+y*tilesX] & 0x0f) != 0x0f) + { + square = x * tilesY + y; + idx1 = square+x; + idx2 = square+1+x; + idx3 = square+tilesY+1+1+x; + idx4 = square+tilesY+1+x; + + // top triangle + liqTris.push_back(idx3); + liqTris.push_back(idx2); + liqTris.push_back(idx1); + // bottom triangle + liqTris.push_back(idx4); + liqTris.push_back(idx3); + liqTris.push_back(idx1); + } + + uint32 liqOffset = meshData.liquidVerts.size() / 3; + for (uint32 i = 0; i < liqVerts.size(); ++i) + meshData.liquidVerts.append(liqVerts[i].y, liqVerts[i].z, liqVerts[i].x); + + for (uint32 i = 0; i < liqTris.size() / 3; ++i) + { + meshData.liquidTris.append(liqTris[i*3+1] + liqOffset, liqTris[i*3+2] + liqOffset, liqTris[i*3] + liqOffset); + meshData.liquidType.append(type); + } + } + } + } + } + while (false); + + vmapManager->unloadMap(mapID, tileX, tileY); + delete vmapManager; + + return retval; + } + + /**************************************************************************/ + void TerrainBuilder::transform(vector<Vector3> &source, vector<Vector3> &transformedVertices, float scale, G3D::Matrix3 &rotation, Vector3 &position) + { + for (vector<Vector3>::iterator it = source.begin(); it != source.end(); ++it) + { + // apply tranform, then mirror along the horizontal axes + Vector3 v((*it) * rotation * scale + position); + v.x *= -1.f; + v.y *= -1.f; + transformedVertices.push_back(v); + } + } + + /**************************************************************************/ + void TerrainBuilder::copyVertices(vector<Vector3> &source, G3D::Array<float> &dest) + { + for (vector<Vector3>::iterator it = source.begin(); it != source.end(); ++it) + { + dest.push_back((*it).y); + dest.push_back((*it).z); + dest.push_back((*it).x); + } + } + + /**************************************************************************/ + void TerrainBuilder::copyIndices(vector<MeshTriangle> &source, G3D::Array<int> &dest, int offset, bool flip) + { + if (flip) + { + for (vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it) + { + dest.push_back((*it).idx2+offset); + dest.push_back((*it).idx1+offset); + dest.push_back((*it).idx0+offset); + } + } + else + { + for (vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it) + { + dest.push_back((*it).idx0+offset); + dest.push_back((*it).idx1+offset); + dest.push_back((*it).idx2+offset); + } + } + } + + /**************************************************************************/ + void TerrainBuilder::copyIndices(G3D::Array<int> &source, G3D::Array<int> &dest, int offset) + { + int* src = source.getCArray(); + for (int32 i = 0; i < source.size(); ++i) + dest.append(src[i] + offset); + } + + /**************************************************************************/ + void TerrainBuilder::cleanVertices(G3D::Array<float> &verts, G3D::Array<int> &tris) + { + map<int, int> vertMap; + + int* t = tris.getCArray(); + float* v = verts.getCArray(); + + // collect all the vertex indices from triangle + for (int i = 0; i < tris.size(); ++i) + { + if (vertMap.find(t[i]) != vertMap.end()) + continue; + + vertMap.insert(std::pair<int, int>(t[i], 0)); + } + + // collect the vertices + G3D::Array<float> cleanVerts; + int index, count = 0; + for (map<int, int>::iterator it = vertMap.begin(); it != vertMap.end(); ++it) + { + index = (*it).first; + (*it).second = count; + cleanVerts.append(v[index*3], v[index*3+1], v[index*3+2]); + count++; + } + verts.fastClear(); + verts.append(cleanVerts); + cleanVerts.clear(); + + // update triangles to use new indices + for (int i = 0; i < tris.size(); ++i) + { + map<int, int>::iterator it; + if ((it = vertMap.find(t[i])) == vertMap.end()) + continue; + + t[i] = (*it).second; + } + + vertMap.clear(); + } + + /**************************************************************************/ + void TerrainBuilder::loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char* offMeshFilePath) + { + // no meshfile input given? + if (offMeshFilePath == NULL) + return; + + FILE* fp = fopen(offMeshFilePath, "rb"); + if (!fp) + { + printf(" loadOffMeshConnections:: input file %s not found!\n", offMeshFilePath); + return; + } + + // pretty silly thing, as we parse entire file and load only the tile we need + // but we don't expect this file to be too large + char* buf = new char[512]; + while(fgets(buf, 512, fp)) + { + float p0[3], p1[3]; + int mid, tx, ty; + float size; + if (10 != sscanf(buf, "%d %d,%d (%f %f %f) (%f %f %f) %f", &mid, &tx, &ty, + &p0[0], &p0[1], &p0[2], &p1[0], &p1[1], &p1[2], &size)) + continue; + + if (mapID == mid, tileX == tx, tileY == ty) + { + meshData.offMeshConnections.append(p0[1]); + meshData.offMeshConnections.append(p0[2]); + meshData.offMeshConnections.append(p0[0]); + + meshData.offMeshConnections.append(p1[1]); + meshData.offMeshConnections.append(p1[2]); + meshData.offMeshConnections.append(p1[0]); + + meshData.offMeshConnectionDirs.append(1); // 1 - both direction, 0 - one sided + meshData.offMeshConnectionRads.append(size); // agent size equivalent + // can be used same way as polygon flags + meshData.offMeshConnectionsAreas.append((unsigned char)0xFF); + meshData.offMeshConnectionsFlags.append((unsigned short)0xFF); // all movement masks can make this path + } + + } + + delete [] buf; + fclose(fp); + } +} diff --git a/src/tools/mmap_extractor/TerrainBuilder.h b/src/tools/mmap_extractor/TerrainBuilder.h new file mode 100644 index 00000000000..a7f21883af2 --- /dev/null +++ b/src/tools/mmap_extractor/TerrainBuilder.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/> + * + * 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 "PathCommon.h" +#include "Map.h" +#include "SharedDefines.h" + +#include "WorldModel.h" + +#include "G3D/Array.h" +#include "G3D/Vector3.h" +#include "G3D/Matrix3.h" + +using namespace Trinity; + +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.33333f; + 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 = -500.f; + static const float INVALID_MAP_LIQ_HEIGHT_MAX = 5000.0f; + + // see following files: + // contrib/extractor/system.cpp + // src/game/Map.cpp + static char const* MAP_VERSION_MAGIC = "v1.2"; + + struct MeshData + { + G3D::Array<float> solidVerts; + G3D::Array<int> solidTris; + + G3D::Array<float> liquidVerts; + G3D::Array<int> liquidTris; + G3D::Array<uint8> liquidType; + + // offmesh connection data + G3D::Array<float> offMeshConnections; // [p0y,p0z,p0x,p1y,p1z,p1x] - per connection + G3D::Array<float> offMeshConnectionRads; + G3D::Array<unsigned char> offMeshConnectionDirs; + G3D::Array<unsigned char> offMeshConnectionsAreas; + G3D::Array<unsigned short> offMeshConnectionsFlags; + }; + + class TerrainBuilder + { + public: + TerrainBuilder(bool skipLiquid); + ~TerrainBuilder(); + + void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData); + bool loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData); + void loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char* offMeshFilePath); + + bool usesLiquids() { return !m_skipLiquid; } + + // vert and triangle methods + static void transform(vector<G3D::Vector3> &original, vector<G3D::Vector3> &transformed, + float scale, G3D::Matrix3 &rotation, G3D::Vector3 &position); + static void copyVertices(vector<G3D::Vector3> &source, G3D::Array<float> &dest); + static void copyIndices(vector<VMAP::MeshTriangle> &source, G3D::Array<int> &dest, int offest, bool flip); + static void copyIndices(G3D::Array<int> &src, G3D::Array<int> &dest, int offset); + static void cleanVertices(G3D::Array<float> &verts, G3D::Array<int> &tris); + private: + /// Loads a portion of a map's terrain + bool loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion); + + /// Sets loop variables for selecting only certain parts of a map's terrain + void getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc); + + /// Controls whether liquids are loaded + bool m_skipLiquid; + + /// Load the map terrain from file + bool loadHeightMap(uint32 mapID, uint32 tileX, uint32 tileY, G3D::Array<float> &vertices, G3D::Array<int> &triangles, Spot portion); + + /// Get the vector coordinate for a specific position + void getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v); + + /// Get the triangle's vector indices for a specific position + void getHeightTriangle(int square, Spot triangle, int* indices, bool liquid = false); + + /// Determines if the specific position's triangles should be rendered + bool isHole(int square, const uint16 holes[16][16]); + + /// Get the liquid vector coordinate for a specific position + void getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v); + + /// Get the liquid type for a specific position + uint8 getLiquidType(int square, const uint8 liquid_type[16][16]); + + // hide parameterless and copy constructor + TerrainBuilder(); + TerrainBuilder(const TerrainBuilder &tb); + }; +} + +#endif + diff --git a/src/tools/mmap_extractor/VMapExtensions.cpp b/src/tools/mmap_extractor/VMapExtensions.cpp new file mode 100644 index 00000000000..4f203e11c21 --- /dev/null +++ b/src/tools/mmap_extractor/VMapExtensions.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/> + * + * 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 <vector> +#include "MapTree.h" +#include "VMapManager2.h" +#include "WorldModel.h" +#include "ModelInstance.h" + +using namespace std; + +namespace VMAP +{ + // Need direct access to encapsulated VMAP data, so we add functions for MMAP generator + // maybe add MapBuilder as friend to all of the below classes would be better? + + // declared in src/shared/vmap/MapTree.h + void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count) + { + models = iTreeValues; + count = iNTreeValues; + } + + // declared in src/shared/vmap/VMapManager2.h + void VMapManager2::getInstanceMapTree(InstanceTreeMap &instanceMapTree) + { + instanceMapTree = iInstanceMapTrees; + } + + // declared in src/shared/vmap/WorldModel.h + void WorldModel::getGroupModels(vector<GroupModel> &groupModels) + { + groupModels = this->groupModels; + } + + // declared in src/shared/vmap/WorldModel.h + void GroupModel::getMeshData(vector<Vector3> &vertices, vector<MeshTriangle> &triangles, WmoLiquid* &liquid) + { + vertices = this->vertices; + triangles = this->triangles; + liquid = iLiquid; + } + + // declared in src/shared/vmap/ModelInstance.h + WorldModel* const ModelInstance::getWorldModel() + { + return iModel; + } + + // declared in src/shared/vmap/WorldModel.h + void WmoLiquid::getPosInfo(uint32 &tilesX, uint32 &tilesY, Vector3 &corner) const + { + tilesX = iTilesX; + tilesY = iTilesY; + corner = iCorner; + } +} |