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"
23 #include "ModelInstance.h"
25 #include "DetourNavMeshBuilder.h"
26 #include "DetourCommon.h"
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
),
43 m_offMeshFilePath(offMeshFilePath
)
45 m_terrainBuilder
= new TerrainBuilder(skipLiquid
);
47 m_rcContext
= new rcContext(false);
52 /**************************************************************************/
53 MapBuilder::~MapBuilder()
55 for (TileList::iterator it
= m_tiles
.begin(); it
!= m_tiles
.end(); ++it
)
57 (*it
).second
->clear();
61 delete m_terrainBuilder
;
65 /**************************************************************************/
66 void MapBuilder::discoverTiles()
69 uint32 mapID
, tileX
, tileY
, tileID
, count
= 0;
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
>));
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
>));
92 printf("found %u.\n", count
);
95 printf("Discovering tiles... ");
96 for (TileList::iterator itr
= m_tiles
.begin(); itr
!= m_tiles
.end(); ++itr
)
98 set
<uint32
>* tiles
= (*itr
).second
;
101 sprintf(filter
, "%03u*.vmtile", mapID
);
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
);
114 sprintf(filter
, "%03u*", mapID
);
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
)
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
));
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
))
153 /**************************************************************************/
154 void MapBuilder::getGridBounds(uint32 mapID
, uint32
& minX
, uint32
& minY
, uint32
& maxX
, uint32
& maxY
)
161 float bmin
[3], bmax
[3], lmin
[3], lmax
[3];
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
))
169 // get the coord bounds of the model data
170 if (meshData
.solidVerts
.size() + meshData
.liquidVerts
.size() == 0)
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
);
181 else if (meshData
.solidVerts
.size())
182 rcCalcBounds(meshData
.solidVerts
.getCArray(), meshData
.solidVerts
.size() / 3, bmin
, bmax
);
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
);
200 printf("Failed creating navmesh! \n");
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
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
));
232 dtNavMesh
* navMesh
= NULL
;
233 buildNavMesh(mapID
, navMesh
);
236 printf("Failed creating navmesh! \n");
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
)
246 // unpack tile coords
247 StaticMapTree::unpackTileID((*it
), tileX
, tileY
);
249 if (shouldSkipTile(mapID
, tileX
, tileY
))
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
);
267 // get heightmap data
268 m_terrainBuilder
->loadMap(mapID
, tileX
, tileY
, meshData
);
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())
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())
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
)
325 else if (tileX
< tileXMin
)
328 if (tileY
> tileYMax
)
330 else if (tileY
< tileYMin
)
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");
358 sprintf(fileName
, "mmaps/%03u.mmap", mapID
);
360 FILE* file
= fopen(fileName
, "wb");
363 dtFreeNavMesh(navMesh
);
365 sprintf(message
, "Failed to open %s for writing!\n", fileName
);
370 // now that we know navMesh params are valid, we can write them to file
371 fwrite(&navMeshParams
, sizeof(dtNavMeshParams
), 1, file
);
375 /**************************************************************************/
376 void MapBuilder::buildMoveMapTile(uint32 mapID
, uint32 tileX
, uint32 tileY
,
377 MeshData
& meshData
, float bmin
[3], float bmax
[3],
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
;
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.
438 memcpy(&tileCfg
, &config
, sizeof(rcConfig
));
439 tileCfg
.width
= config
.tileSize
+ config
.borderSize
* 2;
440 tileCfg
.height
= config
.tileSize
+ config
.borderSize
* 2;
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];
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
);
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
);
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
);
490 // build polymesh intermediates
491 if (!rcErodeWalkableArea(m_rcContext
, config
.walkableRadius
, *tile
.chf
))
493 printf("%sFailed eroding area! \n", tileString
);
497 if (!rcBuildDistanceField(m_rcContext
, *tile
.chf
))
499 printf("%sFailed building distance field! \n", tileString
);
503 if (!rcBuildRegions(m_rcContext
, *tile
.chf
, tileCfg
.borderSize
, tileCfg
.minRegionArea
, tileCfg
.mergeRegionArea
))
505 printf("%sFailed building regions! \n", tileString
);
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
);
517 tile
.pmesh
= rcAllocPolyMesh();
518 if (!tile
.pmesh
|| !rcBuildPolyMesh(m_rcContext
, *tile
.cset
, tileCfg
.maxVertsPerPoly
, *tile
.pmesh
))
520 printf("%sFailed building polymesh! \n", tileString
);
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
);
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
);
536 rcFreeCompactHeightfield(tile
.chf
);
538 rcFreeContourSet(tile
.cset
);
543 // merge per tile poly and detail meshes
544 rcPolyMesh
** pmmerge
= new rcPolyMesh
*[TILES_PER_MAP
* TILES_PER_MAP
];
547 printf("%s alloc pmmerge FIALED! \r", tileString
);
551 rcPolyMeshDetail
** dmmerge
= new rcPolyMeshDetail
*[TILES_PER_MAP
* TILES_PER_MAP
];
554 printf("%s alloc dmmerge FIALED! \r", tileString
);
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
];
566 pmmerge
[nmerge
] = tile
.pmesh
;
567 dmmerge
[nmerge
] = tile
.dmesh
;
573 iv
.polyMesh
= rcAllocPolyMesh();
576 printf("%s alloc iv.polyMesh FIALED! \r", tileString
);
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
);
587 rcMergePolyMeshDetails(m_rcContext
, dmmerge
, nmerge
, *iv
.polyMeshDetail
);
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(¶ms
, 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
;
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
);
656 if (params
.vertCount
>= 0xffff)
658 printf("%s Too many vertices! \n", tileString
);
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);
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
);
679 if (!params
.detailMeshes
|| !params
.detailVerts
|| !params
.detailTris
)
681 printf("%s No detail mesh to build tile! \n", tileString
);
685 printf("%s Building navmesh tile... \r", tileString
);
686 if (!dtCreateNavMeshData(¶ms
, &navData
, &navDataSize
))
688 printf("%s Failed building navmesh tile! \n", tileString
);
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
);
705 sprintf(fileName
, "mmaps/%03u%02i%02i.mmtile", mapID
, tileY
, tileX
);
706 FILE* file
= fopen(fileName
, "wb");
710 sprintf(message
, "Failed to open %s for writing!\n", fileName
);
712 navMesh
->removeTile(tileRef
, NULL
, NULL
);
716 printf("%s Writing to file... \r", tileString
);
719 MmapTileHeader header
;
720 header
.usesLiquids
= m_terrainBuilder
->usesLiquids();
721 header
.size
= uint32(navDataSize
);
722 fwrite(&header
, sizeof(MmapTileHeader
), 1, file
);
725 fwrite(navData
, sizeof(unsigned char), navDataSize
, file
);
728 // now that tile is written to disk, we can unload it
729 navMesh
->removeTile(tileRef
, NULL
, NULL
);
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
);
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
)
786 case 25: // ScottTest.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
797 if (isTransportMap(mapID
))
802 if (m_skipBattlegrounds
)
820 /**************************************************************************/
821 bool MapBuilder::isTransportMap(uint32 mapID
)
860 /**************************************************************************/
861 bool MapBuilder::shouldSkipTile(uint32 mapID
, uint32 tileX
, uint32 tileY
)
864 sprintf(fileName
, "mmaps/%03u%02i%02i.mmtile", mapID
, tileY
, tileX
);
865 FILE* file
= fopen(fileName
, "rb");
869 MmapTileHeader header
;
870 fread(&header
, sizeof(MmapTileHeader
), 1, file
);
873 if (header
.mmapMagic
!= MMAP_MAGIC
|| header
.dtVersion
!= DT_NAVMESH_VERSION
)
876 if (header
.mmapVersion
!= MMAP_VERSION
)