TrinityCore
TerrainBuilder.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 "TerrainBuilder.h"
19#include "MapBuilder.h"
20#include "MapDefines.h"
21#include "MapTree.h"
22#include "MMapDefines.h"
23#include "ModelInstance.h"
24#include "StringFormat.h"
25#include "Util.h"
26#include "VMapManager2.h"
27#include <map>
28
29namespace MMAP
30{
31 TerrainBuilder::TerrainBuilder(bool skipLiquid) : m_skipLiquid (skipLiquid){ }
33
34 /**************************************************************************/
35 void TerrainBuilder::getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc)
36 {
37 switch (portion)
38 {
39 case ENTIRE:
40 loopStart = 0;
41 loopEnd = V8_SIZE_SQ;
42 loopInc = 1;
43 break;
44 case TOP:
45 loopStart = 0;
46 loopEnd = V8_SIZE;
47 loopInc = 1;
48 break;
49 case LEFT:
50 loopStart = 0;
51 loopEnd = V8_SIZE_SQ - V8_SIZE + 1;
52 loopInc = V8_SIZE;
53 break;
54 case RIGHT:
55 loopStart = V8_SIZE - 1;
56 loopEnd = V8_SIZE_SQ;
57 loopInc = V8_SIZE;
58 break;
59 case BOTTOM:
60 loopStart = V8_SIZE_SQ - V8_SIZE;
61 loopEnd = V8_SIZE_SQ;
62 loopInc = 1;
63 break;
64 }
65 }
66
67 /**************************************************************************/
68 void TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
69 {
70 if (loadMap(mapID, tileX, tileY, meshData, ENTIRE))
71 {
72 loadMap(mapID, tileX+1, tileY, meshData, LEFT);
73 loadMap(mapID, tileX-1, tileY, meshData, RIGHT);
74 loadMap(mapID, tileX, tileY+1, meshData, TOP);
75 loadMap(mapID, tileX, tileY-1, meshData, BOTTOM);
76 }
77 }
78
79 /**************************************************************************/
80 bool TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion)
81 {
82 std::string mapFileName = Trinity::StringFormat("maps/{:04}_{:02}_{:02}.map", mapID, tileY, tileX);
83
84 FILE* mapFile = fopen(mapFileName.c_str(), "rb");
85 if (!mapFile)
86 {
87 int32 parentMapId = sMapStore[mapID].ParentMapID;
88 while (!mapFile && parentMapId != -1)
89 {
90 mapFileName = Trinity::StringFormat("maps/{:04}_{:02}_{:02}.map", parentMapId, tileY, tileX);
91 mapFile = fopen(mapFileName.c_str(), "rb");
92 parentMapId = sMapStore[parentMapId].ParentMapID;
93 }
94 }
95
96 if (!mapFile)
97 return false;
98
99 map_fileheader fheader;
100 if (fread(&fheader, sizeof(map_fileheader), 1, mapFile) != 1 ||
101 fheader.versionMagic != MapVersionMagic)
102 {
103 fclose(mapFile);
104 printf("%s is the wrong version, please extract new .map files\n", mapFileName.c_str());
105 return false;
106 }
107
108 map_heightHeader hheader;
109 fseek(mapFile, fheader.heightMapOffset, SEEK_SET);
110
111 bool haveTerrain = false;
112 bool haveLiquid = false;
113 if (fread(&hheader, sizeof(map_heightHeader), 1, mapFile) == 1)
114 {
115 haveTerrain = !hheader.flags.HasFlag(map_heightHeaderFlags::NoHeight);
116 haveLiquid = fheader.liquidMapOffset && !m_skipLiquid;
117 }
118
119 // no data in this map file
120 if (!haveTerrain && !haveLiquid)
121 {
122 fclose(mapFile);
123 return false;
124 }
125
126 // data used later
127 uint8 holes[16][16][8];
128 memset(holes, 0, sizeof(holes));
129 uint16 liquid_entry[16][16];
130 memset(liquid_entry, 0, sizeof(liquid_entry));
132 memset(liquid_flags, 0, sizeof(liquid_flags));
133 G3D::Array<int> ltriangles;
134 G3D::Array<int> ttriangles;
135
136 // terrain data
137 if (haveTerrain)
138 {
139 float heightMultiplier;
140 float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ];
141 int expected = V9_SIZE_SQ + V8_SIZE_SQ;
142
144 {
145 uint8 v9[V9_SIZE_SQ];
146 uint8 v8[V8_SIZE_SQ];
147 int count = 0;
148 count += fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile);
149 count += fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile);
150 if (count != expected)
151 printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
152
153 heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255;
154
155 for (int i = 0; i < V9_SIZE_SQ; ++i)
156 V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
157
158 for (int i = 0; i < V8_SIZE_SQ; ++i)
159 V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
160 }
162 {
163 uint16 v9[V9_SIZE_SQ];
164 uint16 v8[V8_SIZE_SQ];
165 int count = 0;
166 count += fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile);
167 count += fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile);
168 if (count != expected)
169 printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
170
171 heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535;
172
173 for (int i = 0; i < V9_SIZE_SQ; ++i)
174 V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
175
176 for (int i = 0; i < V8_SIZE_SQ; ++i)
177 V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
178 }
179 else
180 {
181 int count = 0;
182 count += fread(V9, sizeof(float), V9_SIZE_SQ, mapFile);
183 count += fread(V8, sizeof(float), V8_SIZE_SQ, mapFile);
184 if (count != expected)
185 printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
186 }
187
188 // hole data
189 if (fheader.holesSize != 0)
190 {
191 memset(holes, 0, fheader.holesSize);
192 fseek(mapFile, fheader.holesOffset, SEEK_SET);
193 if (fread(holes, fheader.holesSize, 1, mapFile) != 1)
194 printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
195 }
196
197 int count = meshData.solidVerts.size() / 3;
198 float xoffset = (float(tileX)-32)*GRID_SIZE;
199 float yoffset = (float(tileY)-32)*GRID_SIZE;
200
201 float coord[3];
202
203 for (int i = 0; i < V9_SIZE_SQ; ++i)
204 {
205 getHeightCoord(i, GRID_V9, xoffset, yoffset, coord, V9);
206 meshData.solidVerts.append(coord[0]);
207 meshData.solidVerts.append(coord[2]);
208 meshData.solidVerts.append(coord[1]);
209 }
210
211 for (int i = 0; i < V8_SIZE_SQ; ++i)
212 {
213 getHeightCoord(i, GRID_V8, xoffset, yoffset, coord, V8);
214 meshData.solidVerts.append(coord[0]);
215 meshData.solidVerts.append(coord[2]);
216 meshData.solidVerts.append(coord[1]);
217 }
218
219 int indices[] = { 0, 0, 0 };
220 int loopStart = 0, loopEnd = 0, loopInc = 0;
221 getLoopVars(portion, loopStart, loopEnd, loopInc);
222 for (int i = loopStart; i < loopEnd; i+=loopInc)
223 for (int j = TOP; j <= BOTTOM; j+=1)
224 {
225 getHeightTriangle(i, Spot(j), indices);
226 ttriangles.append(indices[2] + count);
227 ttriangles.append(indices[1] + count);
228 ttriangles.append(indices[0] + count);
229 }
230 }
231
232 // liquid data
233 if (haveLiquid)
234 {
235 map_liquidHeader lheader;
236 fseek(mapFile, fheader.liquidMapOffset, SEEK_SET);
237 if (fread(&lheader, sizeof(map_liquidHeader), 1, mapFile) != 1)
238 printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
239
240 float* liquid_map = nullptr;
241
243 {
244 if (fread(liquid_entry, sizeof(liquid_entry), 1, mapFile) != 1)
245 printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
246 if (fread(liquid_flags, sizeof(liquid_flags), 1, mapFile) != 1)
247 printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
248 }
249 else
250 {
251 std::fill_n(&liquid_entry[0][0], 16 * 16, lheader.liquidType);
252 std::fill_n(&liquid_flags[0][0], 16 * 16, lheader.liquidFlags);
253 }
254
256 {
257 uint32 toRead = lheader.width * lheader.height;
258 liquid_map = new float [toRead];
259 if (fread(liquid_map, sizeof(float), toRead, mapFile) != toRead)
260 {
261 printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
262 delete[] liquid_map;
263 liquid_map = nullptr;
264 }
265 }
266
267 int count = meshData.liquidVerts.size() / 3;
268 float xoffset = (float(tileX)-32)*GRID_SIZE;
269 float yoffset = (float(tileY)-32)*GRID_SIZE;
270
271 float coord[3];
272 int row, col;
273
274 // generate coordinates
276 {
277 int j = 0;
278 for (int i = 0; i < V9_SIZE_SQ; ++i)
279 {
280 row = i / V9_SIZE;
281 col = i % V9_SIZE;
282
283 if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height ||
284 col < lheader.offsetX || col >= lheader.offsetX + lheader.width)
285 {
286 // dummy vert using invalid height
287 meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, INVALID_MAP_LIQ_HEIGHT, (yoffset+row*GRID_PART_SIZE)*-1);
288 continue;
289 }
290
291 getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map);
292 meshData.liquidVerts.append(coord[0]);
293 meshData.liquidVerts.append(coord[2]);
294 meshData.liquidVerts.append(coord[1]);
295 j++;
296 }
297 }
298 else
299 {
300 for (int i = 0; i < V9_SIZE_SQ; ++i)
301 {
302 row = i / V9_SIZE;
303 col = i % V9_SIZE;
304 meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, lheader.liquidLevel, (yoffset+row*GRID_PART_SIZE)*-1);
305 }
306 }
307
308 delete[] liquid_map;
309
310 int indices[] = { 0, 0, 0 };
311 int loopStart = 0, loopEnd = 0, loopInc = 0, triInc = BOTTOM-TOP;
312 getLoopVars(portion, loopStart, loopEnd, loopInc);
313
314 // generate triangles
315 for (int i = loopStart; i < loopEnd; i += loopInc)
316 {
317 for (int j = TOP; j <= BOTTOM; j += triInc)
318 {
319 getHeightTriangle(i, Spot(j), indices, true);
320 ltriangles.append(indices[2] + count);
321 ltriangles.append(indices[1] + count);
322 ltriangles.append(indices[0] + count);
323 }
324 }
325 }
326
327 fclose(mapFile);
328
329 // now that we have gathered the data, we can figure out which parts to keep:
330 // liquid above ground, ground above liquid
331 int loopStart = 0, loopEnd = 0, loopInc = 0, tTriCount = 4;
332 bool useTerrain, useLiquid;
333
334 float* lverts = meshData.liquidVerts.getCArray();
335 int* ltris = ltriangles.getCArray();
336
337 float* tverts = meshData.solidVerts.getCArray();
338 int* ttris = ttriangles.getCArray();
339
340 if ((ltriangles.size() + ttriangles.size()) == 0)
341 return false;
342
343 // make a copy of liquid vertices
344 // used to pad right-bottom frame due to lost vertex data at extraction
345 float* lverts_copy = nullptr;
346 if (meshData.liquidVerts.size())
347 {
348 lverts_copy = new float[meshData.liquidVerts.size()];
349 memcpy(lverts_copy, lverts, sizeof(float)*meshData.liquidVerts.size());
350 }
351
352 getLoopVars(portion, loopStart, loopEnd, loopInc);
353 for (int i = loopStart; i < loopEnd; i+=loopInc)
354 {
355 for (int j = 0; j < 2; ++j)
356 {
357 // default is true, will change to false if needed
358 useTerrain = true;
359 useLiquid = true;
361 uint8 navLiquidType = NAV_AREA_EMPTY;
362
363 // if there is no liquid, don't use liquid
364 if (!meshData.liquidVerts.size() || !ltriangles.size())
365 useLiquid = false;
366 else
367 {
368 liquidType = getLiquidType(i, liquid_flags);
370 {
371 // players should not be here, so logically neither should creatures
372 useTerrain = false;
373 useLiquid = false;
374 }
376 navLiquidType = NAV_AREA_WATER;
378 navLiquidType = NAV_AREA_MAGMA_SLIME;
379 else
380 useLiquid = false;
381 }
382
383 // if there is no terrain, don't use terrain
384 if (!ttriangles.size())
385 useTerrain = false;
386
387 // while extracting ADT data we are losing right-bottom vertices
388 // this code adds fair approximation of lost data
389 if (useLiquid)
390 {
391 float quadHeight = 0;
392 uint32 validCount = 0;
393 for(uint32 idx = 0; idx < 3; idx++)
394 {
395 float h = lverts_copy[ltris[idx]*3 + 1];
397 {
398 quadHeight += h;
399 validCount++;
400 }
401 }
402
403 // update vertex height data
404 if (validCount > 0 && validCount < 3)
405 {
406 quadHeight /= validCount;
407 for(uint32 idx = 0; idx < 3; idx++)
408 {
409 float h = lverts[ltris[idx]*3 + 1];
411 lverts[ltris[idx]*3 + 1] = quadHeight;
412 }
413 }
414
415 // no valid vertexes - don't use this poly at all
416 if (validCount == 0)
417 useLiquid = false;
418 }
419
420 // if there is a hole here, don't use the terrain
421 if (useTerrain && fheader.holesSize != 0)
422 useTerrain = !isHole(i, holes);
423
424 // we use only one terrain kind per quad - pick higher one
425 if (useTerrain && useLiquid)
426 {
427 float minLLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
428 float maxLLevel = INVALID_MAP_LIQ_HEIGHT;
429 for(uint32 x = 0; x < 3; x++)
430 {
431 float h = lverts[ltris[x]*3 + 1];
432 if (minLLevel > h)
433 minLLevel = h;
434
435 if (maxLLevel < h)
436 maxLLevel = h;
437 }
438
439 float maxTLevel = INVALID_MAP_LIQ_HEIGHT;
440 float minTLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
441 for(uint32 x = 0; x < 6; x++)
442 {
443 float h = tverts[ttris[x]*3 + 1];
444 if (maxTLevel < h)
445 maxTLevel = h;
446
447 if (minTLevel > h)
448 minTLevel = h;
449 }
450
451 // terrain under the liquid?
452 if (minLLevel > maxTLevel)
453 useTerrain = false;
454
455 //liquid under the terrain?
456 if (minTLevel > maxLLevel)
457 useLiquid = false;
458 }
459
460 // store the result
461 if (useLiquid)
462 {
463 meshData.liquidType.append(navLiquidType);
464 for (int k = 0; k < 3; ++k)
465 meshData.liquidTris.append(ltris[k]);
466 }
467
468 if (useTerrain)
469 for (int k = 0; k < 3*tTriCount/2; ++k)
470 meshData.solidTris.append(ttris[k]);
471
472 // advance to next set of triangles
473 ltris += 3;
474 ttris += 3*tTriCount/2;
475 }
476 }
477
478 if (lverts_copy)
479 delete [] lverts_copy;
480
481 return meshData.solidTris.size() || meshData.liquidTris.size();
482 }
483
484 /**************************************************************************/
485 void TerrainBuilder::getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v)
486 {
487 // wow coords: x, y, height
488 // coord is mirroed about the horizontal axes
489 switch (grid)
490 {
491 case GRID_V9:
492 coord[0] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f;
493 coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f;
494 coord[2] = v[index];
495 break;
496 case GRID_V8:
497 coord[0] = (xOffset + index%(V8_SIZE)*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f;
498 coord[1] = (yOffset + (int)(index/(V8_SIZE))*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f;
499 coord[2] = v[index];
500 break;
501 }
502 }
503
504 /**************************************************************************/
505 void TerrainBuilder::getHeightTriangle(int square, Spot triangle, int* indices, bool liquid/* = false*/)
506 {
507 int rowOffset = square/V8_SIZE;
508 if (!liquid)
509 switch (triangle)
510 {
511 case TOP:
512 indices[0] = square+rowOffset; // 0-----1 .... 128
513 indices[1] = square+1+rowOffset; // |\ T /|
514 indices[2] = (V9_SIZE_SQ)+square; // | \ / |
515 break; // |L 0 R| .. 127
516 case LEFT: // | / \ |
517 indices[0] = square+rowOffset; // |/ B \|
518 indices[1] = (V9_SIZE_SQ)+square; // 129---130 ... 386
519 indices[2] = square+V9_SIZE+rowOffset; // |\ /|
520 break; // | \ / |
521 case RIGHT: // | 128 | .. 255
522 indices[0] = square+1+rowOffset; // | / \ |
523 indices[1] = square+V9_SIZE+1+rowOffset; // |/ \|
524 indices[2] = (V9_SIZE_SQ)+square; // 258---259 ... 515
525 break;
526 case BOTTOM:
527 indices[0] = (V9_SIZE_SQ)+square;
528 indices[1] = square+V9_SIZE+1+rowOffset;
529 indices[2] = square+V9_SIZE+rowOffset;
530 break;
531 default: break;
532 }
533 else
534 switch (triangle)
535 { // 0-----1 .... 128
536 case TOP: // |\ |
537 indices[0] = square+rowOffset; // | \ T |
538 indices[1] = square+1+rowOffset; // | \ |
539 indices[2] = square+V9_SIZE+1+rowOffset; // | B \ |
540 break; // | \|
541 case BOTTOM: // 129---130 ... 386
542 indices[0] = square+rowOffset; // |\ |
543 indices[1] = square+V9_SIZE+1+rowOffset; // | \ |
544 indices[2] = square+V9_SIZE+rowOffset; // | \ |
545 break; // | \ |
546 default: break; // | \|
547 } // 258---259 ... 515
548
549 }
550
551 /**************************************************************************/
552 void TerrainBuilder::getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v)
553 {
554 // wow coords: x, y, height
555 // coord is mirroed about the horizontal axes
556 coord[0] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f;
557 coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f;
558 coord[2] = v[index2];
559 }
560
561 /**************************************************************************/
562 bool TerrainBuilder::isHole(int square, uint8 const holes[16][16][8])
563 {
564 int row = square / 128;
565 int col = square % 128;
566 int cellRow = row / 8; // 8 squares per cell
567 int cellCol = col / 8;
568 int holeRow = row % 8;
569 int holeCol = col % 8;
570
571 return (holes[cellRow][cellCol][holeRow] & (1 << holeCol)) != 0;
572 }
573
574 /**************************************************************************/
576 {
577 int row = square / 128;
578 int col = square % 128;
579 int cellRow = row / 8; // 8 squares per cell
580 int cellCol = col / 8;
581
582 return liquid_type[cellRow][cellCol];
583 }
584
585 /**************************************************************************/
586 bool TerrainBuilder::loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
587 {
588 std::unique_ptr<VMapManager2> vmapManager = VMapFactory::CreateVMapManager();
589 LoadResult result = vmapManager->loadMap("vmaps", mapID, tileX, tileY);
590 bool retval = false;
591
592 do
593 {
594 if (result != LoadResult::Success)
595 break;
596
597 InstanceTreeMap instanceTrees;
598 vmapManager->getInstanceMapTree(instanceTrees);
599
600 if (!instanceTrees[mapID])
601 break;
602
603 ModelInstance* models = nullptr;
604 uint32 count = 0;
605 instanceTrees[mapID]->getModelInstances(models, count);
606
607 if (!models)
608 break;
609
610 for (uint32 i = 0; i < count; ++i)
611 {
612 ModelInstance const& instance = models[i];
613
614 // model instances exist in tree even though there are instances of that model in this tile
615 WorldModel const* worldModel = instance.getWorldModel();
616 if (!worldModel)
617 continue;
618
619 // now we have a model to add to the meshdata
620 retval = true;
621
622 std::vector<GroupModel> const& groupModels = worldModel->getGroupModels();
623
624 // all M2s need to have triangle indices reversed
625 bool isM2 = worldModel->IsM2();
626
627 // transform data
628 float scale = instance.iScale;
629 G3D::Matrix3 rotation = instance.GetInvRot();
630 G3D::Vector3 position = instance.iPos;
631 position.x -= 32 * GRID_SIZE;
632 position.y -= 32 * GRID_SIZE;
633
634 for (std::vector<GroupModel>::const_iterator it = groupModels.begin(); it != groupModels.end(); ++it)
635 {
636 std::vector<G3D::Vector3> const& tempVertices = it->GetVertices();
637 std::vector<G3D::Vector3> transformedVertices;
638 std::vector<MeshTriangle> const& tempTriangles = it->GetTriangles();
639 WmoLiquid const* liquid = it->GetLiquid();
640
641 // first handle collision mesh
642 transform(tempVertices, transformedVertices, scale, rotation, position);
643
644 int offset = meshData.solidVerts.size() / 3;
645
646 copyVertices(transformedVertices, meshData.solidVerts);
647 copyIndices(tempTriangles, meshData.solidTris, offset, isM2);
648
649 // now handle liquid data
650 if (liquid && liquid->GetFlagsStorage())
651 {
652 std::vector<G3D::Vector3> liqVerts;
653 std::vector<int> liqTris;
654 uint32 tilesX, tilesY, vertsX, vertsY;
655 G3D::Vector3 corner;
656 liquid->getPosInfo(tilesX, tilesY, corner);
657 vertsX = tilesX + 1;
658 vertsY = tilesY + 1;
659 uint8 const* flags = liquid->GetFlagsStorage();
660 float const* data = liquid->GetHeightStorage();
661 uint8 type = NAV_AREA_EMPTY;
662
663 // convert liquid type to NavTerrain
664 EnumFlag<map_liquidHeaderTypeFlags> liquidFlags = map_liquidHeaderTypeFlags(vmapManager->GetLiquidFlagsPtr(liquid->GetType()));
666 type = NAV_AREA_WATER;
669
670 // indexing is weird...
671 // after a lot of trial and error, this is what works:
672 // vertex = y*vertsX+x
673 // tile = x*tilesY+y
674 // flag = y*tilesY+x
675
676 G3D::Vector3 vert;
677 for (uint32 x = 0; x < vertsX; ++x)
678 {
679 for (uint32 y = 0; y < vertsY; ++y)
680 {
681 vert = G3D::Vector3(corner.x + x * GRID_PART_SIZE, corner.y + y * GRID_PART_SIZE, data[y * vertsX + x]);
682 vert = vert * rotation * scale + position;
683 vert.x *= -1.f;
684 vert.y *= -1.f;
685 liqVerts.push_back(vert);
686 }
687 }
688
689 int idx1, idx2, idx3, idx4;
691 for (uint32 x = 0; x < tilesX; ++x)
692 {
693 for (uint32 y = 0; y < tilesY; ++y)
694 {
695 if ((flags[x + y * tilesX] & 0x0f) != 0x0f)
696 {
697 square = x * tilesY + y;
698 idx1 = square + x;
699 idx2 = square + 1 + x;
700 idx3 = square + tilesY + 1 + 1 + x;
701 idx4 = square + tilesY + 1 + x;
702
703 // top triangle
704 liqTris.push_back(idx3);
705 liqTris.push_back(idx2);
706 liqTris.push_back(idx1);
707 // bottom triangle
708 liqTris.push_back(idx4);
709 liqTris.push_back(idx3);
710 liqTris.push_back(idx1);
711 }
712 }
713 }
714
715 uint32 liqOffset = meshData.liquidVerts.size() / 3;
716 for (uint32 i = 0; i < liqVerts.size(); ++i)
717 meshData.liquidVerts.append(liqVerts[i].y, liqVerts[i].z, liqVerts[i].x);
718
719 for (uint32 i = 0; i < liqTris.size() / 3; ++i)
720 {
721 meshData.liquidTris.append(liqTris[i * 3 + 1] + liqOffset, liqTris[i * 3 + 2] + liqOffset, liqTris[i * 3] + liqOffset);
722 meshData.liquidType.append(type);
723 }
724 }
725 }
726 }
727 }
728 while (false);
729
730 vmapManager->unloadMap(mapID, tileX, tileY);
731
732 return retval;
733 }
734
735 /**************************************************************************/
736 void TerrainBuilder::transform(std::vector<G3D::Vector3> const& source, std::vector<G3D::Vector3>& transformedVertices, float scale, G3D::Matrix3 const& rotation, G3D::Vector3 const& position)
737 {
738 transformedVertices.reserve(transformedVertices.size() + source.size());
739 for (G3D::Vector3 const& vertex : source)
740 {
741 // apply tranform, then mirror along the horizontal axes
742 G3D::Vector3 v(vertex * rotation * scale + position);
743 v.x *= -1.f;
744 v.y *= -1.f;
745 transformedVertices.push_back(v);
746 }
747 }
748
749 /**************************************************************************/
750 void TerrainBuilder::copyVertices(std::vector<G3D::Vector3> const& source, G3D::Array<float>& dest)
751 {
752 dest.reserve(dest.size() + source.size() * 3);
753 for (G3D::Vector3 const& vertex : source)
754 {
755 dest.push_back(vertex.y);
756 dest.push_back(vertex.z);
757 dest.push_back(vertex.x);
758 }
759 }
760
761 /**************************************************************************/
762 void TerrainBuilder::copyIndices(std::vector<MeshTriangle> const& source, G3D::Array<int>& dest, int offset, bool flip)
763 {
764 dest.reserve(dest.size() + source.size() * 3);
765 if (flip)
766 {
767 for (MeshTriangle const& triangle : source)
768 {
769 dest.push_back(triangle.idx2 + offset);
770 dest.push_back(triangle.idx1 + offset);
771 dest.push_back(triangle.idx0 + offset);
772 }
773 }
774 else
775 {
776 for (MeshTriangle const& triangle : source)
777 {
778 dest.push_back(triangle.idx0 + offset);
779 dest.push_back(triangle.idx1 + offset);
780 dest.push_back(triangle.idx2 + offset);
781 }
782 }
783 }
784
785 /**************************************************************************/
786 void TerrainBuilder::copyIndices(G3D::Array<int> const& source, G3D::Array<int>& dest, int offset)
787 {
788 int const* src = source.getCArray();
789 dest.reserve(dest.size() + source.size());
790 for (int32 i = 0; i < source.size(); ++i)
791 dest.append(src[i] + offset);
792 }
793
794 /**************************************************************************/
795 void TerrainBuilder::cleanVertices(G3D::Array<float> &verts, G3D::Array<int> &tris)
796 {
797 std::map<int, int> vertMap;
798
799 int* t = tris.getCArray();
800 float* v = verts.getCArray();
801
802 G3D::Array<float> cleanVerts;
803 int index, count = 0;
804 // collect all the vertex indices from triangle
805 for (int i = 0; i < tris.size(); ++i)
806 {
807 if (vertMap.find(t[i]) != vertMap.end())
808 continue;
809 std::pair<int, int> val;
810 val.first = t[i];
811
812 index = val.first;
813 val.second = count;
814
815 vertMap.insert(val);
816 cleanVerts.append(v[index * 3], v[index * 3 + 1], v[index * 3 + 2]);
817 count++;
818 }
819
820 verts.fastClear();
821 verts.append(cleanVerts);
822 cleanVerts.clear();
823
824 // update triangles to use new indices
825 for (int i = 0; i < tris.size(); ++i)
826 {
827 std::map<int, int>::iterator it;
828 if ((it = vertMap.find(t[i])) == vertMap.end())
829 continue;
830
831 t[i] = (*it).second;
832 }
833
834 vertMap.clear();
835 }
836
837 /**************************************************************************/
838 void TerrainBuilder::loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, std::vector<OffMeshData> const& offMeshConnections)
839 {
840 for (OffMeshData const& offMeshConnection : offMeshConnections)
841 {
842 if (mapID != offMeshConnection.MapId || tileX != offMeshConnection.TileX || tileY != offMeshConnection.TileY)
843 continue;
844
845 meshData.offMeshConnections.append(offMeshConnection.From[1]);
846 meshData.offMeshConnections.append(offMeshConnection.From[2]);
847 meshData.offMeshConnections.append(offMeshConnection.From[0]);
848
849 meshData.offMeshConnections.append(offMeshConnection.To[1]);
850 meshData.offMeshConnections.append(offMeshConnection.To[2]);
851 meshData.offMeshConnections.append(offMeshConnection.To[0]);
852
853 meshData.offMeshConnectionDirs.append(offMeshConnection.Bidirectional ? 1 : 0);
854 meshData.offMeshConnectionRads.append(offMeshConnection.Radius); // agent size equivalent
855 // can be used same way as polygon flags
856 meshData.offMeshConnectionsAreas.append(offMeshConnection.AreaId);
857 meshData.offMeshConnectionsFlags.append(offMeshConnection.Flags);
858 }
859 }
860}
uint8_t uint8
Definition: Define.h:144
int32_t int32
Definition: Define.h:138
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
uint16 flags
Definition: DisableMgr.cpp:49
@ NAV_AREA_MAGMA_SLIME
Definition: MMapDefines.h:57
@ NAV_AREA_EMPTY
Definition: MMapDefines.h:51
@ NAV_AREA_WATER
Definition: MMapDefines.h:56
uint32 const MapVersionMagic
Definition: MapDefines.cpp:21
map_liquidHeaderTypeFlags
Definition: MapDefines.h:97
uint8 holes[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID][8]
Definition: System.cpp:439
map_liquidHeaderTypeFlags liquid_flags[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
Definition: System.cpp:436
uint16 liquid_entry[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
Definition: System.cpp:435
float V8[ADT_GRID_SIZE][ADT_GRID_SIZE]
Definition: System.cpp:428
float V9[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]
Definition: System.cpp:429
T square(T x)
Definition: Util.h:103
constexpr bool HasFlag(T flag) const
Definition: EnumFlag.h:106
Definition: Grid.h:46
void loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, std::vector< OffMeshData > const &offMeshConnections)
bool isHole(int square, uint8 const holes[16][16][8])
Determines if the specific position's triangles should be rendered.
static void copyIndices(std::vector< VMAP::MeshTriangle > const &source, G3D::Array< int > &dest, int offset, bool flip)
static void cleanVertices(G3D::Array< float > &verts, G3D::Array< int > &tris)
bool m_skipLiquid
Controls whether liquids are loaded.
void getHeightTriangle(int square, Spot triangle, int *indices, bool liquid=false)
Get the triangle's vector indices for a specific position.
void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
void getLiquidCoord(int index, int index2, float xOffset, float yOffset, float *coord, float *v)
Get the liquid vector coordinate for a specific position.
static void transform(std::vector< G3D::Vector3 > const &source, std::vector< G3D::Vector3 > &transformed, float scale, G3D::Matrix3 const &rotation, G3D::Vector3 const &position)
bool loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
void getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float *coord, float *v)
Get the vector coordinate for a specific position.
static void copyVertices(std::vector< G3D::Vector3 > const &source, G3D::Array< float > &dest)
void getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc)
Sets loop variables for selecting only certain parts of a map's terrain.
map_liquidHeaderTypeFlags getLiquidType(int square, map_liquidHeaderTypeFlags const (&liquid_type)[16][16])
Get the liquid type for a specific position.
G3D::Matrix3 const & GetInvRot() const
Definition: ModelInstance.h:78
WorldModel const * getWorldModel() const
Definition: ModelInstance.h:79
void getPosInfo(uint32 &tilesX, uint32 &tilesY, G3D::Vector3 &corner) const
Definition: WorldModel.cpp:281
float * GetHeightStorage()
Definition: WorldModel.h:61
uint32 GetType() const
Definition: WorldModel.h:60
uint8 * GetFlagsStorage()
Definition: WorldModel.h:62
std::vector< GroupModel > const & getGroupModels() const
Definition: WorldModel.h:129
bool IsM2() const
Definition: WorldModel.h:128
std::unique_ptr< VMAP::VMapManager2 > CreateVMapManager()
static const float GRID_SIZE
static const float INVALID_MAP_LIQ_HEIGHT_MAX
static const int V9_SIZE
static const float GRID_PART_SIZE
static const float INVALID_MAP_LIQ_HEIGHT
static const int V8_SIZE
static const int V9_SIZE_SQ
static const int V8_SIZE_SQ
std::unordered_map< uint32, MapEntry > sMapStore
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
std::unordered_map< uint32, StaticMapTree * > InstanceTreeMap
Definition: VMapManager2.h:54
LoadResult
Definition: IVMapManager.h:35
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
uint32 holesSize
Definition: MapDefines.h:50
uint32 heightMapOffset
Definition: MapDefines.h:45
uint32 holesOffset
Definition: MapDefines.h:49
uint32 versionMagic
Definition: MapDefines.h:41
uint32 liquidMapOffset
Definition: MapDefines.h:47
float gridMaxHeight
Definition: MapDefines.h:84
EnumFlag< map_heightHeaderFlags > flags
Definition: MapDefines.h:82
EnumFlag< map_liquidHeaderFlags > flags
Definition: MapDefines.h:114
EnumFlag< map_liquidHeaderTypeFlags > liquidFlags
Definition: MapDefines.h:115