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)
85: m_skipLiquid (skipLiquid) { }
bool m_skipLiquid
Controls whether liquids are loaded.
Definition: TerrainBuilder.h:106

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

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
874 {
875 int* src = source.getCArray();
876 for (int32 i = 0; i < source.size(); ++i)
877 dest.append(src[i] + offset);
878 }
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
851 {
852 if (flip)
853 {
854 for (auto & it : source)
855 {
856 dest.push_back(it.idx2 + offset);
857 dest.push_back(it.idx1 + offset);
858 dest.push_back(it.idx0 + offset);
859 }
860 }
861 else
862 {
863 for (auto & it : source)
864 {
865 dest.push_back(it.idx0 + offset);
866 dest.push_back(it.idx1 + offset);
867 dest.push_back(it.idx2 + offset);
868 }
869 }
870 }

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

◆ copyVertices()

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

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.

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

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.

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

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.

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

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.

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

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.

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

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.

639 {
640 int row = square / 128;
641 int col = square % 128;
642 int cellRow = row / 8; // 8 squares per cell
643 int cellCol = col / 8;
644 int holeRow = row % 8 / 2;
645 int holeCol = (square - (row * 128 + cellCol * 8)) / 2;
646
647 uint16 hole = holes[cellRow][cellCol];
648
649 return (hole & holetab_h[holeCol] & holetab_v[holeRow]) != 0;
650 }
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:635
static uint16 holetab_h[4]
Definition: TerrainBuilder.cpp:634

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 
)
123 {
124 if (loadMap(mapID, tileX, tileY, meshData, ENTIRE))
125 {
126 loadMap(mapID, tileX + 1, tileY, meshData, LEFT);
127 loadMap(mapID, tileX - 1, tileY, meshData, RIGHT);
128 loadMap(mapID, tileX, tileY + 1, meshData, TOP);
129 loadMap(mapID, tileX, tileY - 1, meshData, BOTTOM);
130 }
131 }
void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
Definition: TerrainBuilder.cpp:122

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.

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

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

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

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
827 {
828 for (auto & it : source)
829 {
830 // apply tranform, then mirror along the horizontal axes
831 G3D::Vector3 v(it * rotation * scale + position);
832 v.x *= -1.f;
833 v.y *= -1.f;
834 transformedVertices.push_back(v);
835 }
836 }

Referenced by loadVMap().

◆ usesLiquids()

bool MMAP::TerrainBuilder::usesLiquids ( ) const
inline
89{ 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().