Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
MapBuilder.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#include "PathCommon.h"
7#include "MapBuilder.h"
8
9#include "MapTree.h"
10#include "ModelInstance.h"
11
12#include "DetourNavMeshBuilder.h"
13#include "DetourNavMesh.h"
14#include "DetourCommon.h"
15#include "DisableMgr.h"
16
17#include <boost/asio/post.hpp>
18#include <boost/asio/thread_pool.hpp>
19
20#include <functional>
21#include <thread>
22#include <vector>
23
24uint32 GetLiquidFlags(uint32 /*liquidType*/) { return 0; }
25
26namespace DisableMgr
27{
28 bool IsDisabledFor(DisableType /*type*/, uint32 /*entry*/, Unit const* /*unit*/, uint8 /*flags*/ /*= 0*/) { return false; }
29}
30
31#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
32#define MMAP_VERSION 5.2f
33
34struct MmapTileHeader
35{
38 float mmapVersion;
40 bool usesLiquids : 1;
41
44};
45
46namespace MMAP
47{
48 MapBuilder::MapBuilder(float maxWalkableAngle, bool skipLiquid,
49 bool skipContinents, bool skipJunkMaps, bool skipBattlegrounds,
50 bool debugOutput, bool bigBaseUnit, const char* offMeshFilePath) :
51 m_terrainBuilder (NULL),
52 m_debugOutput (debugOutput),
53 m_offMeshFilePath (offMeshFilePath),
54 m_skipContinents (skipContinents),
55 m_skipJunkMaps (skipJunkMaps),
56 m_skipBattlegrounds (skipBattlegrounds),
57 m_maxWalkableAngle (maxWalkableAngle),
58 m_bigBaseUnit (bigBaseUnit),
59 m_rcContext (NULL)
60 {
61 m_terrainBuilder = new TerrainBuilder(skipLiquid);
62
63 m_rcContext = new rcContext(false);
64
66 }
67
68 /**************************************************************************/
70 {
71 for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
72 {
73 (*it).second->clear();
74 delete (*it).second;
75 }
76
77 delete m_terrainBuilder;
78 delete m_rcContext;
79 }
80
81 /**************************************************************************/
83 {
84 std::vector<std::string> files;
85 uint32 mapID, tileX, tileY, tileID, count = 0;
86
87 printf("Discovering maps... ");
88 getDirContents(files, "maps");
89 for (uint32 i = 0; i < files.size(); ++i)
90 {
91 mapID = uint32(atoi(files[i].substr(0, 4).c_str()));
92 if (m_tiles.find(mapID) == m_tiles.end())
93 {
94 m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, new std::set<uint32>));
95 count++;
96 }
97 }
98
99 files.clear();
100 getDirContents(files, "vmaps", "*.vmtree");
101 for (uint32 i = 0; i < files.size(); ++i)
102 {
103 mapID = uint32(atoi(files[i].substr(0, 4).c_str()));
104 m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, new std::set<uint32>));
105 count++;
106 }
107 printf("found %u.\n", count);
108
109 count = 0;
110 printf("Discovering tiles... ");
111 for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr)
112 {
113 std::set<uint32>* tiles = (*itr).second;
114 mapID = (*itr).first;
115
116 files.clear();
117 char filter[13];
118 snprintf(filter, sizeof(filter), "%04u*.vmtile", mapID);
119 getDirContents(files, "vmaps", filter);
120 for (uint32 i = 0; i < files.size(); ++i)
121 {
122 tileX = uint32(atoi(files[i].substr(8, 2).c_str()));
123 tileY = uint32(atoi(files[i].substr(5, 2).c_str()));
124 tileID = StaticMapTree::packTileID(tileY, tileX);
125
126 tiles->insert(tileID);
127 count++;
128 }
129
130 files.clear();
131 snprintf(filter, sizeof(filter), "%04u*", mapID);
132 getDirContents(files, "maps", filter);
133 for (uint32 i = 0; i < files.size(); ++i)
134 {
135 tileY = uint32(atoi(files[i].substr(5, 2).c_str()));
136 tileX = uint32(atoi(files[i].substr(8, 2).c_str()));
137 tileID = StaticMapTree::packTileID(tileX, tileY);
138
139 if (tiles->insert(tileID).second)
140 count++;
141 }
142 }
143 printf("found %u.\n", count);
144 }
145
146 /**************************************************************************/
147 std::set<uint32>* MapBuilder::getTileList(uint32 mapID)
148 {
149 TileList::iterator itr = m_tiles.find(mapID);
150 if (itr != m_tiles.end())
151 return (*itr).second;
152
153 std::set<uint32>* tiles = new std::set<uint32>();
154 m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, tiles));
155 return tiles;
156 }
157
158 /**************************************************************************/
159 void MapBuilder::buildAllMaps(unsigned int threads)
160 {
161 std::vector<uint32> mapsToBuild;
162
163 for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
164 {
165 uint32 mapID = it->first;
166 if (!shouldSkipMap(mapID))
167 mapsToBuild.push_back(mapID);
168 }
169
170 printf("Using %u threads to extract mmaps\n", threads);
171
172 if (threads == 0)
173 {
174 for (std::vector<uint32>::iterator it = mapsToBuild.begin(); it != mapsToBuild.end(); ++it)
175 buildMap(*it);
176
177 return;
178 }
179
180 boost::asio::thread_pool threadPool(threads);
181
182 for (std::vector<uint32>::iterator it = mapsToBuild.begin(); it != mapsToBuild.end(); ++it)
183 {
184 uint32 mapID = *it;
185 boost::asio::post(threadPool,
186 [this, mapID]
187 {
188 buildMap(mapID);
189 });
190 }
191
192 threadPool.join();
193 }
194
195 /**************************************************************************/
196 void MapBuilder::getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY)
197 {
198 maxX = INT_MAX;
199 maxY = INT_MAX;
200 minX = INT_MIN;
201 minY = INT_MIN;
202
203 float bmin[3], bmax[3], lmin[3], lmax[3];
204 MeshData meshData;
205
206 // make sure we process maps which don't have tiles
207 // initialize the static tree, which loads WDT models
208 if (!m_terrainBuilder->loadVMap(mapID, 64, 64, meshData))
209 return;
210
211 // get the coord bounds of the model data
212 if (meshData.solidVerts.size() + meshData.liquidVerts.size() == 0)
213 return;
214
215 // get the coord bounds of the model data
216 if (meshData.solidVerts.size() && meshData.liquidVerts.size())
217 {
218 rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
219 rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
220 rcVmin(bmin, lmin);
221 rcVmax(bmax, lmax);
222 }
223 else if (meshData.solidVerts.size())
224 rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
225 else
226 rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
227
228 // convert coord bounds to grid bounds
229 maxX = 32 - bmin[0] / GRID_SIZE;
230 maxY = 32 - bmin[2] / GRID_SIZE;
231 minX = 32 - bmax[0] / GRID_SIZE;
232 minY = 32 - bmax[2] / GRID_SIZE;
233 }
234
236 {
237 FILE* file = fopen(name, "rb");
238 if (!file)
239 return;
240
241 printf("Building mesh from file\n");
242 int tileX, tileY, mapId;
243 if ((fread(&mapId, sizeof(int), 1, file) != 1) || (fread(&tileX, sizeof(int), 1, file) != 1) || (fread(&tileY, sizeof(int), 1, file) != 1))
244 return;
245
246 dtNavMesh* navMesh = NULL;
247 buildNavMesh(mapId, navMesh);
248 if (!navMesh)
249 {
250 printf("Failed creating navmesh!\n");
251 fclose(file);
252 return;
253 }
254
255 uint32 verticesCount, indicesCount;
256 if (fread(&verticesCount, sizeof(uint32), 1, file) != 1)
257 return;
258 if (fread(&indicesCount, sizeof(uint32), 1, file) != 1)
259 return;
260
261 float* verts = new float[verticesCount];
262 int* inds = new int[indicesCount];
263
264 if ((fread(verts, sizeof(float), verticesCount, file) != verticesCount) ||
265 (fread(inds, sizeof(int), indicesCount, file) != indicesCount))
266 {
267 delete[] verts;
268 delete[] inds;
269 return;
270 }
271
272 MeshData data;
273
274 for (uint32 i = 0; i < verticesCount; ++i)
275 data.solidVerts.append(verts[i]);
276
277 for (uint32 i = 0; i < indicesCount; ++i)
278 data.solidTris.append(inds[i]);
279
280 delete[] verts;
281 delete[] inds;
282
284 // get bounds of current tile
285 float bmin[3], bmax[3];
286 getTileBounds(tileX, tileY, data.solidVerts.getCArray(), data.solidVerts.size() / 3, bmin, bmax);
287
288 // build navmesh tile
289 buildMoveMapTile(mapId, tileX, tileY, data, bmin, bmax, navMesh);
290 fclose(file);
291 }
292
293 /**************************************************************************/
295 {
296 dtNavMesh* navMesh = NULL;
297 buildNavMesh(mapID, navMesh);
298 if (!navMesh)
299 {
300 printf("Failed creating navmesh!\n");
301 return;
302 }
303
304 buildTile(mapID, tileX, tileY, navMesh);
305 dtFreeNavMesh(navMesh);
306 }
307
308 /**************************************************************************/
310 {
311 printf("[Thread %u] Building map %04u:\n", uint32(std::hash<std::thread::id>()(std::this_thread::get_id())), mapID);
312
313 std::set<uint32>* tiles = getTileList(mapID);
314
315 // make sure we process maps which don't have tiles
316 if (!tiles->size())
317 {
318 // convert coord bounds to grid bounds
319 uint32 minX, minY, maxX, maxY;
320 getGridBounds(mapID, minX, minY, maxX, maxY);
321
322 // add all tiles within bounds to tile list.
323 for (uint32 i = minX; i <= maxX; ++i)
324 for (uint32 j = minY; j <= maxY; ++j)
325 tiles->insert(StaticMapTree::packTileID(i, j));
326 }
327
328 if (!tiles->empty())
329 {
330 // build navMesh
331 dtNavMesh* navMesh = NULL;
332 buildNavMesh(mapID, navMesh);
333 if (!navMesh)
334 {
335 printf("[Map %04i] Failed creating navmesh!\n", mapID);
336 return;
337 }
338 // now start building mmtiles for each tile
339 printf("[Map %04i] We have %u tiles. \n", mapID, (unsigned int)tiles->size());
340 for (std::set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
341 {
342 uint32 tileX, tileY;
343
344 // unpack tile coords
345 StaticMapTree::unpackTileID((*it), tileX, tileY);
346
347 if (shouldSkipTile(mapID, tileX, tileY))
348 continue;
349
350 buildTile(mapID, tileX, tileY, navMesh);
351 }
352
353 dtFreeNavMesh(navMesh);
354 }
355
356 printf("[Map %04u] Complete!\n", mapID);
357 }
358
359 /**************************************************************************/
360 void MapBuilder::buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh)
361 {
362 printf("[Map %04i] Building tile [%02u,%02u]\n", mapID, tileX, tileY);
363
364 MeshData meshData;
365
366 // get heightmap data
367 m_terrainBuilder->loadMap(mapID, tileX, tileY, meshData);
368
369 // get model data
370 m_terrainBuilder->loadVMap(mapID, tileY, tileX, meshData);
371
372 // if there is no data, give up now
373 if (!meshData.solidVerts.size() && !meshData.liquidVerts.size())
374 return;
375
376 // remove unused vertices
379
380 // gather all mesh data for final data check, and bounds calculation
381 G3D::Array<float> allVerts;
382 allVerts.append(meshData.liquidVerts);
383 allVerts.append(meshData.solidVerts);
384
385 if (!allVerts.size())
386 return;
387
388 // get bounds of current tile
389 float bmin[3], bmax[3];
390 getTileBounds(tileX, tileY, allVerts.getCArray(), allVerts.size() / 3, bmin, bmax);
391
392 m_terrainBuilder->loadOffMeshConnections(mapID, tileX, tileY, meshData, m_offMeshFilePath);
393
394 // build navmesh tile
395 buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh);
396 }
397
398 /**************************************************************************/
399 void MapBuilder::buildNavMesh(uint32 mapID, dtNavMesh* &navMesh)
400 {
401 std::set<uint32>* tiles = getTileList(mapID);
402
403 // old code for non-statically assigned bitmask sizes:
405 //int tileBits = dtIlog2(dtNextPow2(tiles->size()));
406 //if (tileBits < 1) tileBits = 1; // need at least one bit!
407 //int polyBits = sizeof(dtPolyRef)*8 - SALT_MIN_BITS - tileBits;
408
409 int polyBits = STATIC_POLY_BITS;
410
411 int maxTiles = tiles->size();
412 int maxPolysPerTile = 1 << polyBits;
413
414 /*** calculate bounds of map ***/
415
416 uint32 tileXMin = 64, tileYMin = 64, tileXMax = 0, tileYMax = 0, tileX, tileY;
417 for (std::set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
418 {
419 StaticMapTree::unpackTileID(*it, tileX, tileY);
420
421 if (tileX > tileXMax)
422 tileXMax = tileX;
423 else if (tileX < tileXMin)
424 tileXMin = tileX;
425
426 if (tileY > tileYMax)
427 tileYMax = tileY;
428 else if (tileY < tileYMin)
429 tileYMin = tileY;
430 }
431
432 // use Max because '32 - tileX' is negative for values over 32
433 float bmin[3], bmax[3];
434 getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax);
435
436 /*** now create the navmesh ***/
437
438 // navmesh creation params
439 dtNavMeshParams navMeshParams;
440 memset(&navMeshParams, 0, sizeof(dtNavMeshParams));
441 navMeshParams.tileWidth = GRID_SIZE;
442 navMeshParams.tileHeight = GRID_SIZE;
443 rcVcopy(navMeshParams.orig, bmin);
444 navMeshParams.maxTiles = maxTiles;
445 navMeshParams.maxPolys = maxPolysPerTile;
446
447 navMesh = dtAllocNavMesh();
448 printf("[Map %04u] Creating navMesh...\n", mapID);
449 if (!navMesh->init(&navMeshParams))
450 {
451 printf("[Map %04u] Failed creating navmesh! \n", mapID);
452 return;
453 }
454
455 char fileName[25];
456 snprintf(fileName, sizeof(fileName), "mmaps/%04u.mmap", mapID);
457
458 FILE* file = fopen(fileName, "wb");
459 if (!file)
460 {
461 dtFreeNavMesh(navMesh);
462 char message[1024];
463 snprintf(message, sizeof(message), "[Map %04u] Failed to open %s for writing!\n", mapID, fileName);
464 perror(message);
465 return;
466 }
467
468 // now that we know navMesh params are valid, we can write them to file
469 fwrite(&navMeshParams, sizeof(dtNavMeshParams), 1, file);
470 fclose(file);
471 }
472
473 /**************************************************************************/
475 MeshData &meshData, float bmin[3], float bmax[3],
476 dtNavMesh* navMesh)
477 {
478 // console output
479 char tileString[25];
480 snprintf(tileString, sizeof(tileString), "[Map %04u] [%02i,%02i]: ", mapID, tileX, tileY);
481 printf("%s Building movemap tiles...\n", tileString);
482
484
485 float* tVerts = meshData.solidVerts.getCArray();
486 int tVertCount = meshData.solidVerts.size() / 3;
487 int* tTris = meshData.solidTris.getCArray();
488 int tTriCount = meshData.solidTris.size() / 3;
489
490 float* lVerts = meshData.liquidVerts.getCArray();
491 int lVertCount = meshData.liquidVerts.size() / 3;
492 int* lTris = meshData.liquidTris.getCArray();
493 int lTriCount = meshData.liquidTris.size() / 3;
494 uint8* lTriFlags = meshData.liquidType.getCArray();
495
496 // these are WORLD UNIT based metrics
497 // this are basic unit dimentions
498 // value have to divide GRID_SIZE(533.3333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc )
499 const static float BASE_UNIT_DIM = m_bigBaseUnit ? 0.5333333f : 0.2666666f;
500
501 // All are in UNIT metrics!
502 const static int VERTEX_PER_MAP = int(GRID_SIZE/BASE_UNIT_DIM + 0.5f);
503 const static int VERTEX_PER_TILE = m_bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP
504 const static int TILES_PER_MAP = VERTEX_PER_MAP/VERTEX_PER_TILE;
505
506 rcConfig config;
507 memset(&config, 0, sizeof(rcConfig));
508
509 rcVcopy(config.bmin, bmin);
510 rcVcopy(config.bmax, bmax);
511
512 config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
513 config.cs = BASE_UNIT_DIM;
514 config.ch = BASE_UNIT_DIM;
515 config.walkableSlopeAngle = m_maxWalkableAngle;
516 config.tileSize = VERTEX_PER_TILE;
517 config.walkableRadius = m_bigBaseUnit ? 1 : 2;
518 config.borderSize = config.walkableRadius + 3;
519 config.maxEdgeLen = VERTEX_PER_TILE + 1; // anything bigger than tileSize
520 config.walkableHeight = m_bigBaseUnit ? 3 : 6;
521 config.walkableClimb = m_bigBaseUnit ? 2 : 4; // keep less than walkableHeight
522 config.minRegionArea = rcSqr(60);
523 config.mergeRegionArea = rcSqr(50);
524 config.maxSimplificationError = 1.8f; // eliminates most jagged edges (tiny polygons)
525 config.detailSampleDist = config.cs * 64;
526 config.detailSampleMaxError = config.ch * 2;
527
528 // this sets the dimensions of the heightfield - should maybe happen before border padding
529 rcCalcGridSize(config.bmin, config.bmax, config.cs, &config.width, &config.height);
530
531 // allocate subregions : tiles
532 Tile* tiles = new Tile[TILES_PER_MAP * TILES_PER_MAP];
533
534 // Initialize per tile config.
535 rcConfig tileCfg = config;
536 tileCfg.width = config.tileSize + config.borderSize*2;
537 tileCfg.height = config.tileSize + config.borderSize*2;
538
539 // merge per tile poly and detail meshes
540 rcPolyMesh** pmmerge = new rcPolyMesh*[TILES_PER_MAP * TILES_PER_MAP];
541 if (!pmmerge)
542 {
543 printf("%s alloc pmmerge FAILED!\n", tileString);
544 return;
545 }
546
547 rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[TILES_PER_MAP * TILES_PER_MAP];
548 if (!dmmerge)
549 {
550 printf("%s alloc dmmerge FAILED!\n", tileString);
551 return;
552 }
553
554 int nmerge = 0;
555
556 // build all tiles
557 for (int y = 0; y < TILES_PER_MAP; ++y)
558 {
559 for (int x = 0; x < TILES_PER_MAP; ++x)
560 {
561 Tile& tile = tiles[x + y * TILES_PER_MAP];
562
563 // Calculate the per tile bounding box.
564 tileCfg.bmin[0] = config.bmin[0] + float(x*config.tileSize - config.borderSize)*config.cs;
565 tileCfg.bmin[2] = config.bmin[2] + float(y*config.tileSize - config.borderSize)*config.cs;
566 tileCfg.bmax[0] = config.bmin[0] + float((x+1)*config.tileSize + config.borderSize)*config.cs;
567 tileCfg.bmax[2] = config.bmin[2] + float((y+1)*config.tileSize + config.borderSize)*config.cs;
568
569 // build heightfield
570 tile.solid = rcAllocHeightfield();
571 if (!tile.solid || !rcCreateHeightfield(m_rcContext, *tile.solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch))
572 {
573 printf("%s Failed building heightfield! \n", tileString);
574 continue;
575 }
576
577 // mark all walkable tiles, both liquids and solids
578 unsigned char* triFlags = new unsigned char[tTriCount];
579 memset(triFlags, NAV_GROUND, tTriCount*sizeof(unsigned char));
580 rcClearUnwalkableTriangles(m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags);
581 rcRasterizeTriangles(m_rcContext, tVerts, tVertCount, tTris, triFlags, tTriCount, *tile.solid, config.walkableClimb);
582 delete[] triFlags;
583
584 rcFilterLowHangingWalkableObstacles(m_rcContext, config.walkableClimb, *tile.solid);
585 rcFilterLedgeSpans(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid);
586 rcFilterWalkableLowHeightSpans(m_rcContext, tileCfg.walkableHeight, *tile.solid);
587
588 rcRasterizeTriangles(m_rcContext, lVerts, lVertCount, lTris, lTriFlags, lTriCount, *tile.solid, config.walkableClimb);
589
590 // compact heightfield spans
591 tile.chf = rcAllocCompactHeightfield();
592 if (!tile.chf || !rcBuildCompactHeightfield(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid, *tile.chf))
593 {
594 printf("%s Failed compacting heightfield! \n", tileString);
595 continue;
596 }
597
598 // build polymesh intermediates
599 if (!rcErodeWalkableArea(m_rcContext, config.walkableRadius, *tile.chf))
600 {
601 printf("%s Failed eroding area! \n", tileString);
602 continue;
603 }
604
605 if (!rcBuildDistanceField(m_rcContext, *tile.chf))
606 {
607 printf("%s Failed building distance field! \n", tileString);
608 continue;
609 }
610
611 if (!rcBuildRegions(m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea))
612 {
613 printf("%s Failed building regions! \n", tileString);
614 continue;
615 }
616
617 tile.cset = rcAllocContourSet();
618 if (!tile.cset || !rcBuildContours(m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset))
619 {
620 printf("%s Failed building contours! \n", tileString);
621 continue;
622 }
623
624 // build polymesh
625 tile.pmesh = rcAllocPolyMesh();
626 if (!tile.pmesh || !rcBuildPolyMesh(m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh))
627 {
628 printf("%s Failed building polymesh! \n", tileString);
629 continue;
630 }
631
632 tile.dmesh = rcAllocPolyMeshDetail();
633 if (!tile.dmesh || !rcBuildPolyMeshDetail(m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *tile.dmesh))
634 {
635 printf("%s Failed building polymesh detail!\n", tileString);
636 continue;
637 }
638
639 // free those up
640 // we may want to keep them in the future for debug
641 // but right now, we don't have the code to merge them
642 rcFreeHeightField(tile.solid);
643 tile.solid = NULL;
644 rcFreeCompactHeightfield(tile.chf);
645 tile.chf = NULL;
646 rcFreeContourSet(tile.cset);
647 tile.cset = NULL;
648
649 if (tile.pmesh)
650 {
651 pmmerge[nmerge] = tile.pmesh;
652 dmmerge[nmerge] = tile.dmesh;
653 nmerge++;
654 }
655 }
656 }
657
658 iv.polyMesh = rcAllocPolyMesh();
659 if (!iv.polyMesh)
660 {
661 printf("%s alloc iv.polyMesh FAILED!\n", tileString);
662 return;
663 }
664 rcMergePolyMeshes(m_rcContext, pmmerge, nmerge, *iv.polyMesh);
665
666 iv.polyMeshDetail = rcAllocPolyMeshDetail();
667 if (!iv.polyMeshDetail)
668 {
669 printf("%s alloc m_dmesh FAILED!\n", tileString);
670 return;
671 }
672 rcMergePolyMeshDetails(m_rcContext, dmmerge, nmerge, *iv.polyMeshDetail);
673
674 // free things up
675 delete[] pmmerge;
676 delete[] dmmerge;
677
678 delete[] tiles;
679
680 // set polygons as walkable
681 // TODO: special flags for DYNAMIC polygons, ie surfaces that can be turned on and off
682 for (int i = 0; i < iv.polyMesh->npolys; ++i)
683 if (iv.polyMesh->areas[i] & RC_WALKABLE_AREA)
684 iv.polyMesh->flags[i] = iv.polyMesh->areas[i];
685
686 // setup mesh parameters
687 dtNavMeshCreateParams params;
688 memset(&params, 0, sizeof(params));
689 params.verts = iv.polyMesh->verts;
690 params.vertCount = iv.polyMesh->nverts;
691 params.polys = iv.polyMesh->polys;
692 params.polyAreas = iv.polyMesh->areas;
693 params.polyFlags = iv.polyMesh->flags;
694 params.polyCount = iv.polyMesh->npolys;
695 params.nvp = iv.polyMesh->nvp;
696 params.detailMeshes = iv.polyMeshDetail->meshes;
697 params.detailVerts = iv.polyMeshDetail->verts;
698 params.detailVertsCount = iv.polyMeshDetail->nverts;
699 params.detailTris = iv.polyMeshDetail->tris;
700 params.detailTriCount = iv.polyMeshDetail->ntris;
701
702 params.offMeshConVerts = meshData.offMeshConnections.getCArray();
703 params.offMeshConCount = meshData.offMeshConnections.size()/6;
704 params.offMeshConRad = meshData.offMeshConnectionRads.getCArray();
705 params.offMeshConDir = meshData.offMeshConnectionDirs.getCArray();
706 params.offMeshConAreas = meshData.offMeshConnectionsAreas.getCArray();
707 params.offMeshConFlags = meshData.offMeshConnectionsFlags.getCArray();
708
709 params.walkableHeight = BASE_UNIT_DIM*config.walkableHeight; // agent height
710 params.walkableRadius = BASE_UNIT_DIM*config.walkableRadius; // agent radius
711 params.walkableClimb = BASE_UNIT_DIM*config.walkableClimb; // keep less that walkableHeight (aka agent height)!
712 params.tileX = (((bmin[0] + bmax[0]) / 2) - navMesh->getParams()->orig[0]) / GRID_SIZE;
713 params.tileY = (((bmin[2] + bmax[2]) / 2) - navMesh->getParams()->orig[2]) / GRID_SIZE;
714 rcVcopy(params.bmin, bmin);
715 rcVcopy(params.bmax, bmax);
716 params.cs = config.cs;
717 params.ch = config.ch;
718 params.tileLayer = 0;
719 params.buildBvTree = true;
720
721 // will hold final navmesh
722 unsigned char* navData = NULL;
723 int navDataSize = 0;
724
725 do
726 {
727 // these values are checked within dtCreateNavMeshData - handle them here
728 // so we have a clear error message
729 if (params.nvp > DT_VERTS_PER_POLYGON)
730 {
731 printf("%s Invalid verts-per-polygon value!\n", tileString);
732 continue;
733 }
734 if (params.vertCount >= 0xffff)
735 {
736 printf("%s Too many vertices!\n", tileString);
737 continue;
738 }
739 if (!params.vertCount || !params.verts)
740 {
741 // occurs mostly when adjacent tiles have models
742 // loaded but those models don't span into this tile
743
744 // message is an annoyance
745 //printf("%sNo vertices to build tile!\n", tileString);
746 continue;
747 }
748 if (!params.polyCount || !params.polys ||
749 TILES_PER_MAP*TILES_PER_MAP == params.polyCount)
750 {
751 // we have flat tiles with no actual geometry - don't build those, its useless
752 // keep in mind that we do output those into debug info
753 // drop tiles with only exact count - some tiles may have geometry while having less tiles
754 printf("%s No polygons to build on tile!\n", tileString);
755 continue;
756 }
757 if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
758 {
759 printf("%s No detail mesh to build tile!\n", tileString);
760 continue;
761 }
762
763 printf("%s Building navmesh tile...\n", tileString);
764 if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
765 {
766 printf("%s Failed building navmesh tile!\n", tileString);
767 continue;
768 }
769
770 dtTileRef tileRef = 0;
771 printf("%s Adding tile to navmesh...\n", tileString);
772 // DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
773 // is removed via removeTile()
774 dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef);
775 if (!tileRef || dtResult != DT_SUCCESS)
776 {
777 printf("%s Failed adding tile to navmesh!\n", tileString);
778 continue;
779 }
780
781 // file output
782 char fileName[255];
783 snprintf(fileName, sizeof(fileName), "mmaps/%04u_%02i_%02i.mmtile", mapID, tileY, tileX);
784 FILE* file = fopen(fileName, "wb");
785 if (!file)
786 {
787 char message[1024];
788 snprintf(message, sizeof(message), "[Map %04u] Failed to open %s for writing!\n", mapID, fileName);
789 perror(message);
790 navMesh->removeTile(tileRef, NULL, NULL);
791 continue;
792 }
793
794 printf("%s Writing to file...\n", tileString);
795
796 // write header
797 MmapTileHeader header;
798 header.usesLiquids = m_terrainBuilder->usesLiquids();
799 header.size = uint32(navDataSize);
800 fwrite(&header, sizeof(MmapTileHeader), 1, file);
801
802 // write data
803 fwrite(navData, sizeof(unsigned char), navDataSize, file);
804 fclose(file);
805
806 // now that tile is written to disk, we can unload it
807 navMesh->removeTile(tileRef, NULL, NULL);
808 }
809 while (0);
810
811 if (m_debugOutput)
812 {
813 // restore padding so that the debug visualization is correct
814 for (int i = 0; i < iv.polyMesh->nverts; ++i)
815 {
816 unsigned short* v = &iv.polyMesh->verts[i*3];
817 v[0] += (unsigned short)config.borderSize;
818 v[2] += (unsigned short)config.borderSize;
819 }
820
821 iv.generateObjFile(mapID, tileX, tileY, meshData);
822 iv.writeIV(mapID, tileX, tileY);
823 }
824 }
825
826 /**************************************************************************/
827 void MapBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax)
828 {
829 // this is for elevation
830 if (verts && vertCount)
831 rcCalcBounds(verts, vertCount, bmin, bmax);
832 else
833 {
834 bmin[1] = FLT_MIN;
835 bmax[1] = FLT_MAX;
836 }
837
838 // this is for width and depth
839 bmax[0] = (32 - int(tileX)) * GRID_SIZE;
840 bmax[2] = (32 - int(tileY)) * GRID_SIZE;
841 bmin[0] = bmax[0] - GRID_SIZE;
842 bmin[2] = bmax[2] - GRID_SIZE;
843 }
844
845 /**************************************************************************/
847 {
849 switch (mapID)
850 {
851 case 0: // Eastern Kingdoms
852 case 1: // Kalimdor
853 case 530: // Outland
854 case 571: // Northrend
855 case 870: // Pandaria
856 return true;
857 default:
858 break;
859 }
860
861 if (m_skipJunkMaps)
862 switch (mapID)
863 {
864 case 13: // test.wdt
865 case 25: // ScottTest.wdt
866 case 29: // Test.wdt
867 case 42: // Colin.wdt
868 case 169: // EmeraldDream.wdt (unused, and very large)
869 case 451: // development.wdt
870 case 573: // ExteriorTest.wdt
871 case 597: // CraigTest.wdt
872 case 605: // development_nonweighted.wdt
873 case 606: // QA_DVD.wdt
874 case 627: // unused.wdt
875 case 651: // ElevatorSpawnTest.wdt
876 case 930: // (UNUSED) Scenario: Alcaz Island
877 case 995: // The Depths [UNUSED]
878 case 1014: // (UNUSED) Peak of Serenity Scenario
879 case 1028: // (UNUSED) Scenario: Mogu Ruins
880 case 1029: // (UNUSED) Scenario: Mogu Crypt
881 case 1049: // (UNUSED) Scenario: Black Ox Temple
882 case 1060: // LevelDesignLand-DevOnly.wdt
883 return true;
884 default:
885 if (isTransportMap(mapID))
886 return true;
887 break;
888 }
889
891 switch (mapID)
892 {
893 case 30: // Alterac Valley
894 case 37: // Azshara Crater
895 case 489: // Warsong Gulch
896 case 529: // Arathi Basin
897 case 566: // Eye of the Storm
898 case 607: // Strand of the Ancients
899 case 628: // Isle of Conquest
900 case 726: // Twin Peaks
901 case 727: // Silvershard Mines
902 case 728: // The Battle for Gilneas (Old Map)
903 case 761: // The Battle for Gilneas
904 case 968: // Rated Eye of the Storm
905 case 998: // Temple of Kotmogu
906 case 1010: // Mists of Pandaria CTF3
907 case 1101: // DefenseOfTheAleHouseBG
908 case 1105: // Deepwind Gorge
909 return true;
910 default:
911 break;
912 }
913
914 return false;
915 }
916
917 /**************************************************************************/
919 {
920 switch (mapID)
921 {
922 // Transport maps
923 case 582: // Transport: Rut'theran to Auberdine
924 case 584: // Transport: Menethil to Theramore
925 case 586: // Transport: Exodar to Auberdine
926 case 587: // Transport: Feathermoon Ferry
927 case 588: // Transport: Menethil to Auberdine
928 case 589: // Transport: Orgrimmar to Grom'Gol
929 case 590: // Transport: Grom'Gol to Undercity
930 case 591: // Transport: Undercity to Orgrimmar
931 case 592: // Transport: Borean Tundra Test
932 case 593: // Transport: Booty Bay to Ratchet
933 case 594: // Transport: Howling Fjord Sister Mercy (Quest)
934 case 596: // Transport: Naglfar
935 case 610: // Transport: Tirisfal to Vengeance Landing
936 case 612: // Transport: Menethil to Valgarde
937 case 613: // Transport: Orgrimmar to Warsong Hold
938 case 614: // Transport: Stormwind to Valiance Keep
939 case 620: // Transport: Moa'ki to Unu'pe
940 case 621: // Transport: Moa'ki to Kamagua
941 case 622: // Transport: Orgrim's Hammer
942 case 623: // Transport: The Skybreaker
943 case 641: // Transport: Alliance Airship BG
944 case 642: // Transport: HordeAirshipBG
945 case 647: // Transport: Orgrimmar to Thunder Bluff
946 case 662: // Transport: Alliance Vashj'ir Ship
947 case 672: // Transport: The Skybreaker (Icecrown Citadel Raid)
948 case 673: // Transport: Orgrim's Hammer (Icecrown Citadel Raid)
949 case 674: // Transport: Ship to Vashj'ir
950 case 712: // Transport: The Skybreaker (IC Dungeon)
951 case 713: // Transport: Orgrim's Hammer (IC Dungeon)
952 case 718: // Transport: The Mighty Wind (Icecrown Citadel Raid)
953 case 738: // Ship to Vashj'ir (Orgrimmar -> Vashj'ir)
954 case 739: // Vashj'ir Sub - Horde
955 case 740: // Vashj'ir Sub - Alliance
956 case 741: // Twilight Highlands Horde Transport
957 case 742: // Vashj'ir Sub - Horde - Circling Abyssal Maw
958 case 743: // Vashj'ir Sub - Alliance circling Abyssal Maw
959 case 746: // Uldum Phase Oasis
960 case 747: // Transport: Deepholm Gunship
961 case 748: // Transport: Onyxia/Nefarian Elevator
962 case 749: // Transport: Gilneas Moving Gunship
963 case 750: // Transport: Gilneas Static Gunship
964 case 762: // Twilight Highlands Zeppelin 1
965 case 763: // Twilight Highlands Zeppelin 2
966 case 765: // Krazzworks Attack Zeppelin
967 case 766: // Transport: Gilneas Moving Gunship 02
968 case 767: // Transport: Gilneas Moving Gunship 03
969 case 1113: // Transport: DarkmoonCarousel
970 case 1132: // Transport218599 - The Skybag (Brawl'gar Arena)
971 case 1133: // Transport218600 - Zandalari Ship (Mogu Island)
972 case 1172: // Transport_Siege_of_Orgrimmar_Alliance - Transport: Siege of Orgrimmar (Alliance)
973 case 1173: // Transport_Siege_of_Orgrimmar_Horde - Transport: Siege of Orgrimmar (Horde)
974 return true;
975 default:
976 return false;
977 }
978 }
979
980 /**************************************************************************/
982 {
983 char fileName[255];
984 snprintf(fileName, sizeof(fileName), "mmaps/%04u_%02i_%02i.mmtile", mapID, tileY, tileX);
985 FILE* file = fopen(fileName, "rb");
986 if (!file)
987 return false;
988
989 MmapTileHeader header;
990 int count = fread(&header, sizeof(MmapTileHeader), 1, file);
991 fclose(file);
992 if (count != 1)
993 return false;
994
995 if (header.mmapMagic != MMAP_MAGIC || header.dtVersion != uint32(DT_NAVMESH_VERSION))
996 return false;
997
998 if (header.mmapVersion != MMAP_VERSION)
999 return false;
1000
1001 return true;
1002 }
1003
1004}
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
DisableType
Definition DisableMgr.h:14
uint32 GetLiquidFlags(uint32)
#define MMAP_VERSION
#define MMAP_MAGIC
@ NAV_GROUND
#define MMAP_VERSION
const char * m_offMeshFilePath
Definition MapBuilder.h:99
void buildMeshFromFile(char *name)
std::set< uint32 > * getTileList(uint32 mapID)
bool isTransportMap(uint32 mapID)
void buildNavMesh(uint32 mapID, dtNavMesh *&navMesh)
rcContext * m_rcContext
Definition MapBuilder.h:108
MapBuilder(float maxWalkableAngle=55.0f, bool skipLiquid=false, bool skipContinents=false, bool skipJunkMaps=true, bool skipBattlegrounds=false, bool debugOutput=false, bool bigBaseUnit=false, const char *offMeshFilePath=NULL)
void buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh *navMesh)
void buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, float bmin[3], float bmax[3], dtNavMesh *navMesh)
void getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY)
bool shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY)
bool shouldSkipMap(uint32 mapID)
float m_maxWalkableAngle
Definition MapBuilder.h:104
void buildMap(uint32 mapID)
TileList m_tiles
Definition MapBuilder.h:95
void getTileBounds(uint32 tileX, uint32 tileY, float *verts, int vertCount, float *bmin, float *bmax)
void buildAllMaps(unsigned int threads)
void buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY)
TerrainBuilder * m_terrainBuilder
Definition MapBuilder.h:94
static void cleanVertices(G3D::Array< float > &verts, G3D::Array< int > &tris)
static uint32 packTileID(uint32 tileX, uint32 tileY)
Definition MapTree.h:51
static void unpackTileID(uint32 ID, uint32 &tileX, uint32 &tileY)
Definition MapTree.h:52
bool IsDisabledFor(DisableType type, uint32 entry, Unit const *unit, uint8 flags)
static const float GRID_SIZE
ListFilesResult getDirContents(std::vector< std::string > &fileList, std::string dirpath=".", std::string filter="*")
Definition PathCommon.h:83
rcPolyMeshDetail * polyMeshDetail
void generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
void writeIV(uint32 mapID, uint32 tileX, uint32 tileY)
G3D::Array< float > liquidVerts
G3D::Array< float > offMeshConnectionRads
G3D::Array< unsigned char > offMeshConnectionDirs
G3D::Array< float > offMeshConnections
G3D::Array< unsigned short > offMeshConnectionsFlags
G3D::Array< float > solidVerts
G3D::Array< int > liquidTris
G3D::Array< int > solidTris
G3D::Array< unsigned char > offMeshConnectionsAreas
G3D::Array< uint8 > liquidType
rcPolyMesh * pmesh
Definition MapBuilder.h:39
rcPolyMeshDetail * dmesh
Definition MapBuilder.h:40
rcHeightfield * solid
Definition MapBuilder.h:37
rcContourSet * cset
Definition MapBuilder.h:38
rcCompactHeightfield * chf
Definition MapBuilder.h:36