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 "Errors.h"
20#include "Log.h"
21#include "Memory.h"
22#include "Metric.h"
23#include "ModelInstance.h"
24#include "VMapDefinitions.h"
25#include "VMapManager.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()(G3D::Ray const& 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 bool StaticMapTree::GetLocationInfo(Vector3 const& pos, LocationInfo& info) const
73 {
74 LocationInfoCallback intersectionCallBack(iTreeValues.data(), info);
75 iTree.intersectPoint(pos, intersectionCallBack);
76 return intersectionCallBack.result;
77 }
78
79 StaticMapTree::StaticMapTree(uint32 mapID, std::string const& basePath)
80 : iMapID(mapID), iBasePath(basePath)
81 {
82 if (!iBasePath.empty() && iBasePath.back() != '/' && iBasePath.back() != '\\')
83 iBasePath.push_back('/');
84 }
85
86 //=========================================================
89
90 //=========================================================
96 bool StaticMapTree::getIntersectionTime(G3D::Ray const& pRay, float& pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
97 {
98 float distance = pMaxDist;
99 MapRayCallback intersectionCallBack(iTreeValues.data(), ignoreFlags);
100 iTree.intersectRay(pRay, intersectionCallBack, distance, pStopAtFirstHit);
101 if (intersectionCallBack.didHit())
102 pMaxDist = distance;
103 return intersectionCallBack.didHit();
104 }
105 //=========================================================
106
107 bool StaticMapTree::isInLineOfSight(Vector3 const& pos1, Vector3 const& pos2, ModelIgnoreFlags ignoreFlag) const
108 {
109 float maxDist = (pos2 - pos1).magnitude();
110 // return false if distance is over max float, in case of cheater teleporting to the end of the universe
111 if (maxDist == std::numeric_limits<float>::max() || !std::isfinite(maxDist))
112 return false;
113
114 // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
115 ASSERT(maxDist < std::numeric_limits<float>::max());
116 // prevent NaN values which can cause BIH intersection to enter infinite loop
117 if (maxDist < 1e-10f)
118 return true;
119 // direction with length of 1
120 G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1) / maxDist);
121 if (getIntersectionTime(ray, maxDist, true, ignoreFlag))
122 return false;
123
124 return true;
125 }
126 //=========================================================
132 bool StaticMapTree::getObjectHitPos(Vector3 const& pPos1, Vector3 const& pPos2, Vector3& pResultHitPos, float pModifyDist) const
133 {
134 bool result = false;
135 float maxDist = (pPos2 - pPos1).magnitude();
136 // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
137 ASSERT(maxDist < std::numeric_limits<float>::max());
138 // prevent NaN values which can cause BIH intersection to enter infinite loop
139 if (maxDist < 1e-10f)
140 {
141 pResultHitPos = pPos2;
142 return false;
143 }
144 Vector3 dir = (pPos2 - pPos1) / maxDist; // direction with length of 1
145 G3D::Ray ray(pPos1, dir);
146 float dist = maxDist;
147 if (getIntersectionTime(ray, dist, false, ModelIgnoreFlags::Nothing))
148 {
149 pResultHitPos = pPos1 + dir * dist;
150 if (pModifyDist < 0)
151 {
152 if ((pResultHitPos - pPos1).magnitude() > -pModifyDist)
153 {
154 pResultHitPos = pResultHitPos + dir * pModifyDist;
155 }
156 else
157 {
158 pResultHitPos = pPos1;
159 }
160 }
161 else
162 {
163 pResultHitPos = pResultHitPos + dir * pModifyDist;
164 }
165 result = true;
166 }
167 else
168 {
169 pResultHitPos = pPos2;
170 result = false;
171 }
172 return result;
173 }
174
175 //=========================================================
176
177 float StaticMapTree::getHeight(Vector3 const& pPos, float maxSearchDist) const
178 {
179 float height = G3D::finf();
180 Vector3 dir = Vector3(0, 0, -1);
181 G3D::Ray ray(pPos, dir); // direction with length of 1
182 float maxDist = maxSearchDist;
183 if (getIntersectionTime(ray, maxDist, false, ModelIgnoreFlags::Nothing))
184 {
185 height = pPos.z - maxDist;
186 }
187 return(height);
188 }
189
191 {
192 using FileDeleter = decltype(Trinity::unique_ptr_deleter<FILE*, &::fclose>());
193
194 std::string Name;
195 std::unique_ptr<FILE, FileDeleter> TileFile;
196 std::unique_ptr<FILE, FileDeleter> SpawnIndicesFile;
198
199 explicit operator bool() const { return TileFile && SpawnIndicesFile; }
200 };
201
202 TileFileOpenResult OpenMapTileFile(std::string const& basePath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager* vm)
203 {
204 TileFileOpenResult result;
205 result.Name = basePath + VMapManager::getTileFileName(mapID, tileX, tileY, "vmtile");
206 result.TileFile.reset(fopen(result.Name.c_str(), "rb"));
207 result.SpawnIndicesFile.reset(fopen((basePath + VMapManager::getTileFileName(mapID, tileX, tileY, "vmtileidx")).c_str(), "rb"));
208 result.UsedMapId = mapID;
209 if (!result.TileFile)
210 {
211 int32 parentMapId = vm->getParentMapId(mapID);
212 while (parentMapId != -1)
213 {
214 result.Name = basePath + VMapManager::getTileFileName(parentMapId, tileX, tileY, "vmtile");
215 result.TileFile.reset(fopen(result.Name.c_str(), "rb"));
216 result.UsedMapId = parentMapId;
217 if (result.TileFile)
218 break;
219
220 parentMapId = vm->getParentMapId(uint32(parentMapId));
221 }
222 }
223
224 return result;
225 }
226
227 //=========================================================
228 LoadResult StaticMapTree::CanLoadMap(std::string const& vmapPath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager* vm)
229 {
230 std::string basePath = vmapPath;
231 if (!basePath.empty() && basePath.back() != '/' && basePath.back() != '\\')
232 basePath.push_back('/');
233
234 std::string fullname = basePath + VMapManager::getMapFileName(mapID);
235
236 auto rf = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fullname.c_str(), "rb"));
237 if (!rf)
239
240 char chunk[8];
241 if (!readChunk(rf.get(), chunk, VMAP_MAGIC, 8))
243
244 TileFileOpenResult fileResult = OpenMapTileFile(basePath, mapID, tileX, tileY, vm);
245 if (!fileResult)
247
248 if (!readChunk(fileResult.TileFile.get(), chunk, VMAP_MAGIC, 8))
250
251 return LoadResult::Success;
252 }
253
254 //=========================================================
255
256 LoadResult StaticMapTree::InitMap(std::string const& fname)
257 {
258 TC_LOG_DEBUG("maps", "StaticMapTree::InitMap() : initializing StaticMapTree '{}'", fname);
259 std::string fullname = iBasePath + fname;
260 auto rf = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fullname.c_str(), "rb"));
261 if (!rf)
263
264 char chunk[8];
265
266 if (!readChunk(rf.get(), chunk, VMAP_MAGIC, 8))
268
269 if (!readChunk(rf.get(), chunk, "NODE", 4)
270 || !iTree.readFromFile(rf.get()))
272
273 iTreeValues.resize(iTree.primCount());
274
275 return LoadResult::Success;
276 }
277
278 //=========================================================
279
281 {
282 iTreeValues.clear();
283 iLoadedTiles.clear();
284 }
285
286 //=========================================================
287
289 {
290 if (iTreeValues.empty())
291 {
292 TC_LOG_ERROR("misc", "StaticMapTree::LoadMapTile() : tree has not been initialized [{}, {}]", tileX, tileY);
294 }
296
297 std::vector<uint32>& tileReferenceVals = iLoadedTiles[packTileID(tileX, tileY)];
298 TileFileOpenResult fileResult = OpenMapTileFile(iBasePath, iMapID, tileX, tileY, vm);
299 if (fileResult)
300 {
301 char chunk[8];
302
303 result = LoadResult::Success;
304 if (!readChunk(fileResult.TileFile.get(), chunk, VMAP_MAGIC, 8))
306 if (!readChunk(fileResult.SpawnIndicesFile.get(), chunk, VMAP_MAGIC, 8))
308 uint32 numSpawns = 0;
309 if (result == LoadResult::Success && fread(&numSpawns, sizeof(uint32), 1, fileResult.TileFile.get()) != 1)
311 uint32 numSpawnIndices = 0;
312 if (result == LoadResult::Success && fread(&numSpawnIndices, sizeof(uint32), 1, fileResult.SpawnIndicesFile.get()) != 1)
314 if (numSpawns != numSpawnIndices)
316 tileReferenceVals.reserve(numSpawns);
317 for (uint32 i = 0; i < numSpawns && result == LoadResult::Success; ++i)
318 {
319 // read model spawns
320 ModelSpawn spawn;
321 if (ModelSpawn::readFromFile(fileResult.TileFile.get(), spawn))
322 {
323 // update tree
324 uint32 referencedVal = 0;
325 if (fread(&referencedVal, sizeof(uint32), 1, fileResult.SpawnIndicesFile.get()) != 1)
326 {
327 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : invalid tree element (spawn {}) referenced in tile {} by map {}", spawn.ID, fileResult.Name, iMapID);
329 continue;
330 }
331
332 if (referencedVal >= iTreeValues.size())
333 {
334 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{}) referenced in tile {}", referencedVal, iTreeValues.size(), fileResult.Name);
336 continue;
337 }
338
339 if (spawn.flags & MOD_PATH_ONLY && !vm->LoadPathOnlyModels)
340 continue;
341
342 // acquire model instance
343 std::shared_ptr<WorldModel> model = vm->acquireModelInstance(iBasePath, spawn.name);
344 if (!model)
345 {
346 TC_LOG_ERROR("misc", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [{}, {}]", tileX, tileY);
347 continue;
348 }
349
350 if (!iTreeValues[referencedVal].getWorldModel())
351 iTreeValues[referencedVal] = ModelInstance(spawn, std::move(model));
352#ifdef VMAP_DEBUG
353 else
354 {
355 if (iTreeValues[referencedVal].ID != spawn.ID)
356 TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node");
357 else if (iTreeValues[referencedVal].name != spawn.name)
358 TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : name collision on GUID={}", spawn.ID);
359 }
360#endif
361 iTreeValues[referencedVal].AddTileReference();
362 tileReferenceVals.push_back(referencedVal);
363 }
364 else
365 {
366 TC_LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : cannot read model from file (spawn index {}) referenced in tile {} by map {}", i, fileResult.Name, iMapID);
368 }
369 }
370 }
371
372 TC_METRIC_EVENT("map_events", "LoadMapTile", Trinity::StringFormat("Map: {} TileX: {} TileY: {}", iMapID, tileX, tileY));
373 return result;
374 }
375
376 //=========================================================
377
379 {
380 uint32 tileID = packTileID(tileX, tileY);
381 auto tile = iLoadedTiles.extract(tileID);
382 if (!tile)
383 {
384 TC_LOG_ERROR("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:{} X:{} Y:{}", iMapID, tileX, tileY);
385 return;
386 }
387
388 for (uint32 referencedVal : tile.mapped())
389 {
390 if (!iTreeValues[referencedVal].getWorldModel())
391 {
392 TC_LOG_ERROR("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model ID: {} - Map:{} X:{} Y:{}",
393 iTreeValues[referencedVal].ID, iMapID, tileX, tileY);
394 continue;
395 }
396
397 if (!iTreeValues[referencedVal].RemoveTileReference())
398 iTreeValues[referencedVal].setUnloaded();
399 }
400
401 TC_METRIC_EVENT("map_events", "UnloadMapTile", Trinity::StringFormat("Map: {} TileX: {} TileY: {}", iMapID, tileX, tileY));
402 }
403
404 std::span<ModelInstance const> StaticMapTree::getModelInstances() const
405 {
406 return iTreeValues;
407 }
408}
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
#define ASSERT
Definition Errors.h:80
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_METRIC_EVENT(category, title, description)
Definition Metric.h:201
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
bool operator()(G3D::Ray const &ray, uint32 entry, float &distance, bool pStopAtFirstHit=true)
Definition MapTree.cpp:38
MapRayCallback(ModelInstance const *val, ModelIgnoreFlags ignoreFlags)
Definition MapTree.cpp:37
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
void UnloadMapTile(uint32 tileX, uint32 tileY)
Definition MapTree.cpp:378
std::vector< ModelInstance > iTreeValues
Definition MapTree.h:55
~StaticMapTree()
Make sure to call unloadMap() to unregister acquired model references before destroying.
std::span< ModelInstance const > getModelInstances() const
Definition MapTree.cpp:404
static uint32 packTileID(uint32 tileX, uint32 tileY)
Definition MapTree.h:67
bool GetLocationInfo(G3D::Vector3 const &pos, LocationInfo &info) const
Definition MapTree.cpp:72
LoadResult InitMap(std::string const &fname)
Definition MapTree.cpp:256
std::string iBasePath
Definition MapTree.h:61
StaticMapTree(uint32 mapID, std::string const &basePath)
Definition MapTree.cpp:79
loadedTileMap iLoadedTiles
Definition MapTree.h:60
LoadResult LoadMapTile(uint32 tileX, uint32 tileY, VMapManager *vm)
Definition MapTree.cpp:288
bool getIntersectionTime(G3D::Ray const &pRay, float &pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
Definition MapTree.cpp:96
float getHeight(G3D::Vector3 const &pPos, float maxSearchDist) const
Definition MapTree.cpp:177
static LoadResult CanLoadMap(std::string const &basePath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager *vm)
Definition MapTree.cpp:228
bool isInLineOfSight(G3D::Vector3 const &pos1, G3D::Vector3 const &pos2, ModelIgnoreFlags ignoreFlags) const
Definition MapTree.cpp:107
bool getObjectHitPos(G3D::Vector3 const &pos1, G3D::Vector3 const &pos2, G3D::Vector3 &pResultHitPos, float pModifyDist) const
Definition MapTree.cpp:132
static std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY, std::string_view extension)
std::shared_ptr< WorldModel > acquireModelInstance(std::string const &basepath, std::string const &filename)
int32 getParentMapId(uint32 mapId) const
static std::string getMapFileName(uint32 mapId)
std::string StringFormat(FormatString< Args... > fmt, Args &&... args) noexcept
Default TC string format function.
bool readChunk(FILE *rf, char *dest, const char *compare, uint32 len)
@ MOD_PATH_ONLY
TileFileOpenResult OpenMapTileFile(std::string const &basePath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager *vm)
Definition MapTree.cpp:202
const char VMAP_MAGIC[]
static bool readFromFile(FILE *rf, ModelSpawn &spawn)
std::string name
decltype(Trinity::unique_ptr_deleter< FILE *, &::fclose >()) FileDeleter
Definition MapTree.cpp:192
std::unique_ptr< FILE, FileDeleter > TileFile
Definition MapTree.cpp:195
std::unique_ptr< FILE, FileDeleter > SpawnIndicesFile
Definition MapTree.cpp:196