TrinityCore
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 "Memory.h"
22#include "Metric.h"
23#include "ModelInstance.h"
24#include "VMapDefinitions.h"
25#include "VMapManager2.h"
26#include "WorldModel.h"
27#include <limits>
28#include <string>
29
30using G3D::Vector3;
31
32namespace VMAP
33{
35 {
36 public:
37 MapRayCallback(ModelInstance const* val, ModelIgnoreFlags ignoreFlags) : prims(val), hit(false), flags(ignoreFlags) { }
38 bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit = true)
39 {
40 bool result = prims[entry].intersectRay(ray, distance, pStopAtFirstHit, flags);
41 if (result)
42 hit = true;
43 return result;
44 }
45 bool didHit() { return hit; }
46 protected:
48 bool hit;
50 };
51
53 {
54 public:
55 LocationInfoCallback(ModelInstance const* val, LocationInfo& info) : prims(val), locInfo(info), result(false) { }
56 void operator()(Vector3 const& point, uint32 entry)
57 {
58#ifdef VMAP_DEBUG
59 TC_LOG_DEBUG("maps", "LocationInfoCallback: trying to intersect '{}'", prims[entry].name);
60#endif
61 if (prims[entry].GetLocationInfo(point, locInfo))
62 result = true;
63 }
64
67 bool result;
68 };
69
70 //=========================================================
71
72 std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY, std::string_view extension)
73 {
74 return Trinity::StringFormat("{:04}/{:04}_{:02}_{:02}.{}", mapID, mapID, tileY, tileX, extension);
75 }
76
77 bool StaticMapTree::GetLocationInfo(Vector3 const& pos, LocationInfo& info) const
78 {
79 LocationInfoCallback intersectionCallBack(iTreeValues.data(), info);
80 iTree.intersectPoint(pos, intersectionCallBack);
81 return intersectionCallBack.result;
82 }
83
84 StaticMapTree::StaticMapTree(uint32 mapID, std::string const& basePath)
85 : iMapID(mapID), iBasePath(basePath)
86 {
87 if (iBasePath.length() > 0 && iBasePath[iBasePath.length() - 1] != '/' && iBasePath[iBasePath.length() - 1] != '\\')
88 {
89 iBasePath.push_back('/');
90 }
91 }
92
93 //=========================================================
96
97 //=========================================================
103 bool StaticMapTree::getIntersectionTime(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
104 {
105 float distance = pMaxDist;
106 MapRayCallback intersectionCallBack(iTreeValues.data(), ignoreFlags);
107 iTree.intersectRay(pRay, intersectionCallBack, distance, pStopAtFirstHit);
108 if (intersectionCallBack.didHit())
109 pMaxDist = distance;
110 return intersectionCallBack.didHit();
111 }
112 //=========================================================
113
114 bool StaticMapTree::isInLineOfSight(Vector3 const& pos1, Vector3 const& pos2, ModelIgnoreFlags ignoreFlag) const
115 {
116 float maxDist = (pos2 - pos1).magnitude();
117 // return false if distance is over max float, in case of cheater teleporting to the end of the universe
118 if (maxDist == std::numeric_limits<float>::max() || !std::isfinite(maxDist))
119 return false;
120
121 // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
122 ASSERT(maxDist < std::numeric_limits<float>::max());
123 // prevent NaN values which can cause BIH intersection to enter infinite loop
124 if (maxDist < 1e-10f)
125 return true;
126 // direction with length of 1
127 G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1) / maxDist);
128 if (getIntersectionTime(ray, maxDist, true, ignoreFlag))
129 return false;
130
131 return true;
132 }
133 //=========================================================
139 bool StaticMapTree::getObjectHitPos(Vector3 const& pPos1, Vector3 const& pPos2, Vector3& pResultHitPos, float pModifyDist) const
140 {
141 bool result = false;
142 float maxDist = (pPos2 - pPos1).magnitude();
143 // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
144 ASSERT(maxDist < std::numeric_limits<float>::max());
145 // prevent NaN values which can cause BIH intersection to enter infinite loop
146 if (maxDist < 1e-10f)
147 {
148 pResultHitPos = pPos2;
149 return false;
150 }
151 Vector3 dir = (pPos2 - pPos1) / maxDist; // direction with length of 1
152 G3D::Ray ray(pPos1, dir);
153 float dist = maxDist;
154 if (getIntersectionTime(ray, dist, false, ModelIgnoreFlags::Nothing))
155 {
156 pResultHitPos = pPos1 + dir * dist;
157 if (pModifyDist < 0)
158 {
159 if ((pResultHitPos - pPos1).magnitude() > -pModifyDist)
160 {
161 pResultHitPos = pResultHitPos + dir * pModifyDist;
162 }
163 else
164 {
165 pResultHitPos = pPos1;
166 }
167 }
168 else
169 {
170 pResultHitPos = pResultHitPos + dir * pModifyDist;
171 }
172 result = true;
173 }
174 else
175 {
176 pResultHitPos = pPos2;
177 result = false;
178 }
179 return result;
180 }
181
182 //=========================================================
183
184 float StaticMapTree::getHeight(Vector3 const& pPos, float maxSearchDist) const
185 {
186 float height = G3D::finf();
187 Vector3 dir = Vector3(0, 0, -1);
188 G3D::Ray ray(pPos, dir); // direction with length of 1
189 float maxDist = maxSearchDist;
190 if (getIntersectionTime(ray, maxDist, false, ModelIgnoreFlags::Nothing))
191 {
192 height = pPos.z - maxDist;
193 }
194 return(height);
195 }
196
198 {
199 using FileDeleter = decltype(Trinity::unique_ptr_deleter<FILE*, &::fclose>());
200
201 std::string Name;
202 std::unique_ptr<FILE, FileDeleter> TileFile;
203 std::unique_ptr<FILE, FileDeleter> SpawnIndicesFile;
205
206 explicit operator bool() const { return TileFile && SpawnIndicesFile; }
207 };
208
209 TileFileOpenResult OpenMapTileFile(std::string const& basePath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2* vm)
210 {
211 TileFileOpenResult result;
212 result.Name = basePath + getTileFileName(mapID, tileX, tileY, "vmtile");
213 result.TileFile.reset(fopen(result.Name.c_str(), "rb"));
214 result.SpawnIndicesFile.reset(fopen((basePath + getTileFileName(mapID, tileX, tileY, "vmtileidx")).c_str(), "rb"));
215 result.UsedMapId = mapID;
216 if (!result.TileFile)
217 {
218 int32 parentMapId = vm->getParentMapId(mapID);
219 while (parentMapId != -1)
220 {
221 result.Name = basePath + getTileFileName(parentMapId, tileX, tileY, "vmtile");
222 result.TileFile.reset(fopen(result.Name.c_str(), "rb"));
223 result.UsedMapId = parentMapId;
224 if (result.TileFile)
225 break;
226
227 parentMapId = vm->getParentMapId(uint32(parentMapId));
228 }
229 }
230
231 return result;
232 }
233
234 //=========================================================
235 LoadResult StaticMapTree::CanLoadMap(const std::string& vmapPath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2* vm)
236 {
237 std::string basePath = vmapPath;
238 if (basePath.length() > 0 && basePath[basePath.length() - 1] != '/' && basePath[basePath.length() - 1] != '\\')
239 basePath.push_back('/');
240 std::string fullname = basePath + VMapManager2::getMapFileName(mapID);
241
242 auto rf = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fullname.c_str(), "rb"));
243 if (!rf)
245
246 char chunk[8];
247 if (!readChunk(rf.get(), chunk, VMAP_MAGIC, 8))
249
250 TileFileOpenResult fileResult = OpenMapTileFile(basePath, mapID, tileX, tileY, vm);
251 if (!fileResult)
253
254 if (!readChunk(fileResult.TileFile.get(), chunk, VMAP_MAGIC, 8))
256
257 return LoadResult::Success;
258 }
259
260 //=========================================================
261
262 LoadResult StaticMapTree::InitMap(std::string const& fname)
263 {
264 TC_LOG_DEBUG("maps", "StaticMapTree::InitMap() : initializing StaticMapTree '{}'", fname);
265 std::string fullname = iBasePath + fname;
266 auto rf = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fullname.c_str(), "rb"));
267 if (!rf)
269
270 char chunk[8];
271
272 if (!readChunk(rf.get(), chunk, VMAP_MAGIC, 8))
274
275 if (!readChunk(rf.get(), chunk, "NODE", 4)
276 || !iTree.readFromFile(rf.get()))
278
279 iTreeValues.resize(iTree.primCount());
280
281 return LoadResult::Success;
282 }
283
284 //=========================================================
285
287 {
288 iTreeValues.clear();
289 iLoadedTiles.clear();
290 }
291
292 //=========================================================
293
295 {
296 if (iTreeValues.empty())
297 {
298 TC_LOG_ERROR("misc", "StaticMapTree::LoadMapTile() : tree has not been initialized [{}, {}]", tileX, tileY);
300 }
302
303 TileFileOpenResult fileResult = OpenMapTileFile(iBasePath, iMapID, tileX, tileY, vm);
304 if (fileResult)
305 {
306 char chunk[8];
307
308 result = LoadResult::Success;
309 if (!readChunk(fileResult.TileFile.get(), chunk, VMAP_MAGIC, 8))
311 if (!readChunk(fileResult.SpawnIndicesFile.get(), chunk, VMAP_MAGIC, 8))
313 uint32 numSpawns = 0;
314 if (result == LoadResult::Success && fread(&numSpawns, sizeof(uint32), 1, fileResult.TileFile.get()) != 1)
316 uint32 numSpawnIndices = 0;
317 if (result == LoadResult::Success && fread(&numSpawnIndices, sizeof(uint32), 1, fileResult.SpawnIndicesFile.get()) != 1)
319 if (numSpawns != numSpawnIndices)
321 for (uint32 i = 0; i < numSpawns && result == LoadResult::Success; ++i)
322 {
323 // read model spawns
324 ModelSpawn spawn;
325 if (ModelSpawn::readFromFile(fileResult.TileFile.get(), spawn))
326 {
327 // acquire model instance
328 std::shared_ptr<WorldModel> model = vm->acquireModelInstance(iBasePath, spawn.name);
329 if (!model)
330 TC_LOG_ERROR("misc", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [{}, {}]", tileX, tileY);
331
332 // update tree
333 uint32 referencedVal = 0;
334 if (fread(&referencedVal, sizeof(uint32), 1, fileResult.SpawnIndicesFile.get()) != 1)
335 {
336 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : invalid tree element (spawn {}) referenced in tile {} by map {}", spawn.ID, fileResult.Name, iMapID);
338 continue;
339 }
340
341 if (referencedVal >= iTreeValues.size())
342 {
343 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{}) referenced in tile {}", referencedVal, iTreeValues.size(), fileResult.Name);
345 continue;
346 }
347
348 if (!iTreeValues[referencedVal].getWorldModel())
349 iTreeValues[referencedVal] = ModelInstance(spawn, std::move(model));
350#ifdef VMAP_DEBUG
351 else
352 {
353 if (iTreeValues[referencedVal].ID != spawn.ID)
354 TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node");
355 else if (iTreeValues[referencedVal].name != spawn.name)
356 TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : name collision on GUID={}", spawn.ID);
357 }
358#endif
359 iTreeValues[referencedVal].AddTileReference();
360 }
361 else
362 {
363 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : cannot read model from file (spawn index {}) referenced in tile {} by map {}", i, fileResult.Name, iMapID);
365 }
366 }
367 iLoadedTiles[packTileID(tileX, tileY)] = true;
368 }
369 else
370 iLoadedTiles[packTileID(tileX, tileY)] = false;
371 TC_METRIC_EVENT("map_events", "LoadMapTile",
372 "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
373 return result;
374 }
375
376 //=========================================================
377
379 {
380 uint32 tileID = packTileID(tileX, tileY);
381 loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
382 if (tile == iLoadedTiles.end())
383 {
384 TC_LOG_ERROR("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:{} X:{} Y:{}", iMapID, tileX, tileY);
385 return;
386 }
387 if (tile->second) // file associated with tile
388 {
389 TileFileOpenResult fileResult = OpenMapTileFile(iBasePath, iMapID, tileX, tileY, vm);
390 if (fileResult)
391 {
392 bool result = true;
393 char chunk[8];
394 if (!readChunk(fileResult.TileFile.get(), chunk, VMAP_MAGIC, 8))
395 result = false;
396 uint32 numSpawns;
397 if (fread(&numSpawns, sizeof(uint32), 1, fileResult.TileFile.get()) != 1)
398 result = false;
399 uint32 numSpawnIndices = 0;
400 if (result && fread(&numSpawnIndices, sizeof(uint32), 1, fileResult.SpawnIndicesFile.get()) != 1)
401 result = false;
402 if (numSpawns != numSpawnIndices)
403 result = false;
404 for (uint32 i = 0; i < numSpawns && result; ++i)
405 {
406 // read model spawns
407 ModelSpawn spawn;
408 if (!ModelSpawn::readFromFile(fileResult.TileFile.get(), spawn))
409 break;
410
411 // update tree
412 uint32 referencedNode = 0;
413 if (fread(&referencedNode, sizeof(uint32), 1, fileResult.SpawnIndicesFile.get()) != 1)
414 {
415 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : invalid tree element (spawn {}) referenced in tile {} by map {}", spawn.ID, fileResult.Name, iMapID);
416 result = false;
417 continue;
418 }
419
420 if (referencedNode >= iTreeValues.size())
421 {
422 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{}) referenced in tile {}", referencedNode, iTreeValues.size(), fileResult.Name);
423 result = false;
424 continue;
425 }
426
427 if (!iTreeValues[referencedNode].getWorldModel())
428 TC_LOG_ERROR("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '{}' (ID:{})", spawn.name, spawn.ID);
429 else if (!iTreeValues[referencedNode].RemoveTileReference())
430 iTreeValues[referencedNode].setUnloaded();
431 }
432 }
433 }
434 iLoadedTiles.erase(tile);
435 TC_METRIC_EVENT("map_events", "UnloadMapTile",
436 "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
437 }
438
440 {
441 models = iTreeValues.data();
442 count = iTreeValues.size();
443 }
444}
int32_t int32
Definition: Define.h:144
uint32_t uint32
Definition: Define.h:148
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition: Log.h:180
#define TC_LOG_ERROR(filterType__, message__,...)
Definition: Log.h:189
#define TC_METRIC_EVENT(category, title, description)
Definition: Metric.h:210
void intersectPoint(G3D::Vector3 const &p, IsectCallback &intersectCallback) const
uint32 primCount() const
void intersectRay(G3D::Ray const &r, RayCallback &intersectCallback, float &maxDist, bool stopAtFirst=false) const
bool readFromFile(FILE *rf)
LocationInfoCallback(ModelInstance const *val, LocationInfo &info)
Definition: MapTree.cpp:55
ModelInstance const * prims
Definition: MapTree.cpp:65
LocationInfo & locInfo
Definition: MapTree.cpp:66
void operator()(Vector3 const &point, uint32 entry)
Definition: MapTree.cpp:56
MapRayCallback(ModelInstance const *val, ModelIgnoreFlags ignoreFlags)
Definition: MapTree.cpp:37
bool operator()(const G3D::Ray &ray, uint32 entry, float &distance, bool pStopAtFirstHit=true)
Definition: MapTree.cpp:38
ModelIgnoreFlags flags
Definition: MapTree.cpp:49
ModelInstance const * prims
Definition: MapTree.cpp:47
bool intersectRay(G3D::Ray const &pRay, float &pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
std::vector< ModelInstance > iTreeValues
Definition: MapTree.h:54
StaticMapTree(uint32 mapID, const std::string &basePath)
Definition: MapTree.cpp:84
~StaticMapTree()
Make sure to call unloadMap() to unregister acquired model references before destroying.
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition: MapTree.cpp:378
bool getObjectHitPos(const G3D::Vector3 &pos1, const G3D::Vector3 &pos2, G3D::Vector3 &pResultHitPos, float pModifyDist) const
Definition: MapTree.cpp:139
static uint32 packTileID(uint32 tileX, uint32 tileY)
Definition: MapTree.h:66
LoadResult InitMap(std::string const &fname)
Definition: MapTree.cpp:262
std::string iBasePath
Definition: MapTree.h:60
bool getIntersectionTime(const G3D::Ray &pRay, float &pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
Definition: MapTree.cpp:103
LoadResult LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition: MapTree.cpp:294
loadedTileMap iLoadedTiles
Definition: MapTree.h:59
static LoadResult CanLoadMap(const std::string &basePath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition: MapTree.cpp:235
void getModelInstances(ModelInstance *&models, uint32 &count)
Definition: MapTree.cpp:439
bool GetLocationInfo(const G3D::Vector3 &pos, LocationInfo &info) const
Definition: MapTree.cpp:77
bool isInLineOfSight(const G3D::Vector3 &pos1, const G3D::Vector3 &pos2, ModelIgnoreFlags ignoreFlags) const
Definition: MapTree.cpp:114
float getHeight(const G3D::Vector3 &pPos, float maxSearchDist) const
Definition: MapTree.cpp:184
int32 getParentMapId(uint32 mapId) const
std::shared_ptr< WorldModel > acquireModelInstance(std::string const &basepath, std::string const &filename)
static std::string getMapFileName(unsigned int mapId)
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:39
bool readChunk(FILE *rf, char *dest, const char *compare, uint32 len)
LoadResult
Definition: IVMapManager.h:35
std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY, std::string_view extension)
Definition: MapTree.cpp:72
TileFileOpenResult OpenMapTileFile(std::string const &basePath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition: MapTree.cpp:209
const char VMAP_MAGIC[]
static bool readFromFile(FILE *rf, ModelSpawn &spawn)
std::string name
Definition: ModelInstance.h:62
decltype(Trinity::unique_ptr_deleter< FILE *, &::fclose >()) FileDeleter
Definition: MapTree.cpp:199
std::unique_ptr< FILE, FileDeleter > TileFile
Definition: MapTree.cpp:202
std::unique_ptr< FILE, FileDeleter > SpawnIndicesFile
Definition: MapTree.cpp:203