TrinityCore
Loading...
Searching...
No Matches
TileBuilder.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "TileBuilder.h"
19#include "IntermediateValues.h"
20#include "Log.h"
21#include "MMapDefines.h"
22#include "Memory.h"
23#include "StringFormat.h"
24#include "VMapManager.h"
25#include <DetourNavMeshBuilder.h>
26
27namespace
28{
29 struct Tile
30 {
31 Tile() : chf(nullptr), solid(nullptr), cset(nullptr), pmesh(nullptr), dmesh(nullptr) {}
32 ~Tile()
33 {
34 rcFreeCompactHeightfield(chf);
35 rcFreeContourSet(cset);
36 rcFreeHeightField(solid);
37 rcFreePolyMesh(pmesh);
38 rcFreePolyMeshDetail(dmesh);
39 }
40 rcCompactHeightfield* chf;
41 rcHeightfield* solid;
42 rcContourSet* cset;
43 rcPolyMesh* pmesh;
44 rcPolyMeshDetail* dmesh;
45 };
46}
47
48namespace MMAP
49{
51 {
52 TileConfig(bool bigBaseUnit)
53 {
54 // these are WORLD UNIT based metrics
55 // this are basic unit dimentions
56 // value have to divide GRID_SIZE(533.3333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc )
57 BASE_UNIT_DIM = bigBaseUnit ? 0.5333333f : 0.2666666f;
58
59 // All are in UNIT metrics!
61 VERTEX_PER_TILE = bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP
63 }
64
69 };
70
71 TileBuilder::TileBuilder(boost::filesystem::path const& inputDirectory, boost::filesystem::path const& outputDirectory,
72 Optional<float> maxWalkableAngle, Optional<float> maxWalkableAngleNotSteep,
73 bool skipLiquid, bool bigBaseUnit, bool debugOutput, std::vector<OffMeshData> const* offMeshConnections) :
74 m_outputDirectory(outputDirectory),
75 m_maxWalkableAngle(maxWalkableAngle),
76 m_maxWalkableAngleNotSteep(maxWalkableAngleNotSteep),
77 m_bigBaseUnit(bigBaseUnit),
78 m_debugOutput(debugOutput),
79 m_terrainBuilder(inputDirectory, skipLiquid),
80 m_rcContext(false),
81 m_offMeshConnections(offMeshConnections)
82 {
83 }
84
85 TileBuilder::~TileBuilder() = default;
86
87 /**************************************************************************/
88 void TileBuilder::buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh)
89 {
90 if (shouldSkipTile(mapID, tileX, tileY))
91 {
92 OnTileDone();
93 return;
94 }
95
96 TC_LOG_INFO("maps.mmapgen", "{} [Map {:04}] Building tile [{:02},{:02}]", GetProgressText(), mapID, tileX, tileY);
97
98 MeshData meshData;
99
100 std::unique_ptr<VMAP::VMapManager> vmapManager = CreateVMapManager(mapID);
101
102 // get heightmap data
103 m_terrainBuilder.loadMap(mapID, tileX, tileY, meshData, vmapManager.get());
104
105 // get model data
106 m_terrainBuilder.loadVMap(mapID, tileX, tileY, meshData, vmapManager.get());
107
108 // if there is no data, give up now
109 if (meshData.solidVerts.empty() && meshData.liquidVerts.empty())
110 {
111 OnTileDone();
112 return;
113 }
114
115 // remove unused vertices
118
119 if (meshData.liquidVerts.empty() && meshData.solidVerts.empty())
120 {
121 OnTileDone();
122 return;
123 }
124
125 // gather all mesh data for final data check, and bounds calculation
126 std::vector<float> allVerts(meshData.liquidVerts.size() + meshData.solidVerts.size());
127 std::ranges::copy(meshData.liquidVerts, allVerts.begin());
128 std::ranges::copy(meshData.solidVerts, allVerts.begin() + std::ssize(meshData.liquidVerts));
129
130 // get bounds of current tile
131 float bmin[3], bmax[3];
132 getTileBounds(tileX, tileY, allVerts.data(), allVerts.size() / 3, bmin, bmax);
133
135 m_terrainBuilder.loadOffMeshConnections(mapID, tileX, tileY, meshData, *m_offMeshConnections);
136
137 // build navmesh tile
138 TileResult tileResult = buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh->getParams());
139 if (tileResult.data)
140 saveMoveMapTileToFile(mapID, tileX, tileY, navMesh, tileResult);
141
142 OnTileDone();
143 }
144
145 /**************************************************************************/
147 MeshData& meshData, float (&bmin)[3], float (&bmax)[3],
148 dtNavMeshParams const* navMeshParams, std::string_view fileNameSuffix)
149 {
150 // console output
151 std::string tileString = Trinity::StringFormat("[Map {:04}] [{:02},{:02}]:", mapID, tileX, tileY);
152 TC_LOG_INFO("maps.mmapgen", "{} Building movemap tile...", tileString);
153
154 TileResult tileResult;
155
157
158 float* tVerts = meshData.solidVerts.data();
159 int tVertCount = meshData.solidVerts.size() / 3;
160 int* tTris = meshData.solidTris.data();
161 int tTriCount = meshData.solidTris.size() / 3;
162
163 float* lVerts = meshData.liquidVerts.data();
164 int lVertCount = meshData.liquidVerts.size() / 3;
165 int* lTris = meshData.liquidTris.data();
166 int lTriCount = meshData.liquidTris.size() / 3;
167 uint8* lTriFlags = meshData.liquidType.data();
168
169 const TileConfig tileConfig = TileConfig(m_bigBaseUnit);
170 int TILES_PER_MAP = tileConfig.TILES_PER_MAP;
171 float BASE_UNIT_DIM = tileConfig.BASE_UNIT_DIM;
172 rcConfig config = GetMapSpecificConfig(mapID, bmin, bmax, tileConfig);
173
174 // this sets the dimensions of the heightfield - should maybe happen before border padding
175 rcCalcGridSize(config.bmin, config.bmax, config.cs, &config.width, &config.height);
176
177 // allocate subregions : tiles
178 std::unique_ptr<Tile[]> tiles = std::make_unique<Tile[]>(TILES_PER_MAP * TILES_PER_MAP);
179
180 // Initialize per tile config.
181 rcConfig tileCfg = config;
182 tileCfg.width = config.tileSize + config.borderSize * 2;
183 tileCfg.height = config.tileSize + config.borderSize * 2;
184
185 // merge per tile poly and detail meshes
186 std::unique_ptr<rcPolyMesh*[]> pmmerge = std::make_unique<rcPolyMesh*[]>(TILES_PER_MAP * TILES_PER_MAP);
187 std::unique_ptr<rcPolyMeshDetail*[]> dmmerge = std::make_unique<rcPolyMeshDetail*[]>(TILES_PER_MAP * TILES_PER_MAP);
188 int nmerge = 0;
189 // build all tiles
190 for (int y = 0; y < TILES_PER_MAP; ++y)
191 {
192 for (int x = 0; x < TILES_PER_MAP; ++x)
193 {
194 Tile& tile = tiles[x + y * TILES_PER_MAP];
195
196 // Calculate the per tile bounding box.
197 tileCfg.bmin[0] = config.bmin[0] + x * float(config.tileSize * config.cs);
198 tileCfg.bmin[2] = config.bmin[2] + y * float(config.tileSize * config.cs);
199 tileCfg.bmax[0] = config.bmin[0] + (x + 1) * float(config.tileSize * config.cs);
200 tileCfg.bmax[2] = config.bmin[2] + (y + 1) * float(config.tileSize * config.cs);
201
202 tileCfg.bmin[0] -= tileCfg.borderSize * tileCfg.cs;
203 tileCfg.bmin[2] -= tileCfg.borderSize * tileCfg.cs;
204 tileCfg.bmax[0] += tileCfg.borderSize * tileCfg.cs;
205 tileCfg.bmax[2] += tileCfg.borderSize * tileCfg.cs;
206
207 // build heightfield
208 tile.solid = rcAllocHeightfield();
209 if (!tile.solid || !rcCreateHeightfield(&m_rcContext, *tile.solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch))
210 {
211 TC_LOG_ERROR("maps.mmapgen", "{} Failed building heightfield!", tileString);
212 continue;
213 }
214
215 // mark all walkable tiles, both liquids and solids
216
217 /* we want to have triangles with slope less than walkableSlopeAngleNotSteep (<= 55) to have NAV_AREA_GROUND
218 * and with slope between walkableSlopeAngleNotSteep and walkableSlopeAngle (55 < .. <= 70) to have NAV_AREA_GROUND_STEEP.
219 * we achieve this using recast API: memset everything to NAV_AREA_GROUND_STEEP, call rcClearUnwalkableTriangles with 70 so
220 * any area above that will get RC_NULL_AREA (unwalkable), then call rcMarkWalkableTriangles with 55 to set NAV_AREA_GROUND
221 * on anything below 55 . Players and idle Creatures can use NAV_AREA_GROUND, while Creatures in combat can use NAV_AREA_GROUND_STEEP.
222 */
223 std::unique_ptr<unsigned char[]> triFlags = std::make_unique<unsigned char[]>(tTriCount);
224 memset(triFlags.get(), NAV_AREA_GROUND_STEEP, tTriCount * sizeof(unsigned char));
225 rcClearUnwalkableTriangles(&m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags.get());
226 rcMarkWalkableTriangles(&m_rcContext, tileCfg.walkableSlopeAngleNotSteep, tVerts, tVertCount, tTris, tTriCount, triFlags.get(), NAV_AREA_GROUND);
227 rcRasterizeTriangles(&m_rcContext, tVerts, tVertCount, tTris, triFlags.get(), tTriCount, *tile.solid, config.walkableClimb);
228
229 rcFilterLowHangingWalkableObstacles(&m_rcContext, config.walkableClimb, *tile.solid);
230 rcFilterLedgeSpans(&m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid);
231 rcFilterWalkableLowHeightSpans(&m_rcContext, tileCfg.walkableHeight, *tile.solid);
232
233 // add liquid triangles
234 rcRasterizeTriangles(&m_rcContext, lVerts, lVertCount, lTris, lTriFlags, lTriCount, *tile.solid, config.walkableClimb);
235
236 // compact heightfield spans
237 tile.chf = rcAllocCompactHeightfield();
238 if (!tile.chf || !rcBuildCompactHeightfield(&m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid, *tile.chf))
239 {
240 TC_LOG_ERROR("maps.mmapgen", "{} Failed compacting heightfield!", tileString);
241 continue;
242 }
243
244 // build polymesh intermediates
245 if (!rcErodeWalkableArea(&m_rcContext, config.walkableRadius, *tile.chf))
246 {
247 TC_LOG_ERROR("maps.mmapgen", "{} Failed eroding area!", tileString);
248 continue;
249 }
250
251 if (!rcMedianFilterWalkableArea(&m_rcContext, *tile.chf))
252 {
253 TC_LOG_ERROR("maps.mmapgen", "{} Failed filtering area!", tileString);
254 continue;
255 }
256
257 if (!rcBuildDistanceField(&m_rcContext, *tile.chf))
258 {
259 TC_LOG_ERROR("maps.mmapgen", "{} Failed building distance field!", tileString);
260 continue;
261 }
262
263 if (!rcBuildRegions(&m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea))
264 {
265 TC_LOG_ERROR("maps.mmapgen", "{} Failed building regions!", tileString);
266 continue;
267 }
268
269 tile.cset = rcAllocContourSet();
270 if (!tile.cset || !rcBuildContours(&m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset))
271 {
272 TC_LOG_ERROR("maps.mmapgen", "{} Failed building contours!", tileString);
273 continue;
274 }
275
276 // build polymesh
277 tile.pmesh = rcAllocPolyMesh();
278 if (!tile.pmesh || !rcBuildPolyMesh(&m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh))
279 {
280 TC_LOG_ERROR("maps.mmapgen", "{} Failed building polymesh!", tileString);
281 continue;
282 }
283
284 tile.dmesh = rcAllocPolyMeshDetail();
285 if (!tile.dmesh || !rcBuildPolyMeshDetail(&m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *tile.dmesh))
286 {
287 TC_LOG_ERROR("maps.mmapgen", "{} Failed building polymesh detail!", tileString);
288 continue;
289 }
290
291 // free those up
292 // we may want to keep them in the future for debug
293 // but right now, we don't have the code to merge them
294 rcFreeHeightField(tile.solid);
295 tile.solid = nullptr;
296 rcFreeCompactHeightfield(tile.chf);
297 tile.chf = nullptr;
298 rcFreeContourSet(tile.cset);
299 tile.cset = nullptr;
300
301 pmmerge[nmerge] = tile.pmesh;
302 dmmerge[nmerge] = tile.dmesh;
303 nmerge++;
304 }
305 }
306
307 iv.polyMesh = rcAllocPolyMesh();
308 if (!iv.polyMesh)
309 {
310 TC_LOG_ERROR("maps.mmapgen", "{} alloc iv.polyMesh FAILED!", tileString);
311 return tileResult;
312 }
313 rcMergePolyMeshes(&m_rcContext, pmmerge.get(), nmerge, *iv.polyMesh);
314
315 iv.polyMeshDetail = rcAllocPolyMeshDetail();
316 if (!iv.polyMeshDetail)
317 {
318 TC_LOG_ERROR("maps.mmapgen", "{} alloc m_dmesh FAILED!", tileString);
319 return tileResult;
320 }
321 rcMergePolyMeshDetails(&m_rcContext, dmmerge.get(), nmerge, *iv.polyMeshDetail);
322
323 // free things up
324 pmmerge = nullptr;
325 dmmerge = nullptr;
326 tiles = nullptr;
327
328 // set polygons as walkable
329 // TODO: special flags for DYNAMIC polygons, ie surfaces that can be turned on and off
330 for (int i = 0; i < iv.polyMesh->npolys; ++i)
331 {
332 if (uint8 area = iv.polyMesh->areas[i] & NAV_AREA_ALL_MASK)
333 {
334 if (area >= NAV_AREA_MIN_VALUE)
335 iv.polyMesh->flags[i] = 1 << (NAV_AREA_MAX_VALUE - area);
336 else
337 iv.polyMesh->flags[i] = NAV_GROUND; // TODO: these will be dynamic in future
338 }
339 }
340
341 // setup mesh parameters
342 dtNavMeshCreateParams params = {};
343 params.verts = iv.polyMesh->verts;
344 params.vertCount = iv.polyMesh->nverts;
345 params.polys = iv.polyMesh->polys;
346 params.polyAreas = iv.polyMesh->areas;
347 params.polyFlags = iv.polyMesh->flags;
348 params.polyCount = iv.polyMesh->npolys;
349 params.nvp = iv.polyMesh->nvp;
350 params.detailMeshes = iv.polyMeshDetail->meshes;
351 params.detailVerts = iv.polyMeshDetail->verts;
352 params.detailVertsCount = iv.polyMeshDetail->nverts;
353 params.detailTris = iv.polyMeshDetail->tris;
354 params.detailTriCount = iv.polyMeshDetail->ntris;
355
356 params.offMeshConVerts = meshData.offMeshConnections.data();
357 params.offMeshConCount = meshData.offMeshConnections.size() / 6;
358 params.offMeshConRad = meshData.offMeshConnectionRads.data();
359 params.offMeshConDir = meshData.offMeshConnectionDirs.data();
360 params.offMeshConAreas = meshData.offMeshConnectionsAreas.data();
361 params.offMeshConFlags = meshData.offMeshConnectionsFlags.data();
362
363 params.walkableHeight = BASE_UNIT_DIM * config.walkableHeight; // agent height
364 params.walkableRadius = BASE_UNIT_DIM * config.walkableRadius; // agent radius
365 params.walkableClimb = BASE_UNIT_DIM * config.walkableClimb; // keep less that walkableHeight (aka agent height)!
366 params.tileX = (((bmin[0] + bmax[0]) / 2) - navMeshParams->orig[0]) / GRID_SIZE;
367 params.tileY = (((bmin[2] + bmax[2]) / 2) - navMeshParams->orig[2]) / GRID_SIZE;
368 rcVcopy(params.bmin, bmin);
369 rcVcopy(params.bmax, bmax);
370 params.cs = config.cs;
371 params.ch = config.ch;
372 params.tileLayer = 0;
373 params.buildBvTree = true;
374
375 // will hold final navmesh
376 unsigned char* navData = nullptr;
377
378 auto debugOutputWriter = Trinity::make_unique_ptr_with_deleter(m_debugOutput ? &iv : nullptr,
379 [borderSize = static_cast<unsigned short>(config.borderSize),
380 outputDir = &m_outputDirectory, fileNameSuffix,
381 mapID, tileX, tileY, &meshData](IntermediateValues* intermediate)
382 {
383 // restore padding so that the debug visualization is correct
384 for (std::ptrdiff_t i = 0; i < intermediate->polyMesh->nverts; ++i)
385 {
386 unsigned short* v = &intermediate->polyMesh->verts[i * 3];
387 v[0] += borderSize;
388 v[2] += borderSize;
389 }
390
391 intermediate->generateObjFile(*outputDir, fileNameSuffix, mapID, tileX, tileY, meshData);
392 intermediate->writeIV(*outputDir, fileNameSuffix, mapID, tileX, tileY);
393 });
394
395 // these values are checked within dtCreateNavMeshData - handle them here
396 // so we have a clear error message
397 if (params.nvp > DT_VERTS_PER_POLYGON)
398 {
399 TC_LOG_ERROR("maps.mmapgen", "{} Invalid verts-per-polygon value!", tileString);
400 return tileResult;
401 }
402
403 if (params.vertCount >= 0xffff)
404 {
405 TC_LOG_ERROR("maps.mmapgen", "{} Too many vertices!", tileString);
406 return tileResult;
407 }
408
409 if (!params.vertCount || !params.verts)
410 {
411 // occurs mostly when adjacent tiles have models
412 // loaded but those models don't span into this tile
413
414 // message is an annoyance
415 //TC_LOG_ERROR("maps.mmapgen", "{} No vertices to build tile!", tileString);
416 return tileResult;
417 }
418
419 if (!params.polyCount || !params.polys)
420 {
421 // we have flat tiles with no actual geometry - don't build those, its useless
422 // keep in mind that we do output those into debug info
423 TC_LOG_ERROR("maps.mmapgen", "{} No polygons to build on tile!", tileString);
424 return tileResult;
425 }
426
427 if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
428 {
429 TC_LOG_ERROR("maps.mmapgen", "{} No detail mesh to build tile!", tileString);
430 return tileResult;
431 }
432
433 TC_LOG_DEBUG("maps.mmapgen", "{} Building navmesh tile...", tileString);
434 if (!dtCreateNavMeshData(&params, &navData, &tileResult.size))
435 {
436 TC_LOG_ERROR("maps.mmapgen", "{} Failed building navmesh tile!", tileString);
437 return tileResult;
438 }
439
440 tileResult.data.reset(navData);
441 return tileResult;
442 }
443
444 void TileBuilder::saveMoveMapTileToFile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh,
445 TileResult const& tileResult, std::string_view fileNameSuffix)
446 {
447 dtTileRef tileRef = 0;
448 auto navMeshTile = Trinity::make_unique_ptr_with_deleter<dtTileRef*>(nullptr, [navMesh](dtTileRef const* ref)
449 {
450 navMesh->removeTile(*ref, nullptr, nullptr);
451 });
452
453 if (navMesh)
454 {
455 TC_LOG_DEBUG("maps.mmapgen", "[Map {:04}] [{:02},{:02}]: Adding tile to navmesh...", mapID, tileX, tileY);
456 // DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
457 // is removed via removeTile()
458 dtStatus dtResult = navMesh->addTile(tileResult.data.get(), tileResult.size, 0, 0, &tileRef);
459 if (!tileRef || !dtStatusSucceed(dtResult))
460 {
461 TC_LOG_ERROR("maps.mmapgen", "[Map {:04}] [{:02},{:02}]: Failed adding tile to navmesh!", mapID, tileX, tileY);
462 return;
463 }
464
465 navMeshTile.reset(&tileRef);
466 }
467
468 // file output
469 std::string fileName = Trinity::StringFormat("{}/mmaps/{:04}_{:02}_{:02}{}.mmtile", m_outputDirectory.generic_string(), mapID, tileX, tileY, fileNameSuffix);
470 auto file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fileName.c_str(), "wb"));
471 if (!file)
472 {
473 TC_LOG_ERROR("maps.mmapgen", "[Map {:04}] [{:02},{:02}]: {}: Failed to open {} for writing!", mapID, tileX, tileY, strerror(errno), fileName);
474 return;
475 }
476
477 TC_LOG_DEBUG("maps.mmapgen", "[Map {:04}] [{:02},{:02}]: Writing to file...", mapID, tileX, tileY);
478
479 // write header
480 MmapTileHeader header;
482 header.size = uint32(tileResult.size);
483 fwrite(&header, sizeof(MmapTileHeader), 1, file.get());
484
485 // write data
486 fwrite(tileResult.data.get(), sizeof(unsigned char), tileResult.size, file.get());
487 }
488
489 /**************************************************************************/
490 void TileBuilder::getTileBounds(uint32 tileX, uint32 tileY, float const* verts, std::size_t vertCount, float* bmin, float* bmax)
491 {
492 // this is for elevation
493 if (verts && vertCount)
494 rcCalcBounds(verts, int(vertCount), bmin, bmax);
495 else
496 {
497 bmin[1] = FLT_MIN;
498 bmax[1] = FLT_MAX;
499 }
500
501 // this is for width and depth
502 bmax[0] = (32 - int(tileY)) * GRID_SIZE;
503 bmax[2] = (32 - int(tileX)) * GRID_SIZE;
504 bmin[0] = bmax[0] - GRID_SIZE;
505 bmin[2] = bmax[2] - GRID_SIZE;
506 }
507
508 /**************************************************************************/
509 bool TileBuilder::shouldSkipTile(uint32 /*mapID*/, uint32 /*tileX*/, uint32 /*tileY*/) const
510 {
511 if (m_debugOutput)
512 return false;
513
514 return true;
515 }
516
517 rcConfig TileBuilder::GetMapSpecificConfig(uint32 mapID, float const (&bmin)[3], float const (&bmax)[3], TileConfig const& tileConfig) const
518 {
519 rcConfig config { };
520
521 rcVcopy(config.bmin, bmin);
522 rcVcopy(config.bmax, bmax);
523
524 config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
525 config.cs = tileConfig.BASE_UNIT_DIM;
526 config.ch = tileConfig.BASE_UNIT_DIM;
527 // Keeping these 2 slope angles the same reduces a lot the number of polys.
528 // 55 should be the minimum, maybe 70 is ok (keep in mind blink uses mmaps), 85 is too much for players
529 config.walkableSlopeAngle = m_maxWalkableAngle.value_or(55.0f);
530 config.walkableSlopeAngleNotSteep = m_maxWalkableAngleNotSteep.value_or(55.0f);
531 config.tileSize = tileConfig.VERTEX_PER_TILE;
532 config.walkableRadius = m_bigBaseUnit ? 1 : 2;
533 config.borderSize = config.walkableRadius + 3;
534 config.maxEdgeLen = tileConfig.VERTEX_PER_TILE + 1; // anything bigger than tileSize
535 config.walkableHeight = m_bigBaseUnit ? 3 : 6;
536 // a value >= 3|6 allows npcs to walk over some fences
537 // a value >= 4|8 allows npcs to walk over all fences
538 config.walkableClimb = m_bigBaseUnit ? 3 : 6;
539 config.minRegionArea = rcSqr(60);
540 config.mergeRegionArea = rcSqr(50);
541 config.maxSimplificationError = 1.8f; // eliminates most jagged edges (tiny polygons)
542 config.detailSampleDist = config.cs * 16;
543 config.detailSampleMaxError = config.ch * 1;
544
545 switch (mapID)
546 {
547 // Blade's Edge Arena
548 case 562:
549 // This allows to walk on the ropes to the pillars
550 config.walkableRadius = 0;
551 break;
552 // Blackfathom Deeps
553 case 48:
554 // Reduce the chance to have underground levels
555 config.ch *= 2;
556 break;
557 default:
558 break;
559 }
560
561 return config;
562 }
563
565 {
566 return "";
567 }
568}
uint8_t uint8
Definition Define.h:156
uint32_t uint32
Definition Define.h:154
std::unordered_set< uint32 > params[2]
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
@ NAV_AREA_MIN_VALUE
Definition MMapDefines.h:66
@ NAV_AREA_ALL_MASK
Definition MMapDefines.h:67
@ NAV_AREA_GROUND_STEEP
Definition MMapDefines.h:62
@ NAV_AREA_GROUND
Definition MMapDefines.h:61
@ NAV_AREA_MAX_VALUE
Definition MMapDefines.h:65
@ NAV_GROUND
Definition MMapDefines.h:73
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
void loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, std::vector< OffMeshData > const &offMeshConnections)
void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, VMAP::VMapManager *vmapManager)
bool usesLiquids() const
static void cleanVertices(std::vector< float > &verts, std::vector< int > &tris)
bool loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, VMAP::VMapManager *vmapManager)
std::vector< OffMeshData > const * m_offMeshConnections
Definition TileBuilder.h:96
void saveMoveMapTileToFile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh *navMesh, TileResult const &tileResult, std::string_view fileNameSuffix=""sv)
rcConfig GetMapSpecificConfig(uint32 mapID, float const (&bmin)[3], float const (&bmax)[3], TileConfig const &tileConfig) const
virtual std::string GetProgressText() const
TileResult buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, float(&bmin)[3], float(&bmax)[3], dtNavMeshParams const *navMeshParams, std::string_view fileNameSuffix=""sv)
Optional< float > m_maxWalkableAngleNotSteep
Definition TileBuilder.h:89
boost::filesystem::path m_outputDirectory
Definition TileBuilder.h:87
Optional< float > m_maxWalkableAngle
Definition TileBuilder.h:88
TerrainBuilder m_terrainBuilder
Definition TileBuilder.h:93
virtual bool shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY) const
virtual ~TileBuilder()
virtual void OnTileDone()
Definition TileBuilder.h:84
void buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh *navMesh)
static void getTileBounds(uint32 tileX, uint32 tileY, float const *verts, std::size_t vertCount, float *bmin, float *bmax)
rcContext m_rcContext
Definition TileBuilder.h:95
TileBuilder(boost::filesystem::path const &inputDirectory, boost::filesystem::path const &outputDirectory, Optional< float > maxWalkableAngle, Optional< float > maxWalkableAngleNotSteep, bool skipLiquid, bool bigBaseUnit, bool debugOutput, std::vector< OffMeshData > const *offMeshConnections)
static const float GRID_SIZE
std::unique_ptr< VMAP::VMapManager >(* CreateVMapManager)(uint32 mapId)
std::unique_ptr< T, Impl::stateful_unique_ptr_deleter< Ptr, Del > > make_unique_ptr_with_deleter(Ptr ptr, Del deleter)
Definition Memory.h:133
std::string StringFormat(FormatString< Args... > fmt, Args &&... args) noexcept
Default TC string format function.
rcPolyMeshDetail * polyMeshDetail
void generateObjFile(boost::filesystem::path const &outputDirectory, std::string_view fileNameSuffix, uint32 mapID, uint32 tileX, uint32 tileY, MeshData const &meshData)
void writeIV(boost::filesystem::path const &outputDirectory, std::string_view fileNameSuffix, uint32 mapID, uint32 tileX, uint32 tileY)
std::vector< uint8 > liquidType
std::vector< float > liquidVerts
std::vector< unsigned short > offMeshConnectionsFlags
std::vector< float > solidVerts
std::vector< int > solidTris
std::vector< int > liquidTris
std::vector< float > offMeshConnections
std::vector< unsigned char > offMeshConnectionDirs
std::vector< float > offMeshConnectionRads
std::vector< unsigned char > offMeshConnectionsAreas
TileConfig(bool bigBaseUnit)