AzerothCore 3.3.5a
OpenSource WoW Emulator
Loading...
Searching...
No Matches
MMAP::TerrainBuilder Class Reference

#include "TerrainBuilder.h"

Public Member Functions

 TerrainBuilder (bool skipLiquid)
 
 ~TerrainBuilder ()
 
 TerrainBuilder (const TerrainBuilder &tb)=delete
 
void loadMap (uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
 
bool loadVMap (uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
 
void loadOffMeshConnections (uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char *offMeshFilePath)
 
bool usesLiquids () const
 

Static Public Member Functions

static void transform (std::vector< G3D::Vector3 > &original, std::vector< G3D::Vector3 > &transformed, float scale, G3D::Matrix3 &rotation, G3D::Vector3 &position)
 
static void copyVertices (std::vector< G3D::Vector3 > &source, G3D::Array< float > &dest)
 
static void copyIndices (std::vector< VMAP::MeshTriangle > &source, G3D::Array< int > &dest, int offest, bool flip)
 
static void copyIndices (G3D::Array< int > &src, G3D::Array< int > &dest, int offset)
 
static void cleanVertices (G3D::Array< float > &verts, G3D::Array< int > &tris)
 

Private Member Functions

bool loadMap (uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion)
 Loads a portion of a map's terrain. More...
 
void getLoopVars (Spot portion, int &loopStart, int &loopEnd, int &loopInc)
 Sets loop variables for selecting only certain parts of a map's terrain. More...
 
bool loadHeightMap (uint32 mapID, uint32 tileX, uint32 tileY, G3D::Array< float > &vertices, G3D::Array< int > &triangles, Spot portion)
 Load the map terrain from file. More...
 
void getHeightCoord (int index, Grid grid, float xOffset, float yOffset, float *coord, float *v)
 Get the vector coordinate for a specific position. More...
 
void getHeightTriangle (int square, Spot triangle, int *indices, bool liquid=false)
 Get the triangle's vector indices for a specific position. More...
 
bool isHole (int square, const uint16 holes[16][16])
 Determines if the specific position's triangles should be rendered. More...
 
void getLiquidCoord (int index, int index2, float xOffset, float yOffset, float *coord, float *v)
 Get the liquid vector coordinate for a specific position. More...
 
uint8 getLiquidType (int square, const uint8 liquid_type[16][16])
 Get the liquid type for a specific position. More...
 

Private Attributes

bool m_skipLiquid
 Controls whether liquids are loaded. More...
 

Detailed Description

Constructor & Destructor Documentation

◆ TerrainBuilder() [1/2]

MMAP::TerrainBuilder::TerrainBuilder ( bool  skipLiquid)
86: m_skipLiquid (skipLiquid) { }
bool m_skipLiquid
Controls whether liquids are loaded.
Definition: TerrainBuilder.h:105

◆ ~TerrainBuilder()

MMAP::TerrainBuilder::~TerrainBuilder ( )
default

◆ TerrainBuilder() [2/2]

MMAP::TerrainBuilder::TerrainBuilder ( const TerrainBuilder tb)
delete

Member Function Documentation

◆ cleanVertices()

void MMAP::TerrainBuilder::cleanVertices ( G3D::Array< float > &  verts,
G3D::Array< int > &  tris 
)
static
883 {
884 std::map<int, int> vertMap;
885
886 int* t = tris.getCArray();
887 float* v = verts.getCArray();
888
889 G3D::Array<float> cleanVerts;
890 int index, count = 0;
891 // collect all the vertex indices from triangle
892 for (int i = 0; i < tris.size(); ++i)
893 {
894 if (vertMap.find(t[i]) != vertMap.end())
895 continue;
896 std::pair<int, int> val;
897 val.first = t[i];
898
899 index = val.first;
900 val.second = count;
901
902 vertMap.insert(val);
903 cleanVerts.append(v[index * 3], v[index * 3 + 1], v[index * 3 + 2]);
904 count++;
905 }
906
907 verts.fastClear();
908 verts.append(cleanVerts);
909 cleanVerts.clear();
910
911 // update triangles to use new indices
912 for (int i = 0; i < tris.size(); ++i)
913 {
914 std::map<int, int>::iterator it;
915 if ((it = vertMap.find(t[i])) == vertMap.end())
916 continue;
917
918 t[i] = (*it).second;
919 }
920
921 vertMap.clear();
922 }

Referenced by MMAP::MapBuilder::buildMeshFromFile(), and MMAP::TileBuilder::buildTile().

◆ copyIndices() [1/2]

void MMAP::TerrainBuilder::copyIndices ( G3D::Array< int > &  src,
G3D::Array< int > &  dest,
int  offset 
)
static
875 {
876 int* src = source.getCArray();
877 for (int32 i = 0; i < source.size(); ++i)
878 dest.append(src[i] + offset);
879 }
std::int32_t int32
Definition: Define.h:103

◆ copyIndices() [2/2]

void MMAP::TerrainBuilder::copyIndices ( std::vector< VMAP::MeshTriangle > &  source,
G3D::Array< int > &  dest,
int  offest,
bool  flip 
)
static
852 {
853 if (flip)
854 {
855 for (auto & it : source)
856 {
857 dest.push_back(it.idx2 + offset);
858 dest.push_back(it.idx1 + offset);
859 dest.push_back(it.idx0 + offset);
860 }
861 }
862 else
863 {
864 for (auto & it : source)
865 {
866 dest.push_back(it.idx0 + offset);
867 dest.push_back(it.idx1 + offset);
868 dest.push_back(it.idx2 + offset);
869 }
870 }
871 }

Referenced by MMAP::IntermediateValues::generateObjFile(), and loadVMap().

◆ copyVertices()

void MMAP::TerrainBuilder::copyVertices ( std::vector< G3D::Vector3 > &  source,
G3D::Array< float > &  dest 
)
static
841 {
842 for (auto & it : source)
843 {
844 dest.push_back(it.y);
845 dest.push_back(it.z);
846 dest.push_back(it.x);
847 }
848 }

Referenced by loadVMap().

◆ getHeightCoord()

void MMAP::TerrainBuilder::getHeightCoord ( int  index,
Grid  grid,
float  xOffset,
float  yOffset,
float *  coord,
float *  v 
)
private

Get the vector coordinate for a specific position.

543 {
544 // wow coords: x, y, height
545 // coord is mirroed about the horizontal axes
546 switch (grid)
547 {
548 case GRID_V9:
549 coord[0] = (xOffset + index % (V9_SIZE) * GRID_PART_SIZE) * -1.f;
550 coord[1] = (yOffset + (int)(index / (V9_SIZE)) * GRID_PART_SIZE) * -1.f;
551 coord[2] = v[index];
552 break;
553 case GRID_V8:
554 coord[0] = (xOffset + index % (V8_SIZE) * GRID_PART_SIZE + GRID_PART_SIZE / 2.f) * -1.f;
555 coord[1] = (yOffset + (int)(index / (V8_SIZE)) * GRID_PART_SIZE + GRID_PART_SIZE / 2.f) * -1.f;
556 coord[2] = v[index];
557 break;
558 }
559 }
static const int V9_SIZE
Definition: TerrainBuilder.h:44
static const float GRID_PART_SIZE
Definition: TerrainBuilder.h:49
static const int V8_SIZE
Definition: TerrainBuilder.h:46
@ GRID_V8
Definition: TerrainBuilder.h:40
@ GRID_V9
Definition: TerrainBuilder.h:41

References MMAP::GRID_PART_SIZE, MMAP::GRID_V8, MMAP::GRID_V9, MMAP::V8_SIZE, and MMAP::V9_SIZE.

Referenced by loadMap().

◆ getHeightTriangle()

void MMAP::TerrainBuilder::getHeightTriangle ( int  square,
Spot  triangle,
int *  indices,
bool  liquid = false 
)
private

Get the triangle's vector indices for a specific position.

563 {
564 int rowOffset = square / V8_SIZE;
565 if (!liquid)
566 switch (triangle)
567 {
568 case TOP:
569 indices[0] = square + rowOffset; // 0-----1 .... 128
570 indices[1] = square + 1 + rowOffset; // |\ T /|
571 indices[2] = (V9_SIZE_SQ) + square; // | \ / |
572 break; // |L 0 R| .. 127
573 case LEFT: // | / \ |
574 indices[0] = square + rowOffset; // |/ B \|
575 indices[1] = (V9_SIZE_SQ) + square; // 129---130 ... 386
576 indices[2] = square + V9_SIZE + rowOffset; // |\ /|
577 break; // | \ / |
578 case RIGHT: // | 128 | .. 255
579 indices[0] = square + 1 + rowOffset; // | / \ |
580 indices[1] = square + V9_SIZE + 1 + rowOffset; // |/ \|
581 indices[2] = (V9_SIZE_SQ) + square; // 258---259 ... 515
582 break;
583 case BOTTOM:
584 indices[0] = (V9_SIZE_SQ) + square;
585 indices[1] = square + V9_SIZE + 1 + rowOffset;
586 indices[2] = square + V9_SIZE + rowOffset;
587 break;
588 default:
589 break;
590 }
591 else
592 switch (triangle)
593 {
594 case TOP:
595 indices[0] = square + rowOffset;
596 indices[1] = square + 1 + rowOffset;
597 indices[2] = square + V9_SIZE + 1 + rowOffset;
598 break;
599 case BOTTOM:
600 indices[0] = square + rowOffset;
601 indices[1] = square + V9_SIZE + 1 + rowOffset;
602 indices[2] = square + V9_SIZE + rowOffset;
603 break;
604 default:
605 break;
606 }
607
608 /*
609 0-----1 .... 128
610 |\ |
611 | \ T |
612 | \ |
613 | B \ |
614 | \|
615 129---130 ... 386
616 |\ |
617 | \ |
618 | \ |
619 | \ |
620 | \|
621 258---259 ... 515
622 */
623 }
static const int V9_SIZE_SQ
Definition: TerrainBuilder.h:45
@ LEFT
Definition: TerrainBuilder.h:33
@ RIGHT
Definition: TerrainBuilder.h:32
@ BOTTOM
Definition: TerrainBuilder.h:34
@ TOP
Definition: TerrainBuilder.h:31

References MMAP::BOTTOM, MMAP::LEFT, MMAP::RIGHT, MMAP::TOP, MMAP::V8_SIZE, MMAP::V9_SIZE, and MMAP::V9_SIZE_SQ.

Referenced by loadMap().

◆ getLiquidCoord()

void MMAP::TerrainBuilder::getLiquidCoord ( int  index,
int  index2,
float  xOffset,
float  yOffset,
float *  coord,
float *  v 
)
private

Get the liquid vector coordinate for a specific position.

627 {
628 // wow coords: x, y, height
629 // coord is mirroed about the horizontal axes
630 coord[0] = (xOffset + index % (V9_SIZE) * GRID_PART_SIZE) * -1.f;
631 coord[1] = (yOffset + (int)(index / (V9_SIZE)) * GRID_PART_SIZE) * -1.f;
632 coord[2] = v[index2];
633 }

References MMAP::GRID_PART_SIZE, and MMAP::V9_SIZE.

Referenced by loadMap().

◆ getLiquidType()

uint8 MMAP::TerrainBuilder::getLiquidType ( int  square,
const uint8  liquid_type[16][16] 
)
private

Get the liquid type for a specific position.

655 {
656 int row = square / 128;
657 int col = square % 128;
658 int cellRow = row / 8; // 8 squares per cell
659 int cellCol = col / 8;
660
661 return liquid_type[cellRow][cellCol];
662 }

Referenced by loadMap().

◆ getLoopVars()

void MMAP::TerrainBuilder::getLoopVars ( Spot  portion,
int &  loopStart,
int &  loopEnd,
int &  loopInc 
)
private

Sets loop variables for selecting only certain parts of a map's terrain.

91 {
92 switch (portion)
93 {
94 case ENTIRE:
95 loopStart = 0;
96 loopEnd = V8_SIZE_SQ;
97 loopInc = 1;
98 break;
99 case TOP:
100 loopStart = 0;
101 loopEnd = V8_SIZE;
102 loopInc = 1;
103 break;
104 case LEFT:
105 loopStart = 0;
106 loopEnd = V8_SIZE_SQ - V8_SIZE + 1;
107 loopInc = V8_SIZE;
108 break;
109 case RIGHT:
110 loopStart = V8_SIZE - 1;
111 loopEnd = V8_SIZE_SQ;
112 loopInc = V8_SIZE;
113 break;
114 case BOTTOM:
115 loopStart = V8_SIZE_SQ - V8_SIZE;
116 loopEnd = V8_SIZE_SQ;
117 loopInc = 1;
118 break;
119 }
120 }
static const int V8_SIZE_SQ
Definition: TerrainBuilder.h:47
@ ENTIRE
Definition: TerrainBuilder.h:35

References MMAP::BOTTOM, MMAP::ENTIRE, MMAP::LEFT, MMAP::RIGHT, MMAP::TOP, MMAP::V8_SIZE, and MMAP::V8_SIZE_SQ.

Referenced by loadMap().

◆ isHole()

bool MMAP::TerrainBuilder::isHole ( int  square,
const uint16  holes[16][16] 
)
private

Determines if the specific position's triangles should be rendered.

640 {
641 int row = square / 128;
642 int col = square % 128;
643 int cellRow = row / 8; // 8 squares per cell
644 int cellCol = col / 8;
645 int holeRow = row % 8 / 2;
646 int holeCol = (square - (row * 128 + cellCol * 8)) / 2;
647
648 uint16 hole = holes[cellRow][cellCol];
649
650 return (hole & holetab_h[holeCol] & holetab_v[holeRow]) != 0;
651 }
std::uint16_t uint16
Definition: Define.h:108
uint16 holes[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
Definition: System.cpp:405
static uint16 holetab_v[4]
Definition: TerrainBuilder.cpp:636
static uint16 holetab_h[4]
Definition: TerrainBuilder.cpp:635

References holes, MMAP::holetab_h, and MMAP::holetab_v.

Referenced by loadMap().

◆ loadHeightMap()

bool MMAP::TerrainBuilder::loadHeightMap ( uint32  mapID,
uint32  tileX,
uint32  tileY,
G3D::Array< float > &  vertices,
G3D::Array< int > &  triangles,
Spot  portion 
)
private

Load the map terrain from file.

◆ loadMap() [1/2]

void MMAP::TerrainBuilder::loadMap ( uint32  mapID,
uint32  tileX,
uint32  tileY,
MeshData meshData 
)
124 {
125 if (loadMap(mapID, tileX, tileY, meshData, ENTIRE))
126 {
127 loadMap(mapID, tileX + 1, tileY, meshData, LEFT);
128 loadMap(mapID, tileX - 1, tileY, meshData, RIGHT);
129 loadMap(mapID, tileX, tileY + 1, meshData, TOP);
130 loadMap(mapID, tileX, tileY - 1, meshData, BOTTOM);
131 }
132 }
void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
Definition: TerrainBuilder.cpp:123

References MMAP::BOTTOM, MMAP::ENTIRE, MMAP::LEFT, loadMap(), MMAP::RIGHT, and MMAP::TOP.

Referenced by MMAP::TileBuilder::buildTile(), and loadMap().

◆ loadMap() [2/2]

bool MMAP::TerrainBuilder::loadMap ( uint32  mapID,
uint32  tileX,
uint32  tileY,
MeshData meshData,
Spot  portion 
)
private

Loads a portion of a map's terrain.

136 {
137 char mapFileName[255];
138 sprintf(mapFileName, "maps/%03u%02u%02u.map", mapID, tileY, tileX);
139
140 FILE* mapFile = fopen(mapFileName, "rb");
141 if (!mapFile)
142 return false;
143
144 map_fileheader fheader;
145 if (fread(&fheader, sizeof(map_fileheader), 1, mapFile) != 1 ||
147 {
148 fclose(mapFile);
149 printf("%s is the wrong version, please extract new .map files\n", mapFileName);
150 return false;
151 }
152
153 map_heightHeader hheader;
154 fseek(mapFile, fheader.heightMapOffset, SEEK_SET);
155
156 bool haveTerrain = false;
157 bool haveLiquid = false;
158 if (fread(&hheader, sizeof(map_heightHeader), 1, mapFile) == 1)
159 {
160 haveTerrain = !(hheader.flags & MAP_HEIGHT_NO_HEIGHT);
161 haveLiquid = fheader.liquidMapOffset && !m_skipLiquid;
162 }
163
164 // no data in this map file
165 if (!haveTerrain && !haveLiquid)
166 {
167 fclose(mapFile);
168 return false;
169 }
170
171 // data used later
172 uint16 holes[16][16];
173 memset(holes, 0, sizeof(holes));
174 uint16 liquid_entry[16][16];
175 memset(liquid_entry, 0, sizeof(liquid_entry));
176 uint8 liquid_flags[16][16];
177 memset(liquid_flags, 0, sizeof(liquid_flags));
178 G3D::Array<int> ltriangles;
179 G3D::Array<int> ttriangles;
180
181 // terrain data
182 if (haveTerrain)
183 {
184 float heightMultiplier;
185 float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ];
186 int expected = V9_SIZE_SQ + V8_SIZE_SQ;
187
188 if (hheader.flags & MAP_HEIGHT_AS_INT8)
189 {
190 uint8 v9[V9_SIZE_SQ];
191 uint8 v8[V8_SIZE_SQ];
192 int count = 0;
193 count += fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile);
194 count += fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile);
195 if (count != expected)
196 printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
197
198 heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255;
199
200 for (int i = 0; i < V9_SIZE_SQ; ++i)
201 V9[i] = (float)v9[i] * heightMultiplier + hheader.gridHeight;
202
203 for (int i = 0; i < V8_SIZE_SQ; ++i)
204 V8[i] = (float)v8[i] * heightMultiplier + hheader.gridHeight;
205 }
206 else if (hheader.flags & MAP_HEIGHT_AS_INT16)
207 {
208 uint16 v9[V9_SIZE_SQ];
209 uint16 v8[V8_SIZE_SQ];
210 int count = 0;
211 count += fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile);
212 count += fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile);
213 if (count != expected)
214 printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
215
216 heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535;
217
218 for (int i = 0; i < V9_SIZE_SQ; ++i)
219 V9[i] = (float)v9[i] * heightMultiplier + hheader.gridHeight;
220
221 for (int i = 0; i < V8_SIZE_SQ; ++i)
222 V8[i] = (float)v8[i] * heightMultiplier + hheader.gridHeight;
223 }
224 else
225 {
226 int count = 0;
227 count += fread(V9, sizeof(float), V9_SIZE_SQ, mapFile);
228 count += fread(V8, sizeof(float), V8_SIZE_SQ, mapFile);
229 if (count != expected)
230 printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
231 }
232
233 // hole data
234 if (fheader.holesSize != 0)
235 {
236 memset(holes, 0, fheader.holesSize);
237 fseek(mapFile, fheader.holesOffset, SEEK_SET);
238 if (fread(holes, fheader.holesSize, 1, mapFile) != 1)
239 printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
240 }
241
242 int count = meshData.solidVerts.size() / 3;
243 float xoffset = (float(tileX) - 32) * GRID_SIZE;
244 float yoffset = (float(tileY) - 32) * GRID_SIZE;
245
246 float coord[3];
247
248 for (int i = 0; i < V9_SIZE_SQ; ++i)
249 {
250 getHeightCoord(i, GRID_V9, xoffset, yoffset, coord, V9);
251 meshData.solidVerts.append(coord[0]);
252 meshData.solidVerts.append(coord[2]);
253 meshData.solidVerts.append(coord[1]);
254 }
255
256 for (int i = 0; i < V8_SIZE_SQ; ++i)
257 {
258 getHeightCoord(i, GRID_V8, xoffset, yoffset, coord, V8);
259 meshData.solidVerts.append(coord[0]);
260 meshData.solidVerts.append(coord[2]);
261 meshData.solidVerts.append(coord[1]);
262 }
263
264 int indices[] = { 0, 0, 0 };
265 int loopStart = 0, loopEnd = 0, loopInc = 0;
266 getLoopVars(portion, loopStart, loopEnd, loopInc);
267 for (int i = loopStart; i < loopEnd; i += loopInc)
268 for (int j = TOP; j <= BOTTOM; j += 1)
269 {
270 getHeightTriangle(i, Spot(j), indices);
271 ttriangles.append(indices[2] + count);
272 ttriangles.append(indices[1] + count);
273 ttriangles.append(indices[0] + count);
274 }
275 }
276
277 // liquid data
278 if (haveLiquid)
279 {
280 map_liquidHeader lheader;
281 fseek(mapFile, fheader.liquidMapOffset, SEEK_SET);
282 if (fread(&lheader, sizeof(map_liquidHeader), 1, mapFile) != 1)
283 printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
284
285 float* liquid_map = nullptr;
286
287 if (!(lheader.flags & MAP_LIQUID_NO_TYPE))
288 {
289 if (fread(liquid_entry, sizeof(liquid_entry), 1, mapFile) != 1)
290 printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
291 if (fread(liquid_flags, sizeof(liquid_flags), 1, mapFile) != 1)
292 printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
293 }
294 else
295 {
296 std::fill_n(&liquid_entry[0][0], 16 * 16, lheader.liquidType);
297 std::fill_n(&liquid_flags[0][0], 16 * 16, lheader.liquidFlags);
298 }
299
300 if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
301 {
302 uint32 toRead = lheader.width * lheader.height;
303 liquid_map = new float [toRead];
304 if (fread(liquid_map, sizeof(float), toRead, mapFile) != toRead)
305 {
306 printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
307 delete[] liquid_map;
308 liquid_map = nullptr;
309 }
310 }
311
312 int count = meshData.liquidVerts.size() / 3;
313 float xoffset = (float(tileX)-32)*GRID_SIZE;
314 float yoffset = (float(tileY)-32)*GRID_SIZE;
315
316 float coord[3];
317 int row, col;
318
319 // generate coordinates
320 if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
321 {
322 int j = 0;
323 for (int i = 0; i < V9_SIZE_SQ; ++i)
324 {
325 row = i / V9_SIZE;
326 col = i % V9_SIZE;
327
328 if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height ||
329 col < lheader.offsetX || col >= lheader.offsetX + lheader.width)
330 {
331 // dummy vert using invalid height
332 meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, INVALID_MAP_LIQ_HEIGHT, (yoffset+row*GRID_PART_SIZE)*-1);
333 continue;
334 }
335
336 getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map);
337 meshData.liquidVerts.append(coord[0]);
338 meshData.liquidVerts.append(coord[2]);
339 meshData.liquidVerts.append(coord[1]);
340 j++;
341 }
342 }
343 else
344 {
345 for (int i = 0; i < V9_SIZE_SQ; ++i)
346 {
347 row = i / V9_SIZE;
348 col = i % V9_SIZE;
349 meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, lheader.liquidLevel, (yoffset+row*GRID_PART_SIZE)*-1);
350 }
351 }
352
353 delete[] liquid_map;
354
355 int indices[] = { 0, 0, 0 };
356 int loopStart = 0, loopEnd = 0, loopInc = 0, triInc = BOTTOM-TOP;
357 getLoopVars(portion, loopStart, loopEnd, loopInc);
358
359 // generate triangles
360 for (int i = loopStart; i < loopEnd; i += loopInc)
361 {
362 for (int j = TOP; j <= BOTTOM; j += triInc)
363 {
364 getHeightTriangle(i, Spot(j), indices, true);
365 ltriangles.append(indices[2] + count);
366 ltriangles.append(indices[1] + count);
367 ltriangles.append(indices[0] + count);
368 }
369 }
370 }
371
372 fclose(mapFile);
373
374 // now that we have gathered the data, we can figure out which parts to keep:
375 // liquid above ground, ground above liquid
376 int loopStart = 0, loopEnd = 0, loopInc = 0, tTriCount = 4;
377 bool useTerrain, useLiquid;
378
379 float* lverts = meshData.liquidVerts.getCArray();
380 int* ltris = ltriangles.getCArray();
381
382 float* tverts = meshData.solidVerts.getCArray();
383 int* ttris = ttriangles.getCArray();
384
385 if ((ltriangles.size() + ttriangles.size()) == 0)
386 return false;
387
388 // make a copy of liquid vertices
389 // used to pad right-bottom frame due to lost vertex data at extraction
390 float* lverts_copy = nullptr;
391 if (meshData.liquidVerts.size())
392 {
393 lverts_copy = new float[meshData.liquidVerts.size()];
394 memcpy(lverts_copy, lverts, sizeof(float)*meshData.liquidVerts.size());
395 }
396
397 getLoopVars(portion, loopStart, loopEnd, loopInc);
398 for (int i = loopStart; i < loopEnd; i += loopInc)
399 {
400 for (int j = 0; j < 2; ++j)
401 {
402 // default is true, will change to false if needed
403 useTerrain = true;
404 useLiquid = true;
405 uint8 liquidType = MAP_LIQUID_TYPE_NO_WATER;
406 // FIXME: "warning: the address of ‘liquid_type’ will always evaluate as ‘true’"
407
408 // if there is no liquid, don't use liquid
409 if (!meshData.liquidVerts.size() || !ltriangles.size())
410 {
411 useLiquid = false;
412 }
413 else
414 {
415 liquidType = getLiquidType(i, liquid_flags);
416 switch (liquidType)
417 {
418 default:
419 useLiquid = false;
420 break;
423 // merge different types of water
424 liquidType = NAV_WATER;
425 break;
427 liquidType = NAV_MAGMA;
428 break;
430 liquidType = NAV_SLIME;
431 break;
433 // players should not be here, so logically neither should creatures
434 useTerrain = false;
435 useLiquid = false;
436 break;
437 }
438 }
439
440 // if there is no terrain, don't use terrain
441 if (!ttriangles.size())
442 useTerrain = false;
443
444 // while extracting ADT data we are losing right-bottom vertices
445 // this code adds fair approximation of lost data
446 if (useLiquid)
447 {
448 float quadHeight = 0;
449 uint32 validCount = 0;
450 for (uint32 idx = 0; idx < 3; idx++)
451 {
452 float h = lverts_copy[ltris[idx] * 3 + 1];
454 {
455 quadHeight += h;
456 validCount++;
457 }
458 }
459
460 // update vertex height data
461 if (validCount > 0 && validCount < 3)
462 {
463 quadHeight /= validCount;
464 for (uint32 idx = 0; idx < 3; idx++)
465 {
466 float h = lverts[ltris[idx] * 3 + 1];
468 lverts[ltris[idx] * 3 + 1] = quadHeight;
469 }
470 }
471
472 // no valid vertexes - don't use this poly at all
473 if (validCount == 0)
474 useLiquid = false;
475 }
476
477 // if there is a hole here, don't use the terrain
478 if (useTerrain && fheader.holesSize != 0)
479 useTerrain = !isHole(i, holes);
480
481 // we use only one terrain kind per quad - pick higher one
482 if (useTerrain && useLiquid)
483 {
484 float minLLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
485 float maxLLevel = INVALID_MAP_LIQ_HEIGHT;
486 for (uint32 x = 0; x < 3; x++)
487 {
488 float h = lverts[ltris[x] * 3 + 1];
489 if (minLLevel > h)
490 minLLevel = h;
491
492 if (maxLLevel < h)
493 maxLLevel = h;
494 }
495
496 float maxTLevel = INVALID_MAP_LIQ_HEIGHT;
497 float minTLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
498 for (uint32 x = 0; x < 6; x++)
499 {
500 float h = tverts[ttris[x] * 3 + 1];
501 if (maxTLevel < h)
502 maxTLevel = h;
503
504 if (minTLevel > h)
505 minTLevel = h;
506 }
507
508 // terrain under the liquid?
509 if (minLLevel > maxTLevel)
510 useTerrain = false;
511
512 //liquid under the terrain?
513 if (minTLevel > maxLLevel)
514 useLiquid = false;
515 }
516
517 // store the result
518 if (useLiquid)
519 {
520 meshData.liquidType.append(liquidType);
521 for (int k = 0; k < 3; ++k)
522 meshData.liquidTris.append(ltris[k]);
523 }
524
525 if (useTerrain)
526 for (int k = 0; k < 3 * tTriCount / 2; ++k)
527 meshData.solidTris.append(ttris[k]);
528
529 // advance to next set of triangles
530 ltris += 3;
531 ttris += 3 * tTriCount / 2;
532 }
533 }
534
535 if (lverts_copy)
536 delete [] lverts_copy;
537
538 return meshData.solidTris.size() || meshData.liquidTris.size();
539 }
@ NAV_MAGMA
Definition: MapDefines.h:55
@ NAV_SLIME
Definition: MapDefines.h:56
@ NAV_WATER
Definition: MapDefines.h:57
std::uint8_t uint8
Definition: Define.h:109
std::uint32_t uint32
Definition: Define.h:107
uint16 liquid_entry[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
Definition: System.cpp:401
uint8 liquid_flags[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
Definition: System.cpp:402
float V8[ADT_GRID_SIZE][ADT_GRID_SIZE]
Definition: System.cpp:394
float V9[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]
Definition: System.cpp:395
#define MAP_LIQUID_TYPE_MAGMA
Definition: Map.h:155
#define MAP_HEIGHT_AS_INT8
Definition: Map.h:113
#define MAP_LIQUID_TYPE_NO_WATER
Definition: Map.h:152
#define MAP_LIQUID_NO_TYPE
Definition: Map.h:124
#define MAP_LIQUID_NO_HEIGHT
Definition: Map.h:125
#define MAP_LIQUID_TYPE_WATER
Definition: Map.h:153
#define MAP_LIQUID_TYPE_DARK_WATER
Definition: Map.h:160
#define MAP_LIQUID_TYPE_OCEAN
Definition: Map.h:154
#define MAP_LIQUID_TYPE_SLIME
Definition: Map.h:156
#define MAP_HEIGHT_NO_HEIGHT
Definition: Map.h:111
#define MAP_HEIGHT_AS_INT16
Definition: Map.h:112
static const float GRID_SIZE
Definition: TerrainBuilder.h:48
static const float INVALID_MAP_LIQ_HEIGHT_MAX
Definition: TerrainBuilder.h:53
static const float INVALID_MAP_LIQ_HEIGHT
Definition: TerrainBuilder.h:52
Spot
Definition: TerrainBuilder.h:30
uint32 const MAP_VERSION_MAGIC
Definition: TerrainBuilder.cpp:84
Definition: Map.h:88
uint32 holesSize
Definition: Map.h:99
uint32 heightMapOffset
Definition: Map.h:94
uint32 holesOffset
Definition: Map.h:98
uint32 versionMagic
Definition: Map.h:90
uint32 liquidMapOffset
Definition: Map.h:96
Definition: Map.h:117
float gridMaxHeight
Definition: Map.h:121
uint32 flags
Definition: Map.h:119
float gridHeight
Definition: Map.h:120
Definition: Map.h:128
uint8 offsetX
Definition: Map.h:133
uint8 liquidFlags
Definition: Map.h:131
uint8 width
Definition: Map.h:135
uint8 height
Definition: Map.h:136
uint8 flags
Definition: Map.h:130
uint16 liquidType
Definition: Map.h:132
uint8 offsetY
Definition: Map.h:134
float liquidLevel
Definition: Map.h:137
bool isHole(int square, const uint16 holes[16][16])
Determines if the specific position's triangles should be rendered.
Definition: TerrainBuilder.cpp:639
uint8 getLiquidType(int square, const uint8 liquid_type[16][16])
Get the liquid type for a specific position.
Definition: TerrainBuilder.cpp:654
void getHeightTriangle(int square, Spot triangle, int *indices, bool liquid=false)
Get the triangle's vector indices for a specific position.
Definition: TerrainBuilder.cpp:562
void getLiquidCoord(int index, int index2, float xOffset, float yOffset, float *coord, float *v)
Get the liquid vector coordinate for a specific position.
Definition: TerrainBuilder.cpp:626
void getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float *coord, float *v)
Get the vector coordinate for a specific position.
Definition: TerrainBuilder.cpp:542
void getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc)
Sets loop variables for selecting only certain parts of a map's terrain.
Definition: TerrainBuilder.cpp:90

References MMAP::BOTTOM, map_heightHeader::flags, map_liquidHeader::flags, getHeightCoord(), getHeightTriangle(), getLiquidCoord(), getLiquidType(), getLoopVars(), MMAP::GRID_PART_SIZE, MMAP::GRID_SIZE, MMAP::GRID_V8, MMAP::GRID_V9, map_heightHeader::gridHeight, map_heightHeader::gridMaxHeight, map_liquidHeader::height, map_fileheader::heightMapOffset, holes, map_fileheader::holesOffset, map_fileheader::holesSize, MMAP::INVALID_MAP_LIQ_HEIGHT, MMAP::INVALID_MAP_LIQ_HEIGHT_MAX, isHole(), liquid_entry, liquid_flags, map_liquidHeader::liquidFlags, map_liquidHeader::liquidLevel, map_fileheader::liquidMapOffset, MMAP::MeshData::liquidTris, map_liquidHeader::liquidType, MMAP::MeshData::liquidType, MMAP::MeshData::liquidVerts, m_skipLiquid, MAP_HEIGHT_AS_INT16, MAP_HEIGHT_AS_INT8, MAP_HEIGHT_NO_HEIGHT, MAP_LIQUID_NO_HEIGHT, MAP_LIQUID_NO_TYPE, MAP_LIQUID_TYPE_DARK_WATER, MAP_LIQUID_TYPE_MAGMA, MAP_LIQUID_TYPE_NO_WATER, MAP_LIQUID_TYPE_OCEAN, MAP_LIQUID_TYPE_SLIME, MAP_LIQUID_TYPE_WATER, MMAP::MAP_VERSION_MAGIC, NAV_MAGMA, NAV_SLIME, NAV_WATER, map_liquidHeader::offsetX, map_liquidHeader::offsetY, MMAP::MeshData::solidTris, MMAP::MeshData::solidVerts, MMAP::TOP, V8, MMAP::V8_SIZE_SQ, V9, MMAP::V9_SIZE, MMAP::V9_SIZE_SQ, map_fileheader::versionMagic, and map_liquidHeader::width.

◆ loadOffMeshConnections()

void MMAP::TerrainBuilder::loadOffMeshConnections ( uint32  mapID,
uint32  tileX,
uint32  tileY,
MeshData meshData,
const char *  offMeshFilePath 
)
926 {
927 // no meshfile input given?
928 if (!offMeshFilePath)
929 return;
930
931 FILE* fp = fopen(offMeshFilePath, "rb");
932 if (!fp)
933 {
934 printf(" loadOffMeshConnections:: input file %s not found!\n", offMeshFilePath);
935 return;
936 }
937
938 // pretty silly thing, as we parse entire file and load only the tile we need
939 // but we don't expect this file to be too large
940 char* buf = new char[512];
941 while (fgets(buf, 512, fp))
942 {
943 float p0[3], p1[3];
944 uint32 mid, tx, ty;
945 float size;
946 if (sscanf(buf, "%u %u,%u (%f %f %f) (%f %f %f) %f", &mid, &tx, &ty,
947 &p0[0], &p0[1], &p0[2], &p1[0], &p1[1], &p1[2], &size) != 10)
948 continue;
949
950 if (mapID == mid && tileX == tx && tileY == ty)
951 {
952 meshData.offMeshConnections.append(p0[1]);
953 meshData.offMeshConnections.append(p0[2]);
954 meshData.offMeshConnections.append(p0[0]);
955
956 meshData.offMeshConnections.append(p1[1]);
957 meshData.offMeshConnections.append(p1[2]);
958 meshData.offMeshConnections.append(p1[0]);
959
960 meshData.offMeshConnectionDirs.append(1); // 1 - both direction, 0 - one sided
961 meshData.offMeshConnectionRads.append(size); // agent size equivalent
962 // can be used same way as polygon flags
963 meshData.offMeshConnectionsAreas.append((unsigned char)0xFF);
964 meshData.offMeshConnectionsFlags.append((unsigned short)0xFF); // all movement masks can make this path
965 }
966 }
967
968 delete [] buf;
969 fclose(fp);
970 }

References MMAP::MeshData::offMeshConnectionDirs, MMAP::MeshData::offMeshConnectionRads, MMAP::MeshData::offMeshConnections, MMAP::MeshData::offMeshConnectionsAreas, and MMAP::MeshData::offMeshConnectionsFlags.

Referenced by MMAP::TileBuilder::buildTile().

◆ loadVMap()

bool MMAP::TerrainBuilder::loadVMap ( uint32  mapID,
uint32  tileX,
uint32  tileY,
MeshData meshData 
)
666 {
667 IVMapMgr* vmapMgr = new VMapMgr2();
668 int result = vmapMgr->loadMap("vmaps", mapID, tileX, tileY);
669 bool retval = false;
670
671 do
672 {
673 if (result == VMAP_LOAD_RESULT_ERROR)
674 break;
675
676 InstanceTreeMap instanceTrees;
677 ((VMapMgr2*)vmapMgr)->GetInstanceMapTree(instanceTrees);
678
679 if (!instanceTrees[mapID])
680 break;
681
682 ModelInstance* models = nullptr;
683 uint32 count = 0;
684 instanceTrees[mapID]->GetModelInstances(models, count);
685
686 if (!models)
687 break;
688
689 for (uint32 i = 0; i < count; ++i)
690 {
691 ModelInstance instance = models[i];
692
693 // model instances exist in tree even though there are instances of that model in this tile
694 WorldModel* worldModel = instance.getWorldModel();
695 if (!worldModel)
696 continue;
697
698 // now we have a model to add to the meshdata
699 retval = true;
700
701 std::vector<GroupModel> groupModels;
702 worldModel->GetGroupModels(groupModels);
703
704 // all M2s need to have triangle indices reversed
705 bool isM2 = instance.name.find(".m2") != std::string::npos || instance.name.find(".M2") != std::string::npos;
706
707 // transform data
708 float scale = instance.iScale;
709 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);
710 G3D::Vector3 position = instance.iPos;
711 position.x -= 32 * GRID_SIZE;
712 position.y -= 32 * GRID_SIZE;
713
714 for (auto & groupModel : groupModels)
715 {
716 std::vector<G3D::Vector3> tempVertices;
717 std::vector<G3D::Vector3> transformedVertices;
718 std::vector<MeshTriangle> tempTriangles;
719 WmoLiquid* liquid = nullptr;
720
721 groupModel.GetMeshData(tempVertices, tempTriangles, liquid);
722
723 // first handle collision mesh
724 transform(tempVertices, transformedVertices, scale, rotation, position);
725
726 int offset = meshData.solidVerts.size() / 3;
727
728 copyVertices(transformedVertices, meshData.solidVerts);
729 copyIndices(tempTriangles, meshData.solidTris, offset, isM2);
730
731 // now handle liquid data
732 if (liquid && liquid->GetFlagsStorage())
733 {
734 std::vector<G3D::Vector3> liqVerts;
735 std::vector<int> liqTris;
736 uint32 tilesX, tilesY, vertsX, vertsY;
737 G3D::Vector3 corner;
738 liquid->GetPosInfo(tilesX, tilesY, corner);
739 vertsX = tilesX + 1;
740 vertsY = tilesY + 1;
741 uint8* flags = liquid->GetFlagsStorage();
742 float* data = liquid->GetHeightStorage();
743 uint8 type = NAV_EMPTY;
744
745 switch (liquid->GetType() & 3)
746 {
747 case 0:
748 case 1:
749 type = NAV_WATER;
750 break;
751 case 2:
752 type = NAV_MAGMA;
753 break;
754 case 3:
755 type = NAV_SLIME;
756 break;
757 }
758
759 // indexing is weird...
760 // after a lot of trial and error, this is what works:
761 // vertex = y*vertsX+x
762 // tile = x*tilesY+y
763 // flag = y*tilesY+x
764
765 G3D::Vector3 vert;
766 for (uint32 x = 0; x < vertsX; ++x)
767 {
768 for (uint32 y = 0; y < vertsY; ++y)
769 {
770 vert = G3D::Vector3(corner.x + x * GRID_PART_SIZE, corner.y + y * GRID_PART_SIZE, data[y * vertsX + x]);
771 vert = vert * rotation * scale + position;
772 vert.x *= -1.f;
773 vert.y *= -1.f;
774 liqVerts.push_back(vert);
775 }
776 }
777
778 int idx1, idx2, idx3, idx4;
779 uint32 square;
780 for (uint32 x = 0; x < tilesX; ++x)
781 {
782 for (uint32 y = 0; y < tilesY; ++y)
783 {
784 if ((flags[x + y * tilesX] & 0x0f) != 0x0f)
785 {
786 square = x * tilesY + y;
787 idx1 = square + x;
788 idx2 = square + 1 + x;
789 idx3 = square + tilesY + 1 + 1 + x;
790 idx4 = square + tilesY + 1 + x;
791
792 // top triangle
793 liqTris.push_back(idx3);
794 liqTris.push_back(idx2);
795 liqTris.push_back(idx1);
796 // bottom triangle
797 liqTris.push_back(idx4);
798 liqTris.push_back(idx3);
799 liqTris.push_back(idx1);
800 }
801 }
802 }
803
804 uint32 liqOffset = meshData.liquidVerts.size() / 3;
805 for (auto & liqVert : liqVerts)
806 {
807 meshData.liquidVerts.append(liqVert.y, liqVert.z, liqVert.x);
808 }
809
810 for (uint32 j = 0; j < liqTris.size() / 3; ++j)
811 {
812 meshData.liquidTris.append(liqTris[j * 3 + 1] + liqOffset, liqTris[j * 3 + 2] + liqOffset, liqTris[j * 3] + liqOffset);
813 meshData.liquidType.append(type);
814 }
815 }
816 }
817 }
818 } while (false);
819
820 vmapMgr->unloadMap(mapID, tileX, tileY);
821 delete vmapMgr;
822
823 return retval;
824 }
@ NAV_EMPTY
Definition: MapDefines.h:53
std::unordered_map< uint32, StaticMapTree * > InstanceTreeMap
Definition: VMapMgr2.h:64
@ VMAP_LOAD_RESULT_ERROR
Definition: IVMapMgr.h:36
Definition: IVMapMgr.h:78
virtual void unloadMap(unsigned int pMapId, int x, int y)=0
virtual int loadMap(const char *pBasePath, unsigned int pMapId, int x, int y)=0
Definition: VMapMgr2.h:76
std::string name
Definition: ModelInstance.h:52
G3D::Vector3 iRot
Definition: ModelInstance.h:49
float iScale
Definition: ModelInstance.h:50
G3D::Vector3 iPos
Definition: ModelInstance.h:48
Definition: ModelInstance.h:63
WorldModel * getWorldModel()
Definition: ModelInstance.h:72
Definition: WorldModel.h:46
float * GetHeightStorage()
Definition: WorldModel.h:54
uint32 GetType() const
Definition: WorldModel.h:53
uint8 * GetFlagsStorage()
Definition: WorldModel.h:55
void GetPosInfo(uint32 &tilesX, uint32 &tilesY, G3D::Vector3 &corner) const
Definition: WorldModel.cpp:305
Definition: WorldModel.h:104
void GetGroupModels(std::vector< GroupModel > &outGroupModels)
Definition: WorldModel.cpp:696
static void copyVertices(std::vector< G3D::Vector3 > &source, G3D::Array< float > &dest)
Definition: TerrainBuilder.cpp:840
static void transform(std::vector< G3D::Vector3 > &original, std::vector< G3D::Vector3 > &transformed, float scale, G3D::Matrix3 &rotation, G3D::Vector3 &position)
Definition: TerrainBuilder.cpp:827
static void copyIndices(std::vector< VMAP::MeshTriangle > &source, G3D::Array< int > &dest, int offest, bool flip)
Definition: TerrainBuilder.cpp:851

References copyIndices(), copyVertices(), VMAP::WmoLiquid::GetFlagsStorage(), VMAP::WorldModel::GetGroupModels(), VMAP::WmoLiquid::GetHeightStorage(), VMAP::WmoLiquid::GetPosInfo(), VMAP::WmoLiquid::GetType(), VMAP::ModelInstance::getWorldModel(), MMAP::GRID_PART_SIZE, MMAP::GRID_SIZE, VMAP::ModelSpawn::iPos, VMAP::ModelSpawn::iRot, VMAP::ModelSpawn::iScale, MMAP::MeshData::liquidTris, MMAP::MeshData::liquidType, MMAP::MeshData::liquidVerts, VMAP::IVMapMgr::loadMap(), VMAP::ModelSpawn::name, NAV_EMPTY, NAV_MAGMA, NAV_SLIME, NAV_WATER, MMAP::MeshData::solidTris, MMAP::MeshData::solidVerts, transform(), VMAP::IVMapMgr::unloadMap(), and VMAP::VMAP_LOAD_RESULT_ERROR.

Referenced by MMAP::TileBuilder::buildTile(), and MMAP::MapBuilder::getGridBounds().

◆ transform()

void MMAP::TerrainBuilder::transform ( std::vector< G3D::Vector3 > &  original,
std::vector< G3D::Vector3 > &  transformed,
float  scale,
G3D::Matrix3 &  rotation,
G3D::Vector3 &  position 
)
static
828 {
829 for (auto & it : source)
830 {
831 // apply tranform, then mirror along the horizontal axes
832 G3D::Vector3 v(it * rotation * scale + position);
833 v.x *= -1.f;
834 v.y *= -1.f;
835 transformedVertices.push_back(v);
836 }
837 }

Referenced by loadVMap().

◆ usesLiquids()

bool MMAP::TerrainBuilder::usesLiquids ( ) const
inline
88{ return !m_skipLiquid; }

References m_skipLiquid.

Referenced by MMAP::TileBuilder::buildMoveMapTile().

Member Data Documentation

◆ m_skipLiquid

bool MMAP::TerrainBuilder::m_skipLiquid
private

Controls whether liquids are loaded.

Referenced by loadMap(), and usesLiquids().