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

#include "TerrainBuilder.h"

Public Member Functions

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

Private Attributes

bool m_skipLiquid
 Controls whether liquids are loaded.
 
std::string m_mapsPath
 
std::string m_vmapsPath
 

Detailed Description

Constructor & Destructor Documentation

◆ TerrainBuilder() [1/2]

MMAP::TerrainBuilder::TerrainBuilder ( const std::string &  mapsPath,
bool  skipLiquid 
)
89 :
90 m_skipLiquid (skipLiquid),
91 m_mapsPath((std::filesystem::path(dataDirPath) / "maps").string()),
92 m_vmapsPath((std::filesystem::path(dataDirPath) / "vmaps").string())
93 {
94 }
std::string m_mapsPath
Definition TerrainBuilder.h:125
bool m_skipLiquid
Controls whether liquids are loaded.
Definition TerrainBuilder.h:105
std::string m_vmapsPath
Definition TerrainBuilder.h:126

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

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
880 {
881 int* src = source.getCArray();
882 for (int32 i = 0; i < source.size(); ++i)
883 dest.append(src[i] + offset);
884 }
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
857 {
858 if (flip)
859 {
860 for (auto & it : source)
861 {
862 dest.push_back(it.idx2 + offset);
863 dest.push_back(it.idx1 + offset);
864 dest.push_back(it.idx0 + offset);
865 }
866 }
867 else
868 {
869 for (auto & it : source)
870 {
871 dest.push_back(it.idx0 + offset);
872 dest.push_back(it.idx1 + offset);
873 dest.push_back(it.idx2 + offset);
874 }
875 }
876 }

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

◆ copyVertices()

void MMAP::TerrainBuilder::copyVertices ( std::vector< G3D::Vector3 > &  source,
G3D::Array< float > &  dest 
)
static
846 {
847 for (auto & it : source)
848 {
849 dest.push_back(it.y);
850 dest.push_back(it.z);
851 dest.push_back(it.x);
852 }
853 }

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.

555 {
556 // wow coords: x, y, height
557 // coord is mirroed about the horizontal axes
558 switch (grid)
559 {
560 case GRID_V9:
561 coord[0] = (xOffset + index % (V9_SIZE) * GRID_PART_SIZE) * -1.f;
562 coord[1] = (yOffset + (int)(index / (V9_SIZE)) * GRID_PART_SIZE) * -1.f;
563 coord[2] = v[index];
564 break;
565 case GRID_V8:
566 coord[0] = (xOffset + index % (V8_SIZE) * GRID_PART_SIZE + GRID_PART_SIZE / 2.f) * -1.f;
567 coord[1] = (yOffset + (int)(index / (V8_SIZE)) * GRID_PART_SIZE + GRID_PART_SIZE / 2.f) * -1.f;
568 coord[2] = v[index];
569 break;
570 }
571 }
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.

575 {
576 int rowOffset = square / V8_SIZE;
577 if (!liquid)
578 switch (triangle)
579 {
580 case TOP:
581 indices[0] = square + rowOffset; // 0-----1 .... 128
582 indices[1] = square + 1 + rowOffset; // |\ T /|
583 indices[2] = (V9_SIZE_SQ) + square; // | \ / |
584 break; // |L 0 R| .. 127
585 case LEFT: // | / \ |
586 indices[0] = square + rowOffset; // |/ B \|
587 indices[1] = (V9_SIZE_SQ) + square; // 129---130 ... 386
588 indices[2] = square + V9_SIZE + rowOffset; // |\ /|
589 break; // | \ / |
590 case RIGHT: // | 128 | .. 255
591 indices[0] = square + 1 + rowOffset; // | / \ |
592 indices[1] = square + V9_SIZE + 1 + rowOffset; // |/ \|
593 indices[2] = (V9_SIZE_SQ) + square; // 258---259 ... 515
594 break;
595 case BOTTOM:
596 indices[0] = (V9_SIZE_SQ) + square;
597 indices[1] = square + V9_SIZE + 1 + rowOffset;
598 indices[2] = square + V9_SIZE + rowOffset;
599 break;
600 default:
601 break;
602 }
603 else
604 switch (triangle)
605 {
606 case TOP:
607 indices[0] = square + rowOffset;
608 indices[1] = square + 1 + rowOffset;
609 indices[2] = square + V9_SIZE + 1 + rowOffset;
610 break;
611 case BOTTOM:
612 indices[0] = square + rowOffset;
613 indices[1] = square + V9_SIZE + 1 + rowOffset;
614 indices[2] = square + V9_SIZE + rowOffset;
615 break;
616 default:
617 break;
618 }
619
620 /*
621 0-----1 .... 128
622 |\ |
623 | \ T |
624 | \ |
625 | B \ |
626 | \|
627 129---130 ... 386
628 |\ |
629 | \ |
630 | \ |
631 | \ |
632 | \|
633 258---259 ... 515
634 */
635 }
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.

639 {
640 // wow coords: x, y, height
641 // coord is mirroed about the horizontal axes
642 coord[0] = (xOffset + index % (V9_SIZE) * GRID_PART_SIZE) * -1.f;
643 coord[1] = (yOffset + (int)(index / (V9_SIZE)) * GRID_PART_SIZE) * -1.f;
644 coord[2] = v[index2];
645 }

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.

667 {
668 int row = square / 128;
669 int col = square % 128;
670 int cellRow = row / 8; // 8 squares per cell
671 int cellCol = col / 8;
672
673 return liquid_type[cellRow][cellCol];
674 }

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.

100 {
101 switch (portion)
102 {
103 case ENTIRE:
104 loopStart = 0;
105 loopEnd = V8_SIZE_SQ;
106 loopInc = 1;
107 break;
108 case TOP:
109 loopStart = 0;
110 loopEnd = V8_SIZE;
111 loopInc = 1;
112 break;
113 case LEFT:
114 loopStart = 0;
115 loopEnd = V8_SIZE_SQ - V8_SIZE + 1;
116 loopInc = V8_SIZE;
117 break;
118 case RIGHT:
119 loopStart = V8_SIZE - 1;
120 loopEnd = V8_SIZE_SQ;
121 loopInc = V8_SIZE;
122 break;
123 case BOTTOM:
124 loopStart = V8_SIZE_SQ - V8_SIZE;
125 loopEnd = V8_SIZE_SQ;
126 loopInc = 1;
127 break;
128 }
129 }
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.

652 {
653 int row = square / 128;
654 int col = square % 128;
655 int cellRow = row / 8; // 8 squares per cell
656 int cellCol = col / 8;
657 int holeRow = row % 8 / 2;
658 int holeCol = (square - (row * 128 + cellCol * 8)) / 2;
659
660 uint16 hole = holes[cellRow][cellCol];
661
662 return (hole & holetab_h[holeCol] & holetab_v[holeRow]) != 0;
663 }
std::uint16_t uint16
Definition Define.h:108
uint16 holes[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
Definition System.cpp:409
static uint16 holetab_v[4]
Definition TerrainBuilder.cpp:648
static uint16 holetab_h[4]
Definition TerrainBuilder.cpp:647

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 
)
133 {
134 if (loadMap(mapID, tileX, tileY, meshData, ENTIRE))
135 {
136 loadMap(mapID, tileX + 1, tileY, meshData, LEFT);
137 loadMap(mapID, tileX - 1, tileY, meshData, RIGHT);
138 loadMap(mapID, tileX, tileY + 1, meshData, TOP);
139 loadMap(mapID, tileX, tileY - 1, meshData, BOTTOM);
140 }
141 }
void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
Definition TerrainBuilder.cpp:132

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.

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

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

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

References copyIndices(), copyVertices(), VMAP::WmoLiquid::GetFlagsStorage(), VMAP::WorldModel::GetGroupModels(), VMAP::WmoLiquid::GetHeightStorage(), VMAP::VMapMgr2::getMapFileName(), 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, m_vmapsPath, VMAP::ModelSpawn::name, NAV_EMPTY, NAV_MAGMA, NAV_SLIME, NAV_WATER, MMAP::MeshData::solidTris, MMAP::MeshData::solidVerts, and transform().

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

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_mapsPath

std::string MMAP::TerrainBuilder::m_mapsPath
private

Referenced by loadMap().

◆ m_skipLiquid

bool MMAP::TerrainBuilder::m_skipLiquid
private

Controls whether liquids are loaded.

Referenced by loadMap(), and usesLiquids().

◆ m_vmapsPath

std::string MMAP::TerrainBuilder::m_vmapsPath
private

Referenced by loadVMap().


The documentation for this class was generated from the following files: