Legends-of-Azeroth-Pandaria-5.4.8

Форк
0
483 строки · 17.9 Кб
1
/*
2
* This file is part of the Pandaria 5.4.8 Project. See THANKS 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 "ModelIgnoreFlags.h"
21
#include "VMapManager2.h"
22
#include "VMapDefinitions.h"
23
#include "Log.h"
24
#include "Errors.h"
25

26
#include <string>
27
#include <sstream>
28
#include <iomanip>
29
#include <limits>
30

31
using G3D::Vector3;
32

33
namespace VMAP
34
{
35
    class MapRayCallback
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:
48
        ModelInstance* prims;
49
        bool hit;
50
        ModelIgnoreFlags flags;
51
    };
52

53
    class AreaInfoCallback
54
    {
55
        public:
56
            AreaInfoCallback(ModelInstance* val): prims(val) { }
57
            void operator()(Vector3 const& point, uint32 entry)
58
            {
59
#ifdef VMAP_DEBUG
60
                TC_LOG_DEBUG("maps", "AreaInfoCallback: trying to intersect '%s'", prims[entry].name.c_str());
61
#endif
62
                prims[entry].intersectPoint(point, aInfo);
63
            }
64

65
            ModelInstance* prims;
66
            AreaInfo aInfo;
67
    };
68

69
    class LocationInfoCallback
70
    {
71
        public:
72
            LocationInfoCallback(ModelInstance* val, LocationInfo &info): prims(val), locInfo(info), result(false) { }
73
            void operator()(Vector3 const& point, uint32 entry)
74
            {
75
#ifdef VMAP_DEBUG
76
                TC_LOG_DEBUG("maps", "LocationInfoCallback: trying to intersect '%s'", prims[entry].name.c_str());
77
#endif
78
                if (prims[entry].GetLocationInfo(point, locInfo))
79
                    result = true;
80
            }
81

82
            ModelInstance* prims;
83
            LocationInfo &locInfo;
84
            bool result;
85
    };
86

87
    //=========================================================
88

89
    std::string StaticMapTree::getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY)
90
    {
91
        std::stringstream tilefilename;
92
        tilefilename.fill('0');
93
        tilefilename << std::setw(4) << mapID << '_';
94
        //tilefilename << std::setw(2) << tileX << '_' << std::setw(2) << tileY << ".vmtile";
95
        tilefilename << std::setw(2) << tileY << '_' << std::setw(2) << tileX << ".vmtile";
96
        return tilefilename.str();
97
    }
98

99
    bool StaticMapTree::getAreaInfo(Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const
100
    {
101
        AreaInfoCallback intersectionCallBack(iTreeValues);
102
        iTree.intersectPoint(pos, intersectionCallBack);
103
        if (intersectionCallBack.aInfo.result)
104
        {
105
            flags = intersectionCallBack.aInfo.flags;
106
            adtId = intersectionCallBack.aInfo.adtId;
107
            rootId = intersectionCallBack.aInfo.rootId;
108
            groupId = intersectionCallBack.aInfo.groupId;
109
            pos.z = intersectionCallBack.aInfo.ground_Z;
110
            return true;
111
        }
112
        return false;
113
    }
114

115
    bool StaticMapTree::GetLocationInfo(Vector3 const& pos, LocationInfo &info) const
116
    {
117
        LocationInfoCallback intersectionCallBack(iTreeValues, info);
118
        iTree.intersectPoint(pos, intersectionCallBack);
119
        return intersectionCallBack.result;
120
    }
121

122
    StaticMapTree::StaticMapTree(uint32 mapID, std::string const& basePath) :
123
        iMapID(mapID), iIsTiled(false), iTreeValues(nullptr),
124
        iNTreeValues(0), iBasePath(basePath)
125
    {
126
        if (iBasePath.length() > 0 && iBasePath[iBasePath.length()-1] != '/' && iBasePath[iBasePath.length()-1] != '\\')
127
        {
128
            iBasePath.push_back('/');
129
        }
130
    }
131

132
    //=========================================================
133
    //! Make sure to call unloadMap() to unregister acquired model references before destroying
134
    StaticMapTree::~StaticMapTree()
135
    {
136
        delete[] iTreeValues;
137
    }
138

139
    //=========================================================
140
    /**
141
    If intersection is found within pMaxDist, sets pMaxDist to intersection distance and returns true.
142
    Else, pMaxDist is not modified and returns false;
143
    */
144
    bool StaticMapTree::getIntersectionTime(const G3D::Ray& pRay, float &pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
145
    {
146
        float distance = pMaxDist;
147
        MapRayCallback intersectionCallBack(iTreeValues, ignoreFlags);
148
        iTree.intersectRay(pRay, intersectionCallBack, distance, pStopAtFirstHit);
149
        if (intersectionCallBack.didHit())
150
            pMaxDist = distance;
151
        return intersectionCallBack.didHit();
152
    }
153

154
    //=========================================================
155
    bool StaticMapTree::isInLineOfSight(Vector3 const& pos1, Vector3 const& pos2, ModelIgnoreFlags ignoreFlag) const
156
    {
157
        float maxDist = (pos2 - pos1).magnitude();
158
        // return false if distance is over max float, in case of cheater teleporting to the end of the universe
159
        if (maxDist == std::numeric_limits<float>::max() || !std::isfinite(maxDist))
160
            return false;
161

162
        // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
163
        ASSERT(maxDist < std::numeric_limits<float>::max());
164
        // prevent NaN values which can cause BIH intersection to enter infinite loop
165
        if (maxDist < 1e-10f)
166
            return true;
167
        // direction with length of 1
168
        G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1)/maxDist);
169
        if (getIntersectionTime(ray, maxDist, true, ignoreFlag))
170
            return false;
171

172
        return true;
173
    }
174
    //=========================================================
175
    /**
176
    When moving from pos1 to pos2 check if we hit an object. Return true and the position if we hit one
177
    Return the hit pos or the original dest pos
178
    */
179

180
    bool StaticMapTree::getObjectHitPos(Vector3 const& pPos1, Vector3 const& pPos2, Vector3& pResultHitPos, float pModifyDist) const
181
    {
182
        bool result = false;
183
        float maxDist = (pPos2 - pPos1).magnitude();
184
        // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
185
        ASSERT(maxDist < std::numeric_limits<float>::max());
186
        // prevent NaN values which can cause BIH intersection to enter infinite loop
187
        if (maxDist < 1e-10f)
188
        {
189
            pResultHitPos = pPos2;
190
            return false;
191
        }
192
        Vector3 dir = (pPos2 - pPos1)/maxDist;              // direction with length of 1
193
        G3D::Ray ray(pPos1, dir);
194
        float dist = maxDist;
195
        if (getIntersectionTime(ray, dist, false, ModelIgnoreFlags::Nothing))
196
        {
197
            pResultHitPos = pPos1 + dir * dist;
198
            if (pModifyDist < 0)
199
            {
200
                if ((pResultHitPos - pPos1).magnitude() > -pModifyDist)
201
                {
202
                    pResultHitPos = pResultHitPos + dir*pModifyDist;
203
                }
204
                else
205
                {
206
                    pResultHitPos = pPos1;
207
                }
208
            }
209
            else
210
            {
211
                pResultHitPos = pResultHitPos + dir*pModifyDist;
212
            }
213
            result = true;
214
        }
215
        else
216
        {
217
            pResultHitPos = pPos2;
218
            result = false;
219
        }
220
        return result;
221
    }
222

223
    //=========================================================
224

225
    float StaticMapTree::getHeight(Vector3 const& pPos, float maxSearchDist) const
226
    {
227
        float height = G3D::finf();
228
        Vector3 dir = Vector3(0, 0, -1);
229
        G3D::Ray ray(pPos, dir);   // direction with length of 1
230
        float maxDist = maxSearchDist;
231
        if (getIntersectionTime(ray, maxDist, false, ModelIgnoreFlags::Nothing))
232
        {
233
            height = pPos.z - maxDist;
234
        }
235
        return(height);
236
    }
237

238
    //=========================================================
239
    LoadResult StaticMapTree::CanLoadMap(const std::string &vmapPath, uint32 mapID, uint32 tileX, uint32 tileY)
240
    {
241
        std::string basePath = vmapPath;
242
        if (basePath.length() > 0 && basePath[basePath.length()-1] != '/' && basePath[basePath.length()-1] != '\\')
243
            basePath.push_back('/');
244
        std::string fullname = basePath + VMapManager2::getMapFileName(mapID);
245
        LoadResult result = LoadResult::Success;
246

247
        FILE* rf = fopen(fullname.c_str(), "rb");
248
        if (!rf)
249
            return LoadResult::FileNotFound;
250

251
        char tiled;
252
        char chunk[8];
253
        if (!readChunk(rf, chunk, VMAP_MAGIC, 8) || fread(&tiled, sizeof(char), 1, rf) != 1)
254
        {
255
            fclose(rf);
256
            return LoadResult::VersionMismatch;
257
        }
258
        if (tiled)
259
        {
260
            std::string tilefile = basePath + getTileFileName(mapID, tileX, tileY);
261
            FILE* tf = fopen(tilefile.c_str(), "rb");
262
            if (!tf)
263
                result = LoadResult::FileNotFound;
264
            else
265
            {
266
                if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
267
                    result = LoadResult::VersionMismatch;
268
                fclose(tf);
269
            }
270
        }
271
        fclose(rf);
272
        return result;
273
    }
274

275
    //=========================================================
276

277
    bool StaticMapTree::InitMap(const std::string &fname, VMapManager2* vm)
278
    {
279
        VMAP_DEBUG_LOG("maps", "StaticMapTree::InitMap() : initializing StaticMapTree '%s'", fname.c_str());
280
        bool success = false;
281
        std::string fullname = iBasePath + fname;
282
        FILE* rf = fopen(fullname.c_str(), "rb");
283
        if (!rf)
284
            return false;
285

286
        char chunk[8];
287
        char tiled = '\0';
288

289
        if (readChunk(rf, chunk, VMAP_MAGIC, 8) && fread(&tiled, sizeof(char), 1, rf) == 1 &&
290
            readChunk(rf, chunk, "NODE", 4) && iTree.readFromFile(rf))
291
        {
292
            iNTreeValues = iTree.primCount();
293
            iTreeValues = new ModelInstance[iNTreeValues];
294
            success = readChunk(rf, chunk, "GOBJ", 4);
295
        }
296

297
        iIsTiled = tiled != '\0';
298

299
        // global model spawns
300
        // only non-tiled maps have them, and if so exactly one (so far at least...)
301
        ModelSpawn spawn;
302
#ifdef VMAP_DEBUG
303
        TC_LOG_DEBUG("maps", "StaticMapTree::InitMap() : map isTiled: %u", static_cast<uint32>(iIsTiled));
304
#endif
305
        if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
306
        {
307
            WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags);
308
            VMAP_DEBUG_LOG("maps", "StaticMapTree::InitMap() : loading %s", spawn.name.c_str());
309
            if (model)
310
            {
311
                // assume that global model always is the first and only tree value (could be improved...)
312
                iTreeValues[0] = ModelInstance(spawn, model);
313
                iLoadedSpawns[0] = 1;
314
            }
315
            else
316
            {
317
                success = false;
318
                VMAP_ERROR_LOG("misc", "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '%s'", spawn.name.c_str());
319
            }
320
        }
321

322
        fclose(rf);
323
        return success;
324
    }
325

326
    //=========================================================
327

328
    void StaticMapTree::UnloadMap(VMapManager2* vm)
329
    {
330
        for (std::pair<uint32 const, uint32>& iLoadedSpawn : iLoadedSpawns)
331
        {
332
            iTreeValues[iLoadedSpawn.first].setUnloaded();
333
            for (uint32 refCount = 0; refCount < iLoadedSpawn.second; ++refCount)
334
                vm->releaseModelInstance(iTreeValues[iLoadedSpawn.first].name);
335
        }
336
        iLoadedSpawns.clear();
337
        iLoadedTiles.clear();
338
    }
339

340
    //=========================================================
341

342
    bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm)
343
    {
344
        if (!iIsTiled)
345
        {
346
            // currently, core creates grids for all maps, whether it has terrain tiles or not
347
            // so we need "fake" tile loads to know when we can unload map geometry
348
            iLoadedTiles[packTileID(tileX, tileY)] = false;
349
            return true;
350
        }
351
        if (!iTreeValues)
352
        {
353
            VMAP_ERROR_LOG("misc", "StaticMapTree::LoadMapTile() : tree has not been initialized [%u, %u]", tileX, tileY);
354
            return false;
355
        }
356
        bool result = true;
357

358
        std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
359
        FILE* tf = fopen(tilefile.c_str(), "rb");
360
        if (tf)
361
        {
362
            char chunk[8];
363

364
            if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
365
                result = false;
366
            uint32 numSpawns = 0;
367
            if (result && fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
368
                result = false;
369
            for (uint32 i=0; i<numSpawns && result; ++i)
370
            {
371
                // read model spawns
372
                ModelSpawn spawn;
373
                result = ModelSpawn::readFromFile(tf, spawn);
374
                if (result)
375
                {
376
                    // acquire model instance
377
                    WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags);
378
                    if (!model)
379
                        VMAP_ERROR_LOG("misc", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [%u, %u]", tileX, tileY);
380

381
                    // update tree
382
                    uint32 referencedVal;
383

384
                    if (fread(&referencedVal, sizeof(uint32), 1, tf) == 1)
385
                    {
386
                        if (!iLoadedSpawns.count(referencedVal))
387
                        {
388
                            if (referencedVal > iNTreeValues)
389
                            {
390
                                VMAP_ERROR_LOG("maps", "StaticMapTree::LoadMapTile() : invalid tree element (%u/%u) referenced in tile %s", referencedVal, iNTreeValues, tilefile.c_str());
391
                                continue;
392
                            }
393

394
                            iTreeValues[referencedVal] = ModelInstance(spawn, model);
395
                            iLoadedSpawns[referencedVal] = 1;
396
                        }
397
                        else
398
                        {
399
                            ++iLoadedSpawns[referencedVal];
400
#ifdef VMAP_DEBUG
401
                            if (iTreeValues[referencedVal].ID != spawn.ID)
402
                                TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node");
403
                            else if (iTreeValues[referencedVal].name != spawn.name)
404
                                TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : name collision on GUID=%u", spawn.ID);
405
#endif
406
                        }
407
                    }
408
                    else
409
                        result = false;
410
                }
411
            }
412
            iLoadedTiles[packTileID(tileX, tileY)] = true;
413
            fclose(tf);
414
        }
415
        else
416
            iLoadedTiles[packTileID(tileX, tileY)] = false;
417

418
        return result;
419
    }
420

421
    //=========================================================
422

423
    void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm)
424
    {
425
        uint32 tileID = packTileID(tileX, tileY);
426
        loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
427
        if (tile == iLoadedTiles.end())
428
        {
429
            VMAP_ERROR_LOG("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:%u X:%u Y:%u", iMapID, tileX, tileY);
430
            return;
431
        }
432
        if (tile->second) // file associated with tile
433
        {
434
            std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
435
            FILE* tf = fopen(tilefile.c_str(), "rb");
436
            if (tf)
437
            {
438
                bool result = true;
439
                char chunk[8];
440
                if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
441
                    result = false;
442
                uint32 numSpawns;
443
                if (fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
444
                    result = false;
445
                for (uint32 i=0; i<numSpawns && result; ++i)
446
                {
447
                    // read model spawns
448
                    ModelSpawn spawn;
449
                    result = ModelSpawn::readFromFile(tf, spawn);
450
                    if (result)
451
                    {
452
                        // release model instance
453
                        vm->releaseModelInstance(spawn.name);
454

455
                        // update tree
456
                        uint32 referencedNode;
457

458
                        if (fread(&referencedNode, sizeof(uint32), 1, tf) != 1)
459
                            result = false;
460
                        else
461
                        {
462
                            if (!iLoadedSpawns.count(referencedNode))
463
                            VMAP_ERROR_LOG("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '%s' (ID:%u)", spawn.name.c_str(), spawn.ID);
464
                            else if (--iLoadedSpawns[referencedNode] == 0)
465
                            {
466
                                iTreeValues[referencedNode].setUnloaded();
467
                                iLoadedSpawns.erase(referencedNode);
468
                            }
469
                        }
470
                    }
471
                }
472
                fclose(tf);
473
            }
474
        }
475
        iLoadedTiles.erase(tile);
476
    }
477

478
    void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count)
479
    {
480
        models = iTreeValues;
481
        count = iNTreeValues;
482
    }
483
}
484

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.