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