TrinityCore
MapTree.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "MapTree.h"
19#include "Errors.h"
20#include "Log.h"
21#include "Metric.h"
22#include "ModelInstance.h"
23#include "VMapDefinitions.h"
24#include "VMapManager2.h"
25#include "WorldModel.h"
26#include <iomanip>
27#include <limits>
28#include <sstream>
29#include <string>
30
31using G3D::Vector3;
32
33namespace VMAP
34{
36 {
37 public:
38 MapRayCallback(ModelInstance* val, ModelIgnoreFlags ignoreFlags) : prims(val), hit(false), flags(ignoreFlags) { }
39 bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit = true)
40 {
41 bool result = prims[entry].intersectRay(ray, distance, pStopAtFirstHit, flags);
42 if (result)
43 hit = true;
44 return result;
45 }
46 bool didHit() { return hit; }
47 protected:
49 bool hit;
51 };
52
54 {
55 public:
56 LocationInfoCallback(ModelInstance* val, LocationInfo& info) : prims(val), locInfo(info), result(false) { }
57 void operator()(Vector3 const& point, uint32 entry)
58 {
59#ifdef VMAP_DEBUG
60 TC_LOG_DEBUG("maps", "LocationInfoCallback: trying to intersect '{}'", prims[entry].name);
61#endif
62 if (prims[entry].GetLocationInfo(point, locInfo))
63 result = true;
64 }
65
68 bool result;
69 };
70
71 //=========================================================
72
73 std::string StaticMapTree::getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY)
74 {
75 std::stringstream tilefilename;
76 tilefilename.fill('0');
77 tilefilename << std::setw(4) << mapID << '_';
78 //tilefilename << std::setw(2) << tileX << '_' << std::setw(2) << tileY << ".vmtile";
79 tilefilename << std::setw(2) << tileY << '_' << std::setw(2) << tileX << ".vmtile";
80 return tilefilename.str();
81 }
82
83 bool StaticMapTree::GetLocationInfo(Vector3 const& pos, LocationInfo& info) const
84 {
85 LocationInfoCallback intersectionCallBack(iTreeValues, info);
86 iTree.intersectPoint(pos, intersectionCallBack);
87 return intersectionCallBack.result;
88 }
89
90 StaticMapTree::StaticMapTree(uint32 mapID, std::string const& basePath)
91 : iMapID(mapID), iTreeValues(nullptr), iNTreeValues(0), iBasePath(basePath)
92 {
93 if (iBasePath.length() > 0 && iBasePath[iBasePath.length() - 1] != '/' && iBasePath[iBasePath.length() - 1] != '\\')
94 {
95 iBasePath.push_back('/');
96 }
97 }
98
99 //=========================================================
102 {
103 delete[] iTreeValues;
104 }
105
106 //=========================================================
112 bool StaticMapTree::getIntersectionTime(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
113 {
114 float distance = pMaxDist;
115 MapRayCallback intersectionCallBack(iTreeValues, ignoreFlags);
116 iTree.intersectRay(pRay, intersectionCallBack, distance, pStopAtFirstHit);
117 if (intersectionCallBack.didHit())
118 pMaxDist = distance;
119 return intersectionCallBack.didHit();
120 }
121 //=========================================================
122
123 bool StaticMapTree::isInLineOfSight(Vector3 const& pos1, Vector3 const& pos2, ModelIgnoreFlags ignoreFlag) const
124 {
125 float maxDist = (pos2 - pos1).magnitude();
126 // return false if distance is over max float, in case of cheater teleporting to the end of the universe
127 if (maxDist == std::numeric_limits<float>::max() || !std::isfinite(maxDist))
128 return false;
129
130 // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
131 ASSERT(maxDist < std::numeric_limits<float>::max());
132 // prevent NaN values which can cause BIH intersection to enter infinite loop
133 if (maxDist < 1e-10f)
134 return true;
135 // direction with length of 1
136 G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1) / maxDist);
137 if (getIntersectionTime(ray, maxDist, true, ignoreFlag))
138 return false;
139
140 return true;
141 }
142 //=========================================================
148 bool StaticMapTree::getObjectHitPos(Vector3 const& pPos1, Vector3 const& pPos2, Vector3& pResultHitPos, float pModifyDist) const
149 {
150 bool result = false;
151 float maxDist = (pPos2 - pPos1).magnitude();
152 // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
153 ASSERT(maxDist < std::numeric_limits<float>::max());
154 // prevent NaN values which can cause BIH intersection to enter infinite loop
155 if (maxDist < 1e-10f)
156 {
157 pResultHitPos = pPos2;
158 return false;
159 }
160 Vector3 dir = (pPos2 - pPos1) / maxDist; // direction with length of 1
161 G3D::Ray ray(pPos1, dir);
162 float dist = maxDist;
163 if (getIntersectionTime(ray, dist, false, ModelIgnoreFlags::Nothing))
164 {
165 pResultHitPos = pPos1 + dir * dist;
166 if (pModifyDist < 0)
167 {
168 if ((pResultHitPos - pPos1).magnitude() > -pModifyDist)
169 {
170 pResultHitPos = pResultHitPos + dir * pModifyDist;
171 }
172 else
173 {
174 pResultHitPos = pPos1;
175 }
176 }
177 else
178 {
179 pResultHitPos = pResultHitPos + dir * pModifyDist;
180 }
181 result = true;
182 }
183 else
184 {
185 pResultHitPos = pPos2;
186 result = false;
187 }
188 return result;
189 }
190
191 //=========================================================
192
193 float StaticMapTree::getHeight(Vector3 const& pPos, float maxSearchDist) const
194 {
195 float height = G3D::finf();
196 Vector3 dir = Vector3(0, 0, -1);
197 G3D::Ray ray(pPos, dir); // direction with length of 1
198 float maxDist = maxSearchDist;
199 if (getIntersectionTime(ray, maxDist, false, ModelIgnoreFlags::Nothing))
200 {
201 height = pPos.z - maxDist;
202 }
203 return(height);
204 }
205
207 {
208 TileFileOpenResult result;
209 result.Name = basePath + getTileFileName(mapID, tileX, tileY);
210 result.File = fopen(result.Name.c_str(), "rb");
211 result.UsedMapId = mapID;
212 if (!result.File)
213 {
214 int32 parentMapId = vm->getParentMapId(mapID);
215 while (parentMapId != -1)
216 {
217 result.Name = basePath + getTileFileName(parentMapId, tileX, tileY);
218 result.File = fopen(result.Name.c_str(), "rb");
219 result.UsedMapId = parentMapId;
220 if (result.File)
221 break;
222
223 parentMapId = vm->getParentMapId(uint32(parentMapId));
224 }
225 }
226
227 return result;
228 }
229
230 //=========================================================
231 LoadResult StaticMapTree::CanLoadMap(const std::string& vmapPath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2* vm)
232 {
233 std::string basePath = vmapPath;
234 if (basePath.length() > 0 && basePath[basePath.length() - 1] != '/' && basePath[basePath.length() - 1] != '\\')
235 basePath.push_back('/');
236 std::string fullname = basePath + VMapManager2::getMapFileName(mapID);
237
239
240 FILE* rf = fopen(fullname.c_str(), "rb");
241 if (!rf)
243
244 char chunk[8];
245 if (!readChunk(rf, chunk, VMAP_MAGIC, 8))
246 {
247 fclose(rf);
249 }
250 FILE* tf = OpenMapTileFile(basePath, mapID, tileX, tileY, vm).File;
251 if (!tf)
252 {
253 fclose(rf);
255 }
256 else
257 {
258 std::string tilefile = basePath + getTileFileName(mapID, tileX, tileY);
259 FILE* tf = fopen(tilefile.c_str(), "rb");
260 if (!tf)
262 else
263 {
264 if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
266 fclose(tf);
267 }
268 }
269 fclose(rf);
270 return result;
271 }
272
273 //=========================================================
274
275 LoadResult StaticMapTree::InitMap(std::string const& fname)
276 {
277 TC_LOG_DEBUG("maps", "StaticMapTree::InitMap() : initializing StaticMapTree '{}'", fname);
278 std::string fullname = iBasePath + fname;
279 FILE* rf = fopen(fullname.c_str(), "rb");
280 if (!rf)
282
284 char chunk[8];
285
286 if (!readChunk(rf, chunk, VMAP_MAGIC, 8))
288
289 if (result == LoadResult::Success &&
290 readChunk(rf, chunk, "NODE", 4) &&
292 {
295 result = LoadResult::Success;
296 }
297
298 if (result == LoadResult::Success)
299 {
300 result = readChunk(rf, chunk, "SIDX", 4) ? LoadResult::Success : LoadResult::ReadFromFileFailed;
301 uint32 spawnIndicesSize = 0;
302 uint32 spawnId;
303 if (result == LoadResult::Success && fread(&spawnIndicesSize, sizeof(uint32), 1, rf) != 1)
305 for (uint32 i = 0; i < spawnIndicesSize && result == LoadResult::Success; ++i)
306 {
307 if (fread(&spawnId, sizeof(uint32), 1, rf) == 1)
308 iSpawnIndices[spawnId] = i;
309 else
311 }
312 }
313
314 fclose(rf);
315 return result;
316 }
317
318 //=========================================================
319
321 {
322 for (std::pair<uint32 const, uint32>& iLoadedSpawn : iLoadedSpawns)
323 {
324 for (uint32 refCount = 0; refCount < iLoadedSpawn.second; ++refCount)
325 vm->releaseModelInstance(iTreeValues[iLoadedSpawn.first].getWorldModel()->GetName());
326
327 iTreeValues[iLoadedSpawn.first].setUnloaded();
328 }
329 iLoadedSpawns.clear();
330 iLoadedTiles.clear();
331 }
332
333 //=========================================================
334
336 {
337 if (!iTreeValues)
338 {
339 TC_LOG_ERROR("misc", "StaticMapTree::LoadMapTile() : tree has not been initialized [{}, {}]", tileX, tileY);
341 }
343
344 TileFileOpenResult fileResult = OpenMapTileFile(iBasePath, iMapID, tileX, tileY, vm);
345 if (fileResult.File)
346 {
347 char chunk[8];
348
349 result = LoadResult::Success;
350 if (!readChunk(fileResult.File, chunk, VMAP_MAGIC, 8))
352 uint32 numSpawns = 0;
353 if (result == LoadResult::Success && fread(&numSpawns, sizeof(uint32), 1, fileResult.File) != 1)
355 for (uint32 i = 0; i < numSpawns && result == LoadResult::Success; ++i)
356 {
357 // read model spawns
358 ModelSpawn spawn;
359 if (ModelSpawn::readFromFile(fileResult.File, spawn))
360 {
361 // acquire model instance
362 WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name);
363 if (!model)
364 TC_LOG_ERROR("misc", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [{}, {}]", tileX, tileY);
365
366 // update tree
367 auto spawnIndex = iSpawnIndices.find(spawn.ID);
368 if (spawnIndex != iSpawnIndices.end())
369 {
370 uint32 referencedVal = spawnIndex->second;
371 if (!iLoadedSpawns.count(referencedVal))
372 {
373 if (referencedVal >= iNTreeValues)
374 {
375 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{}) referenced in tile {}", referencedVal, iNTreeValues, fileResult.Name);
376 continue;
377 }
378
379 iTreeValues[referencedVal] = ModelInstance(spawn, model);
380 iLoadedSpawns[referencedVal] = 1;
381 }
382 else
383 {
384 ++iLoadedSpawns[referencedVal];
385#ifdef VMAP_DEBUG
386 if (iTreeValues[referencedVal].ID != spawn.ID)
387 TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node");
388 else if (iTreeValues[referencedVal].name != spawn.name)
389 TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : name collision on GUID={}", spawn.ID);
390#endif
391 }
392 }
393 else if (int32(iMapID) == fileResult.UsedMapId)
394 {
395 // unknown parent spawn might appear in because it overlaps multiple tiles
396 // in case the original tile is swapped but its neighbour is now (adding this spawn)
397 // we want to not mark it as loading error and just skip that model
398 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : invalid tree element (spawn {}) referenced in tile {} by map {}", spawn.ID, fileResult.Name, iMapID);
400 }
401 }
402 else
403 {
404 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : cannot read model from file (spawn index {}) referenced in tile {} by map {}", i, fileResult.Name, iMapID);
406 }
407 }
408 iLoadedTiles[packTileID(tileX, tileY)] = true;
409 fclose(fileResult.File);
410 }
411 else
412 iLoadedTiles[packTileID(tileX, tileY)] = false;
413 TC_METRIC_EVENT("map_events", "LoadMapTile",
414 "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
415 return result;
416 }
417
418 //=========================================================
419
421 {
422 uint32 tileID = packTileID(tileX, tileY);
423 loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
424 if (tile == iLoadedTiles.end())
425 {
426 TC_LOG_ERROR("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:{} X:{} Y:{}", iMapID, tileX, tileY);
427 return;
428 }
429 if (tile->second) // file associated with tile
430 {
431 TileFileOpenResult fileResult = OpenMapTileFile(iBasePath, iMapID, tileX, tileY, vm);
432 if (fileResult.File)
433 {
434 bool result = true;
435 char chunk[8];
436 if (!readChunk(fileResult.File, chunk, VMAP_MAGIC, 8))
437 result = false;
438 uint32 numSpawns;
439 if (fread(&numSpawns, sizeof(uint32), 1, fileResult.File) != 1)
440 result = false;
441 for (uint32 i = 0; i < numSpawns && result; ++i)
442 {
443 // read model spawns
444 ModelSpawn spawn;
445 result = ModelSpawn::readFromFile(fileResult.File, spawn);
446 if (result)
447 {
448 // release model instance
449 vm->releaseModelInstance(spawn.name);
450
451 // update tree
452 auto spawnIndex = iSpawnIndices.find(spawn.ID);
453 if (spawnIndex != iSpawnIndices.end())
454 {
455 uint32 referencedNode = spawnIndex->second;
456 if (!iLoadedSpawns.count(referencedNode))
457 TC_LOG_ERROR("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '{}' (ID:{})", spawn.name, spawn.ID);
458 else if (--iLoadedSpawns[referencedNode] == 0)
459 {
460 iTreeValues[referencedNode].setUnloaded();
461 iLoadedSpawns.erase(referencedNode);
462 }
463 }
464 else if (int32(iMapID) == fileResult.UsedMapId) // logic documented in StaticMapTree::LoadMapTile
465 result = false;
466 }
467 }
468 fclose(fileResult.File);
469 }
470 }
471 iLoadedTiles.erase(tile);
472 TC_METRIC_EVENT("map_events", "UnloadMapTile",
473 "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
474 }
475
477 {
478 models = iTreeValues;
479 count = iNTreeValues;
480 }
481}
int32_t int32
Definition: Define.h:138
uint32_t uint32
Definition: Define.h:142
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_METRIC_EVENT(category, title, description)
Definition: Metric.h:206
uint32 primCount() const
bool readFromFile(FILE *rf)
void intersectPoint(const G3D::Vector3 &p, IsectCallback &intersectCallback) const
void intersectRay(const G3D::Ray &r, RayCallback &intersectCallback, float &maxDist, bool stopAtFirst=false) const
ModelInstance * prims
Definition: MapTree.cpp:66
LocationInfoCallback(ModelInstance *val, LocationInfo &info)
Definition: MapTree.cpp:56
LocationInfo & locInfo
Definition: MapTree.cpp:67
void operator()(Vector3 const &point, uint32 entry)
Definition: MapTree.cpp:57
MapRayCallback(ModelInstance *val, ModelIgnoreFlags ignoreFlags)
Definition: MapTree.cpp:38
ModelInstance * prims
Definition: MapTree.cpp:48
bool operator()(const G3D::Ray &ray, uint32 entry, float &distance, bool pStopAtFirstHit=true)
Definition: MapTree.cpp:39
ModelIgnoreFlags flags
Definition: MapTree.cpp:50
WorldModel const * getWorldModel() const
Definition: ModelInstance.h:79
bool intersectRay(G3D::Ray const &pRay, float &pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
StaticMapTree(uint32 mapID, const std::string &basePath)
Definition: MapTree.cpp:90
uint32 iNTreeValues
Definition: MapTree.h:56
~StaticMapTree()
Make sure to call unloadMap() to unregister acquired model references before destroying.
Definition: MapTree.cpp:101
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition: MapTree.cpp:420
static std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY)
Definition: MapTree.cpp:73
bool getObjectHitPos(const G3D::Vector3 &pos1, const G3D::Vector3 &pos2, G3D::Vector3 &pResultHitPos, float pModifyDist) const
Definition: MapTree.cpp:148
static uint32 packTileID(uint32 tileX, uint32 tileY)
Definition: MapTree.h:81
LoadResult InitMap(std::string const &fname)
Definition: MapTree.cpp:275
std::string iBasePath
Definition: MapTree.h:66
bool getIntersectionTime(const G3D::Ray &pRay, float &pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
Definition: MapTree.cpp:112
ModelInstance * iTreeValues
Definition: MapTree.h:55
LoadResult LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition: MapTree.cpp:335
std::unordered_map< uint32, uint32 > iSpawnIndices
Definition: MapTree.h:57
loadedTileMap iLoadedTiles
Definition: MapTree.h:62
static LoadResult CanLoadMap(const std::string &basePath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition: MapTree.cpp:231
loadedSpawnMap iLoadedSpawns
Definition: MapTree.h:65
static TileFileOpenResult OpenMapTileFile(std::string const &basePath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition: MapTree.cpp:206
void getModelInstances(ModelInstance *&models, uint32 &count)
Definition: MapTree.cpp:476
bool GetLocationInfo(const G3D::Vector3 &pos, LocationInfo &info) const
Definition: MapTree.cpp:83
bool isInLineOfSight(const G3D::Vector3 &pos1, const G3D::Vector3 &pos2, ModelIgnoreFlags ignoreFlags) const
Definition: MapTree.cpp:123
float getHeight(const G3D::Vector3 &pPos, float maxSearchDist) const
Definition: MapTree.cpp:193
void UnloadMap(VMapManager2 *vm)
Definition: MapTree.cpp:320
int32 getParentMapId(uint32 mapId) const
void releaseModelInstance(std::string const &filename)
static std::string getMapFileName(unsigned int mapId)
WorldModel * acquireModelInstance(std::string const &basepath, std::string const &filename)
std::string const & GetName() const
Definition: WorldModel.h:130
bool readChunk(FILE *rf, char *dest, const char *compare, uint32 len)
LoadResult
Definition: IVMapManager.h:35
const char VMAP_MAGIC[]
static bool readFromFile(FILE *rf, ModelSpawn &spawn)
std::string name
Definition: ModelInstance.h:62