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