Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
MapTree.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#include "MapTree.h"
7#include "ModelInstance.h"
8#include "VMapManager2.h"
9#include "VMapDefinitions.h"
10#include "Log.h"
11#include "Errors.h"
12
13#include <string>
14#include <sstream>
15#include <iomanip>
16#include <limits>
17
18using G3D::Vector3;
19
20namespace VMAP
21{
22
24 {
25 public:
26 explicit MapRayCallback(ModelInstance* val): prims(val), hit(false) { }
27 bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit=true)
28 {
29 bool result = prims[entry].intersectRay(ray, distance, pStopAtFirstHit);
30 if (result)
31 hit = true;
32 return result;
33 }
34 bool didHit() { return hit; }
35 protected:
37 bool hit;
38 };
39
41 {
42 public:
43 explicit AreaInfoCallback(ModelInstance* val) : prims(val) { }
44 void operator()(const Vector3& point, uint32 entry)
45 {
46#ifdef VMAP_DEBUG
47 SF_LOG_DEBUG("maps", "AreaInfoCallback: trying to intersect '%s'", prims[entry].name.c_str());
48#endif
49 prims[entry].intersectPoint(point, aInfo);
50 }
51
54 };
55
57 {
58 public:
59 LocationInfoCallback(ModelInstance* val, LocationInfo &info): prims(val), locInfo(info), result(false) { }
60 void operator()(const Vector3& point, uint32 entry)
61 {
62#ifdef VMAP_DEBUG
63 SF_LOG_DEBUG("maps", "LocationInfoCallback: trying to intersect '%s'", prims[entry].name.c_str());
64#endif
65 if (prims[entry].GetLocationInfo(point, locInfo))
66 result = true;
67 }
68
71 bool result;
72 };
73
74 //=========================================================
75
76 std::string StaticMapTree::getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY)
77 {
78 std::stringstream tilefilename;
79 tilefilename.fill('0');
80 tilefilename << std::setw(4) << mapID << '_';
81 //tilefilename << std::setw(2) << tileX << '_' << std::setw(2) << tileY << ".vmtile";
82 tilefilename << std::setw(2) << tileY << '_' << std::setw(2) << tileX << ".vmtile";
83 return tilefilename.str();
84 }
85
86 bool StaticMapTree::getAreaInfo(Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const
87 {
88 AreaInfoCallback intersectionCallBack(iTreeValues);
89 iTree.intersectPoint(pos, intersectionCallBack);
90 if (intersectionCallBack.aInfo.result)
91 {
92 flags = intersectionCallBack.aInfo.flags;
93 adtId = intersectionCallBack.aInfo.adtId;
94 rootId = intersectionCallBack.aInfo.rootId;
95 groupId = intersectionCallBack.aInfo.groupId;
96 pos.z = intersectionCallBack.aInfo.ground_Z;
97 return true;
98 }
99 return false;
100 }
101
102 bool StaticMapTree::GetLocationInfo(const Vector3 &pos, LocationInfo &info) const
103 {
104 LocationInfoCallback intersectionCallBack(iTreeValues, info);
105 iTree.intersectPoint(pos, intersectionCallBack);
106 return intersectionCallBack.result;
107 }
108
109 StaticMapTree::StaticMapTree(uint32 mapID, const std::string &basePath) :
110 iMapID(mapID), iIsTiled(false), iTreeValues(NULL),
111 iNTreeValues(0), iBasePath(basePath)
112 {
113 if (iBasePath.length() > 0 && iBasePath[iBasePath.length()-1] != '/' && iBasePath[iBasePath.length()-1] != '\\')
114 {
115 iBasePath.push_back('/');
116 }
117 }
118
119 //=========================================================
122 {
123 delete[] iTreeValues;
124 }
125
126 //=========================================================
131
132 bool StaticMapTree::getIntersectionTime(const G3D::Ray& pRay, float &pMaxDist, bool pStopAtFirstHit) const
133 {
134 float distance = pMaxDist;
135 MapRayCallback intersectionCallBack(iTreeValues);
136 iTree.intersectRay(pRay, intersectionCallBack, distance, pStopAtFirstHit);
137 if (intersectionCallBack.didHit())
138 pMaxDist = distance;
139 return intersectionCallBack.didHit();
140 }
141 //=========================================================
142
143 bool StaticMapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2) const
144 {
145 float maxDist = (pos2 - pos1).magnitude();
146 // return false if distance is over max float, in case of cheater teleporting to the end of the universe
147 if (maxDist == std::numeric_limits<float>::max() ||
148 maxDist == std::numeric_limits<float>::infinity())
149 return false;
150
151 // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
152 ASSERT(maxDist < std::numeric_limits<float>::max());
153 // prevent NaN values which can cause BIH intersection to enter infinite loop
154 if (maxDist < 1e-10f)
155 return true;
156 // direction with length of 1
157 G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1)/maxDist);
158 if (getIntersectionTime(ray, maxDist, true))
159 return false;
160
161 return true;
162 }
163 //=========================================================
168
169 bool StaticMapTree::getObjectHitPos(const Vector3& pPos1, const Vector3& pPos2, Vector3& pResultHitPos, float pModifyDist) const
170 {
171 bool result=false;
172 float maxDist = (pPos2 - pPos1).magnitude();
173 // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
174 ASSERT(maxDist < std::numeric_limits<float>::max());
175 // prevent NaN values which can cause BIH intersection to enter infinite loop
176 if (maxDist < 1e-10f)
177 {
178 pResultHitPos = pPos2;
179 return false;
180 }
181 Vector3 dir = (pPos2 - pPos1)/maxDist; // direction with length of 1
182 G3D::Ray ray(pPos1, dir);
183 float dist = maxDist;
184 if (getIntersectionTime(ray, dist, false))
185 {
186 pResultHitPos = pPos1 + dir * dist;
187 if (pModifyDist < 0)
188 {
189 if ((pResultHitPos - pPos1).magnitude() > -pModifyDist)
190 {
191 pResultHitPos = pResultHitPos + dir*pModifyDist;
192 }
193 else
194 {
195 pResultHitPos = pPos1;
196 }
197 }
198 else
199 {
200 pResultHitPos = pResultHitPos + dir*pModifyDist;
201 }
202 result = true;
203 }
204 else
205 {
206 pResultHitPos = pPos2;
207 result = false;
208 }
209 return result;
210 }
211
212 //=========================================================
213
214 float StaticMapTree::getHeight(const Vector3& pPos, float maxSearchDist) const
215 {
216 float height = G3D::inf();
217 Vector3 dir = Vector3(0, 0, -1);
218 G3D::Ray ray(pPos, dir); // direction with length of 1
219 float maxDist = maxSearchDist;
220 if (getIntersectionTime(ray, maxDist, false))
221 {
222 height = pPos.z - maxDist;
223 }
224 return(height);
225 }
226
227 //=========================================================
228
229 bool StaticMapTree::CanLoadMap(const std::string &vmapPath, uint32 mapID, uint32 tileX, uint32 tileY)
230 {
231 std::string basePath = vmapPath;
232 if (basePath.length() > 0 && basePath[basePath.length()-1] != '/' && basePath[basePath.length()-1] != '\\')
233 basePath.push_back('/');
234 std::string fullname = basePath + VMapManager2::getMapFileName(mapID);
235 bool success = true;
236 FILE* rf = fopen(fullname.c_str(), "rb");
237 if (!rf)
238 return false;
240 char tiled;
241 char chunk[8];
242 if (!readChunk(rf, chunk, VMAP_MAGIC, 8) || fread(&tiled, sizeof(char), 1, rf) != 1)
243 {
244 fclose(rf);
245 return false;
246 }
247 if (tiled)
248 {
249 std::string tilefile = basePath + getTileFileName(mapID, tileX, tileY);
250 FILE* tf = fopen(tilefile.c_str(), "rb");
251 if (!tf)
252 success = false;
253 else
254 {
255 if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
256 success = false;
257 fclose(tf);
258 }
259 }
260 fclose(rf);
261 return success;
262 }
263
264 //=========================================================
265
266 bool StaticMapTree::InitMap(const std::string &fname, VMapManager2* vm)
267 {
268 VMAP_DEBUG_LOG("maps", "StaticMapTree::InitMap() : initializing StaticMapTree '%s'", fname.c_str());
269 bool success = false;
270 std::string fullname = iBasePath + fname;
271 FILE* rf = fopen(fullname.c_str(), "rb");
272 if (!rf)
273 return false;
274
275 char chunk[8];
276 char tiled = '\0';
277
278 if (readChunk(rf, chunk, VMAP_MAGIC, 8) && fread(&tiled, sizeof(char), 1, rf) == 1 &&
279 readChunk(rf, chunk, "NODE", 4) && iTree.readFromFile(rf))
280 {
281 iNTreeValues = iTree.primCount();
283 success = readChunk(rf, chunk, "GOBJ", 4);
284 }
285
286 iIsTiled = bool(tiled);
287
288 // global model spawns
289 // only non-tiled maps have them, and if so exactly one (so far at least...)
290 ModelSpawn spawn;
291#ifdef VMAP_DEBUG
292 SF_LOG_DEBUG("maps", "StaticMapTree::InitMap() : map isTiled: %u", static_cast<uint32>(iIsTiled));
293#endif
294 if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
295 {
296 WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name);
297 VMAP_DEBUG_LOG("maps", "StaticMapTree::InitMap() : loading %s", spawn.name.c_str());
298 if (model)
299 {
300 // assume that global model always is the first and only tree value (could be improved...)
301 iTreeValues[0] = ModelInstance(spawn, model);
302 iLoadedSpawns[0] = 1;
303 }
304 else
305 {
306 success = false;
307 VMAP_ERROR_LOG("misc", "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '%s'", spawn.name.c_str());
308 }
309 }
310
311 fclose(rf);
312 return success;
313 }
314
315 //=========================================================
316
318 {
319 for (loadedSpawnMap::iterator i = iLoadedSpawns.begin(); i != iLoadedSpawns.end(); ++i)
320 {
321 iTreeValues[i->first].setUnloaded();
322 for (uint32 refCount = 0; refCount < i->second; ++refCount)
323 vm->releaseModelInstance(iTreeValues[i->first].name);
324 }
325 iLoadedSpawns.clear();
326 iLoadedTiles.clear();
327 }
328
329 //=========================================================
330
332 {
333 if (!iIsTiled)
334 {
335 // currently, core creates grids for all maps, whether it has terrain tiles or not
336 // so we need "fake" tile loads to know when we can unload map geometry
337 iLoadedTiles[packTileID(tileX, tileY)] = false;
338 return true;
339 }
340 if (!iTreeValues)
341 {
342 VMAP_ERROR_LOG("misc", "StaticMapTree::LoadMapTile() : tree has not been initialized [%u, %u]", tileX, tileY);
343 return false;
344 }
345 bool result = true;
346
347 std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
348 FILE* tf = fopen(tilefile.c_str(), "rb");
349 if (tf)
350 {
351 char chunk[8];
352
353 if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
354 result = false;
355 uint32 numSpawns = 0;
356 if (result && fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
357 result = false;
358 for (uint32 i=0; i<numSpawns && result; ++i)
359 {
360 // read model spawns
361 ModelSpawn spawn;
362 result = ModelSpawn::readFromFile(tf, spawn);
363 if (result)
364 {
365 // acquire model instance
366 WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name);
367 if (!model)
368 VMAP_ERROR_LOG("misc", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [%u, %u]", tileX, tileY);
369
370 // update tree
371 uint32 referencedVal;
372
373 if (fread(&referencedVal, sizeof(uint32), 1, tf) == 1)
374 {
375 if (!iLoadedSpawns.count(referencedVal))
376 {
377#ifdef VMAP_DEBUG
378 if (referencedVal > iNTreeValues)
379 {
380 SF_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : invalid tree element (%u/%u)", referencedVal, iNTreeValues);
381 continue;
382 }
383#endif
384 iTreeValues[referencedVal] = ModelInstance(spawn, model);
385 iLoadedSpawns[referencedVal] = 1;
386 }
387 else
388 {
389 ++iLoadedSpawns[referencedVal];
390#ifdef VMAP_DEBUG
391 if (iTreeValues[referencedVal].ID != spawn.ID)
392 SF_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node");
393 else if (iTreeValues[referencedVal].name != spawn.name)
394 SF_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : name collision on GUID=%u", spawn.ID);
395#endif
396 }
397 }
398 else
399 result = false;
400 }
401 }
402 iLoadedTiles[packTileID(tileX, tileY)] = true;
403 fclose(tf);
404 }
405 else
406 iLoadedTiles[packTileID(tileX, tileY)] = false;
407 return result;
408 }
409
410 //=========================================================
411
413 {
414 uint32 tileID = packTileID(tileX, tileY);
415 loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
416 if (tile == iLoadedTiles.end())
417 {
418 VMAP_ERROR_LOG("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:%u X:%u Y:%u", iMapID, tileX, tileY);
419 return;
420 }
421 if (tile->second) // file associated with tile
422 {
423 std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
424 FILE* tf = fopen(tilefile.c_str(), "rb");
425 if (tf)
426 {
427 bool result=true;
428 char chunk[8];
429 if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
430 result = false;
431 uint32 numSpawns;
432 if (fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
433 result = false;
434 for (uint32 i=0; i<numSpawns && result; ++i)
435 {
436 // read model spawns
437 ModelSpawn spawn;
438 result = ModelSpawn::readFromFile(tf, spawn);
439 if (result)
440 {
441 // release model instance
442 vm->releaseModelInstance(spawn.name);
443
444 // update tree
445 uint32 referencedNode;
446
447 if (fread(&referencedNode, sizeof(uint32), 1, tf) != 1)
448 result = false;
449 else
450 {
451 if (!iLoadedSpawns.count(referencedNode))
452 VMAP_ERROR_LOG("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '%s' (ID:%u)", spawn.name.c_str(), spawn.ID);
453 else if (--iLoadedSpawns[referencedNode] == 0)
454 {
455 iTreeValues[referencedNode].setUnloaded();
456 iLoadedSpawns.erase(referencedNode);
457 }
458 }
459 }
460 }
461 fclose(tf);
462 }
463 }
464 iLoadedTiles.erase(tile);
465 }
466}
std::int32_t int32
Definition Define.h:73
std::uint32_t uint32
Definition Define.h:77
#define ASSERT
Definition Errors.h:29
#define SF_LOG_DEBUG(filterType__,...)
Definition Log.h:134
#define VMAP_DEBUG_LOG(FILTER,...)
#define VMAP_ERROR_LOG(FILTER,...)
AreaInfoCallback(ModelInstance *val)
Definition MapTree.cpp:43
void operator()(const Vector3 &point, uint32 entry)
Definition MapTree.cpp:44
ModelInstance * prims
Definition MapTree.cpp:52
ModelInstance * prims
Definition MapTree.cpp:69
LocationInfoCallback(ModelInstance *val, LocationInfo &info)
Definition MapTree.cpp:59
LocationInfo & locInfo
Definition MapTree.cpp:70
void operator()(const Vector3 &point, uint32 entry)
Definition MapTree.cpp:60
ModelInstance * prims
Definition MapTree.cpp:36
bool operator()(const G3D::Ray &ray, uint32 entry, float &distance, bool pStopAtFirstHit=true)
Definition MapTree.cpp:27
MapRayCallback(ModelInstance *val)
Definition MapTree.cpp:26
static bool readFromFile(FILE *rf, ModelSpawn &spawn)
std::string name
bool getIntersectionTime(const G3D::Ray &pRay, float &pMaxDist, bool pStopAtFirstHit) const
Definition MapTree.cpp:132
StaticMapTree(uint32 mapID, const std::string &basePath)
Definition MapTree.cpp:109
bool getAreaInfo(G3D::Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const
Definition MapTree.cpp:86
static bool CanLoadMap(const std::string &basePath, uint32 mapID, uint32 tileX, uint32 tileY)
Definition MapTree.cpp:229
uint32 iNTreeValues
Definition MapTree.h:36
bool LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition MapTree.cpp:331
~StaticMapTree()
Make sure to call unloadMap() to unregister acquired model references before destroying.
Definition MapTree.cpp:121
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
Definition MapTree.cpp:412
static std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY)
Definition MapTree.cpp:76
bool isInLineOfSight(const G3D::Vector3 &pos1, const G3D::Vector3 &pos2) const
Definition MapTree.cpp:143
bool getObjectHitPos(const G3D::Vector3 &pos1, const G3D::Vector3 &pos2, G3D::Vector3 &pResultHitPos, float pModifyDist) const
Definition MapTree.cpp:169
static uint32 packTileID(uint32 tileX, uint32 tileY)
Definition MapTree.h:51
std::string iBasePath
Definition MapTree.h:44
ModelInstance * iTreeValues
Definition MapTree.h:35
loadedTileMap iLoadedTiles
Definition MapTree.h:41
loadedSpawnMap iLoadedSpawns
Definition MapTree.h:43
bool InitMap(const std::string &fname, VMapManager2 *vm)
Definition MapTree.cpp:266
bool GetLocationInfo(const G3D::Vector3 &pos, LocationInfo &info) const
Definition MapTree.cpp:102
float getHeight(const G3D::Vector3 &pPos, float maxSearchDist) const
Definition MapTree.cpp:214
void UnloadMap(VMapManager2 *vm)
Definition MapTree.cpp:317
void releaseModelInstance(const std::string &filename)
WorldModel * acquireModelInstance(const std::string &basepath, const std::string &filename)
static std::string getMapFileName(unsigned int mapId)
bool readChunk(FILE *rf, char *dest, const char *compare, uint32 len)
const char VMAP_MAGIC[]
int32 rootId
Definition MapTree.h:84
int32 groupId
Definition MapTree.h:85
uint32 flags
Definition MapTree.h:82
int32 adtId
Definition MapTree.h:83
float ground_Z
Definition MapTree.h:81