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