TrinityCore
Loading...
Searching...
No Matches
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 "ModelInstance.h"
20#include "VMapManager2.h"
21#include "VMapDefinitions.h"
22#include "Log.h"
23#include "Errors.h"
24#include "Metric.h"
25
26#include <string>
27#include <sstream>
28#include <iomanip>
29#include <limits>
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(3) << 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), iIsTiled(false), iTreeValues(nullptr),
92 iNTreeValues(0), iBasePath(basePath)
93 {
94 if (iBasePath.length() > 0 && iBasePath[iBasePath.length()-1] != '/' && iBasePath[iBasePath.length()-1] != '\\')
95 {
96 iBasePath.push_back('/');
97 }
98 }
99
100 //=========================================================
103 {
104 delete[] iTreeValues;
105 }
106
107 //=========================================================
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
206 //=========================================================
207 LoadResult StaticMapTree::CanLoadMap(const std::string &vmapPath, uint32 mapID, uint32 tileX, uint32 tileY)
208 {
209 std::string basePath = vmapPath;
210 if (basePath.length() > 0 && basePath[basePath.length()-1] != '/' && basePath[basePath.length()-1] != '\\')
211 basePath.push_back('/');
212 std::string fullname = basePath + VMapManager2::getMapFileName(mapID);
213
215
216 FILE* rf = fopen(fullname.c_str(), "rb");
217 if (!rf)
219
220 char tiled;
221 char chunk[8];
222 if (!readChunk(rf, chunk, VMAP_MAGIC, 8) || fread(&tiled, sizeof(char), 1, rf) != 1)
223 {
224 fclose(rf);
226 }
227 if (tiled)
228 {
229 std::string tilefile = basePath + getTileFileName(mapID, tileX, tileY);
230 FILE* tf = fopen(tilefile.c_str(), "rb");
231 if (!tf)
233 else
234 {
235 if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
237 fclose(tf);
238 }
239 }
240 fclose(rf);
241 return result;
242 }
243
244 //=========================================================
245
246 bool StaticMapTree::InitMap(const std::string &fname, VMapManager2* vm)
247 {
248 TC_LOG_DEBUG("maps", "StaticMapTree::InitMap() : initializing StaticMapTree '{}'", fname);
249 bool success = false;
250 std::string fullname = iBasePath + fname;
251 FILE* rf = fopen(fullname.c_str(), "rb");
252 if (!rf)
253 return false;
254
255 char chunk[8];
256 char tiled = '\0';
257
258 if (readChunk(rf, chunk, VMAP_MAGIC, 8) && fread(&tiled, sizeof(char), 1, rf) == 1 &&
259 readChunk(rf, chunk, "NODE", 4) && iTree.readFromFile(rf))
260 {
263 success = readChunk(rf, chunk, "GOBJ", 4);
264 }
265
266 iIsTiled = tiled != '\0';
267
268 // global model spawns
269 // only non-tiled maps have them, and if so exactly one (so far at least...)
270 ModelSpawn spawn;
271#ifdef VMAP_DEBUG
272 TC_LOG_DEBUG("maps", "StaticMapTree::InitMap() : map isTiled: {}", static_cast<uint32>(iIsTiled));
273#endif
274 if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
275 {
276 WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags);
277 TC_LOG_DEBUG("maps", "StaticMapTree::InitMap() : loading {}", spawn.name);
278 if (model)
279 {
280 // assume that global model always is the first and only tree value (could be improved...)
281 iTreeValues[0] = ModelInstance(spawn, model);
282 iLoadedSpawns[0] = 1;
283 }
284 else
285 {
286 success = false;
287 TC_LOG_ERROR("misc", "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '{}'", spawn.name);
288 }
289 }
290
291 fclose(rf);
292 return success;
293 }
294
295 //=========================================================
296
298 {
299 for (std::pair<uint32 const, uint32>& iLoadedSpawn : iLoadedSpawns)
300 {
301 iTreeValues[iLoadedSpawn.first].setUnloaded();
302 for (uint32 refCount = 0; refCount < iLoadedSpawn.second; ++refCount)
303 vm->releaseModelInstance(iTreeValues[iLoadedSpawn.first].name);
304 }
305 iLoadedSpawns.clear();
306 iLoadedTiles.clear();
307 }
308
309 //=========================================================
310
312 {
313 if (!iIsTiled)
314 {
315 // currently, core creates grids for all maps, whether it has terrain tiles or not
316 // so we need "fake" tile loads to know when we can unload map geometry
317 iLoadedTiles[packTileID(tileX, tileY)] = false;
318 return true;
319 }
320 if (!iTreeValues)
321 {
322 TC_LOG_ERROR("misc", "StaticMapTree::LoadMapTile() : tree has not been initialized [{}, {}]", tileX, tileY);
323 return false;
324 }
325 bool result = true;
326
327 std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
328 FILE* tf = fopen(tilefile.c_str(), "rb");
329 if (tf)
330 {
331 char chunk[8];
332
333 if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
334 result = false;
335 uint32 numSpawns = 0;
336 if (result && fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
337 result = false;
338 for (uint32 i=0; i<numSpawns && result; ++i)
339 {
340 // read model spawns
341 ModelSpawn spawn;
342 result = ModelSpawn::readFromFile(tf, spawn);
343 if (result)
344 {
345 // acquire model instance
346 WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags);
347 if (!model)
348 TC_LOG_ERROR("misc", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [{}, {}]", tileX, tileY);
349
350 // update tree
351 uint32 referencedVal;
352
353 if (fread(&referencedVal, sizeof(uint32), 1, tf) == 1)
354 {
355 if (!iLoadedSpawns.count(referencedVal))
356 {
357 if (referencedVal > iNTreeValues)
358 {
359 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{}) referenced in tile {}", referencedVal, iNTreeValues, tilefile);
360 continue;
361 }
362
363 iTreeValues[referencedVal] = ModelInstance(spawn, model);
364 iLoadedSpawns[referencedVal] = 1;
365 }
366 else
367 {
368 ++iLoadedSpawns[referencedVal];
369#ifdef VMAP_DEBUG
370 if (iTreeValues[referencedVal].ID != spawn.ID)
371 TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node");
372 else if (iTreeValues[referencedVal].name != spawn.name)
373 TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : name collision on GUID={}", spawn.ID);
374#endif
375 }
376 }
377 else
378 result = false;
379 }
380 }
381 iLoadedTiles[packTileID(tileX, tileY)] = true;
382 fclose(tf);
383 }
384 else
385 iLoadedTiles[packTileID(tileX, tileY)] = false;
386 TC_METRIC_EVENT("map_events", "LoadMapTile",
387 "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
388 return result;
389 }
390
391 //=========================================================
392
394 {
395 uint32 tileID = packTileID(tileX, tileY);
396 loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
397 if (tile == iLoadedTiles.end())
398 {
399 TC_LOG_ERROR("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:{} X:{} Y:{}", iMapID, tileX, tileY);
400 return;
401 }
402 if (tile->second) // file associated with tile
403 {
404 std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
405 FILE* tf = fopen(tilefile.c_str(), "rb");
406 if (tf)
407 {
408 bool result = true;
409 char chunk[8];
410 if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
411 result = false;
412 uint32 numSpawns;
413 if (fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
414 result = false;
415 for (uint32 i=0; i<numSpawns && result; ++i)
416 {
417 // read model spawns
418 ModelSpawn spawn;
419 result = ModelSpawn::readFromFile(tf, spawn);
420 if (result)
421 {
422 // release model instance
423 vm->releaseModelInstance(spawn.name);
424
425 // update tree
426 uint32 referencedNode;
427
428 if (fread(&referencedNode, sizeof(uint32), 1, tf) != 1)
429 result = false;
430 else
431 {
432 if (!iLoadedSpawns.count(referencedNode))
433 TC_LOG_ERROR("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '{}' (ID:{})", spawn.name, spawn.ID);
434 else if (--iLoadedSpawns[referencedNode] == 0)
435 {
436 iTreeValues[referencedNode].setUnloaded();
437 iLoadedSpawns.erase(referencedNode);
438 }
439 }
440 }
441 }
442 fclose(tf);
443 }
444 }
445 iLoadedTiles.erase(tile);
446 TC_METRIC_EVENT("map_events", "UnloadMapTile",
447 "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
448 }
449
451 {
452 models = iTreeValues;
453 count = iNTreeValues;
454 }
455}
uint32_t uint32
Definition Define.h:133
#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:195
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
bool intersectRay(G3D::Ray const &pRay, float &pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
static bool readFromFile(FILE *rf, ModelSpawn &spawn)
std::string name
static LoadResult CanLoadMap(const std::string &basePath, uint32 mapID, uint32 tileX, uint32 tileY)
Definition MapTree.cpp:207
StaticMapTree(uint32 mapID, const std::string &basePath)
Definition MapTree.cpp:90
uint32 iNTreeValues
Definition MapTree.h:57
bool LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition MapTree.cpp:311
~StaticMapTree()
Make sure to call unloadMap() to unregister acquired model references before destroying.
Definition MapTree.cpp:102
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition MapTree.cpp:393
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:72
std::string iBasePath
Definition MapTree.h:65
bool getIntersectionTime(const G3D::Ray &pRay, float &pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
Definition MapTree.cpp:112
ModelInstance * iTreeValues
Definition MapTree.h:56
loadedTileMap iLoadedTiles
Definition MapTree.h:62
loadedSpawnMap iLoadedSpawns
Definition MapTree.h:64
bool InitMap(const std::string &fname, VMapManager2 *vm)
Definition MapTree.cpp:246
void getModelInstances(ModelInstance *&models, uint32 &count)
Definition MapTree.cpp:450
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:297
void releaseModelInstance(const std::string &filename)
WorldModel * acquireModelInstance(const std::string &basepath, const std::string &filename, uint32 flags=0)
static std::string getMapFileName(unsigned int mapId)
bool readChunk(FILE *rf, char *dest, const char *compare, uint32 len)
const char VMAP_MAGIC[]