Updated Copyright year to 2013
[getmangos.git] / contrib / mmap / src / MapBuilder.cpp
blobf7fba8169aa1cd47a991ee6435a40edb90bd0ec6
1 /*
2 * Copyright (C) 2005-2013 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "MMapCommon.h"
20 #include "MapBuilder.h"
22 #include "MapTree.h"
23 #include "ModelInstance.h"
25 #include "DetourNavMeshBuilder.h"
26 #include "DetourCommon.h"
28 using namespace VMAP;
30 namespace MMAP
32 MapBuilder::MapBuilder(float maxWalkableAngle, bool skipLiquid,
33 bool skipContinents, bool skipJunkMaps, bool skipBattlegrounds,
34 bool debugOutput, bool bigBaseUnit, const char* offMeshFilePath) :
35 m_terrainBuilder(NULL),
36 m_debugOutput(debugOutput),
37 m_skipContinents(skipContinents),
38 m_skipJunkMaps(skipJunkMaps),
39 m_skipBattlegrounds(skipBattlegrounds),
40 m_maxWalkableAngle(maxWalkableAngle),
41 m_bigBaseUnit(bigBaseUnit),
42 m_rcContext(NULL),
43 m_offMeshFilePath(offMeshFilePath)
45 m_terrainBuilder = new TerrainBuilder(skipLiquid);
47 m_rcContext = new rcContext(false);
49 discoverTiles();
52 /**************************************************************************/
53 MapBuilder::~MapBuilder()
55 for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
57 (*it).second->clear();
58 delete(*it).second;
61 delete m_terrainBuilder;
62 delete m_rcContext;
65 /**************************************************************************/
66 void MapBuilder::discoverTiles()
68 vector<string> files;
69 uint32 mapID, tileX, tileY, tileID, count = 0;
70 char filter[12];
72 printf("Discovering maps... ");
73 getDirContents(files, "maps");
74 for (uint32 i = 0; i < files.size(); ++i)
76 mapID = uint32(atoi(files[i].substr(0, 3).c_str()));
77 if (m_tiles.find(mapID) == m_tiles.end())
79 m_tiles.insert(pair<uint32, set<uint32>*>(mapID, new set<uint32>));
80 count++;
84 files.clear();
85 getDirContents(files, "vmaps", "*.vmtree");
86 for (uint32 i = 0; i < files.size(); ++i)
88 mapID = uint32(atoi(files[i].substr(0, 3).c_str()));
89 m_tiles.insert(pair<uint32, set<uint32>*>(mapID, new set<uint32>));
90 count++;
92 printf("found %u.\n", count);
94 count = 0;
95 printf("Discovering tiles... ");
96 for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr)
98 set<uint32>* tiles = (*itr).second;
99 mapID = (*itr).first;
101 sprintf(filter, "%03u*.vmtile", mapID);
102 files.clear();
103 getDirContents(files, "vmaps", filter);
104 for (uint32 i = 0; i < files.size(); ++i)
106 tileX = uint32(atoi(files[i].substr(7, 2).c_str()));
107 tileY = uint32(atoi(files[i].substr(4, 2).c_str()));
108 tileID = StaticMapTree::packTileID(tileY, tileX);
110 tiles->insert(tileID);
111 count++;
114 sprintf(filter, "%03u*", mapID);
115 files.clear();
116 getDirContents(files, "maps", filter);
117 for (uint32 i = 0; i < files.size(); ++i)
119 tileY = uint32(atoi(files[i].substr(3, 2).c_str()));
120 tileX = uint32(atoi(files[i].substr(5, 2).c_str()));
121 tileID = StaticMapTree::packTileID(tileX, tileY);
123 if (tiles->insert(tileID).second)
124 count++;
127 printf("found %u.\n\n", count);
130 /**************************************************************************/
131 set<uint32>* MapBuilder::getTileList(uint32 mapID)
133 TileList::iterator itr = m_tiles.find(mapID);
134 if (itr != m_tiles.end())
135 return (*itr).second;
137 set<uint32>* tiles = new set<uint32>();
138 m_tiles.insert(pair<uint32, set<uint32>*>(mapID, tiles));
139 return tiles;
142 /**************************************************************************/
143 void MapBuilder::buildAllMaps()
145 for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
147 uint32 mapID = (*it).first;
148 if (!shouldSkipMap(mapID))
149 buildMap(mapID);
153 /**************************************************************************/
154 void MapBuilder::getGridBounds(uint32 mapID, uint32& minX, uint32& minY, uint32& maxX, uint32& maxY)
156 maxX = INT_MAX;
157 maxY = INT_MAX;
158 minX = INT_MIN;
159 minY = INT_MIN;
161 float bmin[3], bmax[3], lmin[3], lmax[3];
162 MeshData meshData;
164 // make sure we process maps which don't have tiles
165 // initialize the static tree, which loads WDT models
166 if (!m_terrainBuilder->loadVMap(mapID, 64, 64, meshData))
167 return;
169 // get the coord bounds of the model data
170 if (meshData.solidVerts.size() + meshData.liquidVerts.size() == 0)
171 return;
173 // get the coord bounds of the model data
174 if (meshData.solidVerts.size() && meshData.liquidVerts.size())
176 rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
177 rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
178 rcVmin(bmin, lmin);
179 rcVmax(bmax, lmax);
181 else if (meshData.solidVerts.size())
182 rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
183 else
184 rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
186 // convert coord bounds to grid bounds
187 maxX = 32 - bmin[0] / GRID_SIZE;
188 maxY = 32 - bmin[2] / GRID_SIZE;
189 minX = 32 - bmax[0] / GRID_SIZE;
190 minY = 32 - bmax[2] / GRID_SIZE;
193 /**************************************************************************/
194 void MapBuilder::buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY)
196 dtNavMesh* navMesh = NULL;
197 buildNavMesh(mapID, navMesh);
198 if (!navMesh)
200 printf("Failed creating navmesh! \n");
201 return;
204 buildTile(mapID, tileX, tileY, navMesh);
205 dtFreeNavMesh(navMesh);
208 /**************************************************************************/
209 void MapBuilder::buildMap(uint32 mapID)
211 printf("Building map %03u:\n", mapID);
213 set<uint32>* tiles = getTileList(mapID);
215 // make sure we process maps which don't have tiles
216 if (!tiles->size())
218 // convert coord bounds to grid bounds
219 uint32 minX, minY, maxX, maxY;
220 getGridBounds(mapID, minX, minY, maxX, maxY);
222 // add all tiles within bounds to tile list.
223 for (uint32 i = minX; i <= maxX; ++i)
224 for (uint32 j = minY; j <= maxY; ++j)
225 tiles->insert(StaticMapTree::packTileID(i, j));
228 if (!tiles->size())
229 return;
231 // build navMesh
232 dtNavMesh* navMesh = NULL;
233 buildNavMesh(mapID, navMesh);
234 if (!navMesh)
236 printf("Failed creating navmesh! \n");
237 return;
240 // now start building mmtiles for each tile
241 printf("We have %u tiles. \n", (unsigned int)tiles->size());
242 for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
244 uint32 tileX, tileY;
246 // unpack tile coords
247 StaticMapTree::unpackTileID((*it), tileX, tileY);
249 if (shouldSkipTile(mapID, tileX, tileY))
250 continue;
252 buildTile(mapID, tileX, tileY, navMesh);
255 dtFreeNavMesh(navMesh);
257 printf("Complete! \n\n");
260 /**************************************************************************/
261 void MapBuilder::buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh)
263 printf("Building map %03u, tile [%02u,%02u]\n", mapID, tileX, tileY);
265 MeshData meshData;
267 // get heightmap data
268 m_terrainBuilder->loadMap(mapID, tileX, tileY, meshData);
270 // get model data
271 m_terrainBuilder->loadVMap(mapID, tileY, tileX, meshData);
273 // if there is no data, give up now
274 if (!meshData.solidVerts.size() && !meshData.liquidVerts.size())
275 return;
277 // remove unused vertices
278 TerrainBuilder::cleanVertices(meshData.solidVerts, meshData.solidTris);
279 TerrainBuilder::cleanVertices(meshData.liquidVerts, meshData.liquidTris);
281 // gather all mesh data for final data check, and bounds calculation
282 G3D::Array<float> allVerts;
283 allVerts.append(meshData.liquidVerts);
284 allVerts.append(meshData.solidVerts);
286 if (!allVerts.size())
287 return;
289 // get bounds of current tile
290 float bmin[3], bmax[3];
291 getTileBounds(tileX, tileY, allVerts.getCArray(), allVerts.size() / 3, bmin, bmax);
293 m_terrainBuilder->loadOffMeshConnections(mapID, tileX, tileY, meshData, m_offMeshFilePath);
295 // build navmesh tile
296 buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh);
299 /**************************************************************************/
300 void MapBuilder::buildNavMesh(uint32 mapID, dtNavMesh*& navMesh)
302 set<uint32>* tiles = getTileList(mapID);
304 // old code for non-statically assigned bitmask sizes:
305 ///*** calculate number of bits needed to store tiles & polys ***/
306 //int tileBits = dtIlog2(dtNextPow2(tiles->size()));
307 //if (tileBits < 1) tileBits = 1; // need at least one bit!
308 //int polyBits = sizeof(dtPolyRef)*8 - SALT_MIN_BITS - tileBits;
310 int tileBits = STATIC_TILE_BITS;
311 int polyBits = STATIC_POLY_BITS;
313 int maxTiles = tiles->size();
314 int maxPolysPerTile = 1 << polyBits;
316 /*** calculate bounds of map ***/
318 uint32 tileXMin = 64, tileYMin = 64, tileXMax = 0, tileYMax = 0, tileX, tileY;
319 for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
321 StaticMapTree::unpackTileID((*it), tileX, tileY);
323 if (tileX > tileXMax)
324 tileXMax = tileX;
325 else if (tileX < tileXMin)
326 tileXMin = tileX;
328 if (tileY > tileYMax)
329 tileYMax = tileY;
330 else if (tileY < tileYMin)
331 tileYMin = tileY;
334 // use Max because '32 - tileX' is negative for values over 32
335 float bmin[3], bmax[3];
336 getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax);
338 /*** now create the navmesh ***/
340 // navmesh creation params
341 dtNavMeshParams navMeshParams;
342 memset(&navMeshParams, 0, sizeof(dtNavMeshParams));
343 navMeshParams.tileWidth = GRID_SIZE;
344 navMeshParams.tileHeight = GRID_SIZE;
345 rcVcopy(navMeshParams.orig, bmin);
346 navMeshParams.maxTiles = maxTiles;
347 navMeshParams.maxPolys = maxPolysPerTile;
349 navMesh = dtAllocNavMesh();
350 printf("Creating navMesh... \r");
351 if (!navMesh->init(&navMeshParams))
353 printf("Failed creating navmesh! \n");
354 return;
357 char fileName[25];
358 sprintf(fileName, "mmaps/%03u.mmap", mapID);
360 FILE* file = fopen(fileName, "wb");
361 if (!file)
363 dtFreeNavMesh(navMesh);
364 char message[1024];
365 sprintf(message, "Failed to open %s for writing!\n", fileName);
366 perror(message);
367 return;
370 // now that we know navMesh params are valid, we can write them to file
371 fwrite(&navMeshParams, sizeof(dtNavMeshParams), 1, file);
372 fclose(file);
375 /**************************************************************************/
376 void MapBuilder::buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY,
377 MeshData& meshData, float bmin[3], float bmax[3],
378 dtNavMesh* navMesh)
380 // console output
381 char tileString[10];
382 sprintf(tileString, "[%02i,%02i]: ", tileX, tileY);
383 printf("%s Building movemap tiles... \r", tileString);
385 IntermediateValues iv;
387 float* tVerts = meshData.solidVerts.getCArray();
388 int tVertCount = meshData.solidVerts.size() / 3;
389 int* tTris = meshData.solidTris.getCArray();
390 int tTriCount = meshData.solidTris.size() / 3;
392 float* lVerts = meshData.liquidVerts.getCArray();
393 int lVertCount = meshData.liquidVerts.size() / 3;
394 int* lTris = meshData.liquidTris.getCArray();
395 int lTriCount = meshData.liquidTris.size() / 3;
396 uint8* lTriFlags = meshData.liquidType.getCArray();
398 // these are WORLD UNIT based metrics
399 // this are basic unit dimentions
400 // value have to divide GRID_SIZE(533.33333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc )
401 const static float BASE_UNIT_DIM = m_bigBaseUnit ? 0.533333f : 0.266666f;
403 // All are in UNIT metrics!
404 const static int VERTEX_PER_MAP = int(GRID_SIZE / BASE_UNIT_DIM + 0.5f);
405 const static int VERTEX_PER_TILE = m_bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP
406 const static int TILES_PER_MAP = VERTEX_PER_MAP / VERTEX_PER_TILE;
408 rcConfig config;
409 memset(&config, 0, sizeof(rcConfig));
411 rcVcopy(config.bmin, bmin);
412 rcVcopy(config.bmax, bmax);
414 config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
415 config.cs = BASE_UNIT_DIM;
416 config.ch = BASE_UNIT_DIM;
417 config.walkableSlopeAngle = m_maxWalkableAngle;
418 config.tileSize = VERTEX_PER_TILE;
419 config.walkableRadius = m_bigBaseUnit ? 1 : 2;
420 config.borderSize = config.walkableRadius + 3;
421 config.maxEdgeLen = VERTEX_PER_TILE + 1; //anything bigger than tileSize
422 config.walkableHeight = m_bigBaseUnit ? 3 : 6;
423 config.walkableClimb = m_bigBaseUnit ? 2 : 4; // keep less than walkableHeight
424 config.minRegionArea = rcSqr(60);
425 config.mergeRegionArea = rcSqr(50);
426 config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons)
427 config.detailSampleDist = config.cs * 64;
428 config.detailSampleMaxError = config.ch * 2;
430 // this sets the dimensions of the heightfield - should maybe happen before border padding
431 rcCalcGridSize(config.bmin, config.bmax, config.cs, &config.width, &config.height);
433 // allocate subregions : tiles
434 Tile* tiles = new Tile[TILES_PER_MAP * TILES_PER_MAP];
436 // Initialize per tile config.
437 rcConfig tileCfg;
438 memcpy(&tileCfg, &config, sizeof(rcConfig));
439 tileCfg.width = config.tileSize + config.borderSize * 2;
440 tileCfg.height = config.tileSize + config.borderSize * 2;
442 // build all tiles
443 for (int y = 0; y < TILES_PER_MAP; ++y)
445 for (int x = 0; x < TILES_PER_MAP; ++x)
447 Tile& tile = tiles[x + y * TILES_PER_MAP];
449 // Calculate the per tile bounding box.
450 tileCfg.bmin[0] = config.bmin[0] + (x * config.tileSize - config.borderSize) * config.cs;
451 tileCfg.bmin[2] = config.bmin[2] + (y * config.tileSize - config.borderSize) * config.cs;
452 tileCfg.bmax[0] = config.bmin[0] + ((x + 1) * config.tileSize + config.borderSize) * config.cs;
453 tileCfg.bmax[2] = config.bmin[2] + ((y + 1) * config.tileSize + config.borderSize) * config.cs;
455 float tbmin[2], tbmax[2];
456 tbmin[0] = tileCfg.bmin[0];
457 tbmin[1] = tileCfg.bmin[2];
458 tbmax[0] = tileCfg.bmax[0];
459 tbmax[1] = tileCfg.bmax[2];
461 // build heightfield
462 tile.solid = rcAllocHeightfield();
463 if (!tile.solid || !rcCreateHeightfield(m_rcContext, *tile.solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch))
465 printf("%sFailed building heightfield! \n", tileString);
466 continue;
469 // mark all walkable tiles, both liquids and solids
470 unsigned char* triFlags = new unsigned char[tTriCount];
471 memset(triFlags, NAV_GROUND, tTriCount * sizeof(unsigned char));
472 rcClearUnwalkableTriangles(m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags);
473 rcRasterizeTriangles(m_rcContext, tVerts, tVertCount, tTris, triFlags, tTriCount, *tile.solid, config.walkableClimb);
474 delete [] triFlags;
476 rcFilterLowHangingWalkableObstacles(m_rcContext, config.walkableClimb, *tile.solid);
477 rcFilterLedgeSpans(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid);
478 rcFilterWalkableLowHeightSpans(m_rcContext, tileCfg.walkableHeight, *tile.solid);
480 rcRasterizeTriangles(m_rcContext, lVerts, lVertCount, lTris, lTriFlags, lTriCount, *tile.solid, config.walkableClimb);
482 // compact heightfield spans
483 tile.chf = rcAllocCompactHeightfield();
484 if (!tile.chf || !rcBuildCompactHeightfield(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid, *tile.chf))
486 printf("%sFailed compacting heightfield! \n", tileString);
487 continue;
490 // build polymesh intermediates
491 if (!rcErodeWalkableArea(m_rcContext, config.walkableRadius, *tile.chf))
493 printf("%sFailed eroding area! \n", tileString);
494 continue;
497 if (!rcBuildDistanceField(m_rcContext, *tile.chf))
499 printf("%sFailed building distance field! \n", tileString);
500 continue;
503 if (!rcBuildRegions(m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea))
505 printf("%sFailed building regions! \n", tileString);
506 continue;
509 tile.cset = rcAllocContourSet();
510 if (!tile.cset || !rcBuildContours(m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset))
512 printf("%sFailed building contours! \n", tileString);
513 continue;
516 // build polymesh
517 tile.pmesh = rcAllocPolyMesh();
518 if (!tile.pmesh || !rcBuildPolyMesh(m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh))
520 printf("%sFailed building polymesh! \n", tileString);
521 continue;
524 tile.dmesh = rcAllocPolyMeshDetail();
525 if (!tile.dmesh || !rcBuildPolyMeshDetail(m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg .detailSampleMaxError, *tile.dmesh))
527 printf("%sFailed building polymesh detail! \n", tileString);
528 continue;
531 // free those up
532 // we may want to keep them in the future for debug
533 // but right now, we don't have the code to merge them
534 rcFreeHeightField(tile.solid);
535 tile.solid = NULL;
536 rcFreeCompactHeightfield(tile.chf);
537 tile.chf = NULL;
538 rcFreeContourSet(tile.cset);
539 tile.cset = NULL;
543 // merge per tile poly and detail meshes
544 rcPolyMesh** pmmerge = new rcPolyMesh*[TILES_PER_MAP * TILES_PER_MAP];
545 if (!pmmerge)
547 printf("%s alloc pmmerge FIALED! \r", tileString);
548 return;
551 rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[TILES_PER_MAP * TILES_PER_MAP];
552 if (!dmmerge)
554 printf("%s alloc dmmerge FIALED! \r", tileString);
555 return;
558 int nmerge = 0;
559 for (int y = 0; y < TILES_PER_MAP; ++y)
561 for (int x = 0; x < TILES_PER_MAP; ++x)
563 Tile& tile = tiles[x + y * TILES_PER_MAP];
564 if (tile.pmesh)
566 pmmerge[nmerge] = tile.pmesh;
567 dmmerge[nmerge] = tile.dmesh;
568 nmerge++;
573 iv.polyMesh = rcAllocPolyMesh();
574 if (!iv.polyMesh)
576 printf("%s alloc iv.polyMesh FIALED! \r", tileString);
577 return;
579 rcMergePolyMeshes(m_rcContext, pmmerge, nmerge, *iv.polyMesh);
581 iv.polyMeshDetail = rcAllocPolyMeshDetail();
582 if (!iv.polyMeshDetail)
584 printf("%s alloc m_dmesh FIALED! \r", tileString);
585 return;
587 rcMergePolyMeshDetails(m_rcContext, dmmerge, nmerge, *iv.polyMeshDetail);
589 // free things up
590 delete [] pmmerge;
591 delete [] dmmerge;
593 delete [] tiles;
595 // remove padding for extraction
596 for (int i = 0; i < iv.polyMesh->nverts; ++i)
598 unsigned short* v = &iv.polyMesh->verts[i * 3];
599 v[0] -= (unsigned short)config.borderSize;
600 v[2] -= (unsigned short)config.borderSize;
603 // set polygons as walkable
604 // TODO: special flags for DYNAMIC polygons, ie surfaces that can be turned on and off
605 for (int i = 0; i < iv.polyMesh->npolys; ++i)
606 if (iv.polyMesh->areas[i] & RC_WALKABLE_AREA)
607 iv.polyMesh->flags[i] = iv.polyMesh->areas[i];
609 // setup mesh parameters
610 dtNavMeshCreateParams params;
611 memset(&params, 0, sizeof(params));
612 params.verts = iv.polyMesh->verts;
613 params.vertCount = iv.polyMesh->nverts;
614 params.polys = iv.polyMesh->polys;
615 params.polyAreas = iv.polyMesh->areas;
616 params.polyFlags = iv.polyMesh->flags;
617 params.polyCount = iv.polyMesh->npolys;
618 params.nvp = iv.polyMesh->nvp;
619 params.detailMeshes = iv.polyMeshDetail->meshes;
620 params.detailVerts = iv.polyMeshDetail->verts;
621 params.detailVertsCount = iv.polyMeshDetail->nverts;
622 params.detailTris = iv.polyMeshDetail->tris;
623 params.detailTriCount = iv.polyMeshDetail->ntris;
625 params.offMeshConVerts = meshData.offMeshConnections.getCArray();
626 params.offMeshConCount = meshData.offMeshConnections.size() / 6;
627 params.offMeshConRad = meshData.offMeshConnectionRads.getCArray();
628 params.offMeshConDir = meshData.offMeshConnectionDirs.getCArray();
629 params.offMeshConAreas = meshData.offMeshConnectionsAreas.getCArray();
630 params.offMeshConFlags = meshData.offMeshConnectionsFlags.getCArray();
632 params.walkableHeight = BASE_UNIT_DIM * config.walkableHeight; // agent height
633 params.walkableRadius = BASE_UNIT_DIM * config.walkableRadius; // agent radius
634 params.walkableClimb = BASE_UNIT_DIM * config.walkableClimb; // keep less that walkableHeight (aka agent height)!
635 params.tileX = (((bmin[0] + bmax[0]) / 2) - navMesh->getParams()->orig[0]) / GRID_SIZE;
636 params.tileY = (((bmin[2] + bmax[2]) / 2) - navMesh->getParams()->orig[2]) / GRID_SIZE;
637 rcVcopy(params.bmin, bmin);
638 rcVcopy(params.bmax, bmax);
639 params.cs = config.cs;
640 params.ch = config.ch;
641 params.tileSize = VERTEX_PER_MAP;
643 // will hold final navmesh
644 unsigned char* navData = NULL;
645 int navDataSize = 0;
649 // these values are checked within dtCreateNavMeshData - handle them here
650 // so we have a clear error message
651 if (params.nvp > DT_VERTS_PER_POLYGON)
653 printf("%s Invalid verts-per-polygon value! \n", tileString);
654 continue;
656 if (params.vertCount >= 0xffff)
658 printf("%s Too many vertices! \n", tileString);
659 continue;
661 if (!params.vertCount || !params.verts)
663 // occurs mostly when adjacent tiles have models
664 // loaded but those models don't span into this tile
666 // message is an annoyance
667 //printf("%sNo vertices to build tile! \n", tileString);
668 continue;
670 if (!params.polyCount || !params.polys ||
671 TILES_PER_MAP * TILES_PER_MAP == params.polyCount)
673 // we have flat tiles with no actual geometry - don't build those, its useless
674 // keep in mind that we do output those into debug info
675 // drop tiles with only exact count - some tiles may have geometry while having less tiles
676 printf("%s No polygons to build on tile! \n", tileString);
677 continue;
679 if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
681 printf("%s No detail mesh to build tile! \n", tileString);
682 continue;
685 printf("%s Building navmesh tile... \r", tileString);
686 if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
688 printf("%s Failed building navmesh tile! \n", tileString);
689 continue;
692 dtTileRef tileRef = 0;
693 printf("%s Adding tile to navmesh... \r", tileString);
694 // DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
695 // is removed via removeTile()
696 dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef);
697 if (!tileRef || dtResult != DT_SUCCESS)
699 printf("%s Failed adding tile to navmesh! \n", tileString);
700 continue;
703 // file output
704 char fileName[255];
705 sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
706 FILE* file = fopen(fileName, "wb");
707 if (!file)
709 char message[1024];
710 sprintf(message, "Failed to open %s for writing!\n", fileName);
711 perror(message);
712 navMesh->removeTile(tileRef, NULL, NULL);
713 continue;
716 printf("%s Writing to file... \r", tileString);
718 // write header
719 MmapTileHeader header;
720 header.usesLiquids = m_terrainBuilder->usesLiquids();
721 header.size = uint32(navDataSize);
722 fwrite(&header, sizeof(MmapTileHeader), 1, file);
724 // write data
725 fwrite(navData, sizeof(unsigned char), navDataSize, file);
726 fclose(file);
728 // now that tile is written to disk, we can unload it
729 navMesh->removeTile(tileRef, NULL, NULL);
731 while (0);
733 if (m_debugOutput)
735 // restore padding so that the debug visualization is correct
736 for (int i = 0; i < iv.polyMesh->nverts; ++i)
738 unsigned short* v = &iv.polyMesh->verts[i * 3];
739 v[0] += (unsigned short)config.borderSize;
740 v[2] += (unsigned short)config.borderSize;
743 iv.generateObjFile(mapID, tileX, tileY, meshData);
744 iv.writeIV(mapID, tileX, tileY);
748 /**************************************************************************/
749 void MapBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax)
751 // this is for elevation
752 if (verts && vertCount)
753 rcCalcBounds(verts, vertCount, bmin, bmax);
754 else
756 bmin[1] = FLT_MIN;
757 bmax[1] = FLT_MAX;
760 // this is for width and depth
761 bmax[0] = (32 - int(tileX)) * GRID_SIZE;
762 bmax[2] = (32 - int(tileY)) * GRID_SIZE;
763 bmin[0] = bmax[0] - GRID_SIZE;
764 bmin[2] = bmax[2] - GRID_SIZE;
767 /**************************************************************************/
768 bool MapBuilder::shouldSkipMap(uint32 mapID)
770 if (m_skipContinents)
771 switch (mapID)
773 case 0:
774 case 1:
775 case 530:
776 case 571:
777 return true;
778 default:
779 break;
782 if (m_skipJunkMaps)
783 switch (mapID)
785 case 13: // test.wdt
786 case 25: // ScottTest.wdt
787 case 29: // Test.wdt
788 case 42: // Colin.wdt
789 case 169: // EmeraldDream.wdt (unused, and very large)
790 case 451: // development.wdt
791 case 573: // ExteriorTest.wdt
792 case 597: // CraigTest.wdt
793 case 605: // development_nonweighted.wdt
794 case 606: // QA_DVD.wdt
795 return true;
796 default:
797 if (isTransportMap(mapID))
798 return true;
799 break;
802 if (m_skipBattlegrounds)
803 switch (mapID)
805 case 30: // AV
806 case 37: // ?
807 case 489: // WSG
808 case 529: // AB
809 case 566: // EotS
810 case 607: // SotA
811 case 628: // IoC
812 return true;
813 default:
814 break;
817 return false;
820 /**************************************************************************/
821 bool MapBuilder::isTransportMap(uint32 mapID)
823 switch (mapID)
825 // transport maps
826 case 582:
827 case 584:
828 case 586:
829 case 587:
830 case 588:
831 case 589:
832 case 590:
833 case 591:
834 case 592:
835 case 593:
836 case 594:
837 case 596:
838 case 610:
839 case 612:
840 case 613:
841 case 614:
842 case 620:
843 case 621:
844 case 622:
845 case 623:
846 case 641:
847 case 642:
848 case 647:
849 case 672:
850 case 673:
851 case 712:
852 case 713:
853 case 718:
854 return true;
855 default:
856 return false;
860 /**************************************************************************/
861 bool MapBuilder::shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY)
863 char fileName[255];
864 sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
865 FILE* file = fopen(fileName, "rb");
866 if (!file)
867 return false;
869 MmapTileHeader header;
870 fread(&header, sizeof(MmapTileHeader), 1, file);
871 fclose(file);
873 if (header.mmapMagic != MMAP_MAGIC || header.dtVersion != DT_NAVMESH_VERSION)
874 return false;
876 if (header.mmapVersion != MMAP_VERSION)
877 return false;
879 return true;