Legends-of-Azeroth-Pandaria-5.4.8

Форк
0
580 строк · 20.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 "TileAssembler.h"
19
#include "MapTree.h"
20
#include "BoundingIntervalHierarchy.h"
21
#include "VMapDefinitions.h"
22

23
#include <set>
24
#include <iomanip>
25
#include <sstream>
26
#include <iomanip>
27
#include <boost/filesystem.hpp>
28

29
using G3D::Vector3;
30
using G3D::AABox;
31
using G3D::inf;
32
using std::pair;
33

34
template<> struct BoundsTrait<VMAP::ModelSpawn*>
35
{
36
    static void getBounds(const VMAP::ModelSpawn* const &obj, G3D::AABox& out) { out = obj->getBounds(); }
37
};
38

39
namespace VMAP
40
{
41
    bool readChunk(FILE* rf, char *dest, const char *compare, uint32 len)
42
    {
43
        if (fread(dest, sizeof(char), len, rf) != len) return false;
44
        return memcmp(dest, compare, len) == 0;
45
    }
46

47
    Vector3 ModelPosition::transform(const Vector3& pIn) const
48
    {
49
        Vector3 out = pIn * iScale;
50
        out = iRotation * out;
51
        return(out);
52
    }
53

54
    //=================================================================
55

56
    TileAssembler::TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName)
57
        : iDestDir(pDestDirName), iSrcDir(pSrcDirName)
58
    {
59
        boost::filesystem::create_directory(iDestDir);
60
        //init();
61
    }
62

63
    TileAssembler::~TileAssembler()
64
    {
65
        //delete iCoordModelMapping;
66
    }
67

68
    bool TileAssembler::convertWorld2()
69
    {
70
        bool success = readMapSpawns();
71
        if (!success)
72
            return false;
73

74
        // export Map data
75
        for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end() && success; ++map_iter)
76
        {
77
            // build global map tree
78
            std::vector<ModelSpawn*> mapSpawns;
79
            UniqueEntryMap::iterator entry;
80
            printf("Calculating model bounds for map %u...\n", map_iter->first);
81
            for (entry = map_iter->second->UniqueEntries.begin(); entry != map_iter->second->UniqueEntries.end(); ++entry)
82
            {
83
                // M2 models don't have a bound set in WDT/ADT placement data, i still think they're not used for LoS at all on retail
84
                if (entry->second.flags & MOD_M2)
85
                {
86
                    if (!calculateTransformedBound(entry->second))
87
                        break;
88
                }
89
                else if (entry->second.flags & MOD_WORLDSPAWN) // WMO maps and terrain maps use different origin, so we need to adapt :/
90
                {
91
                    /// @todo remove extractor hack and uncomment below line:
92
                    //entry->second.iPos += Vector3(533.33333f*32, 533.33333f*32, 0.f);
93
                    entry->second.iBound = entry->second.iBound + Vector3(533.33333f*32, 533.33333f*32, 0.f);
94
                }
95
                mapSpawns.push_back(&(entry->second));
96
                spawnedModelFiles.insert(entry->second.name);
97
            }
98

99
            printf("Creating map tree for map %u...\n", map_iter->first);
100
            BIH pTree;
101

102
            try
103
            {
104
                pTree.build(mapSpawns, BoundsTrait<ModelSpawn*>::getBounds);
105
            }
106
            catch (std::exception& e)
107
            {
108
                printf("Exception ""%s"" when calling pTree.build", e.what());
109
                return false;
110
            }
111

112
            // ===> possibly move this code to StaticMapTree class
113
            std::map<uint32, uint32> modelNodeIdx;
114
            for (uint32 i=0; i<mapSpawns.size(); ++i)
115
                modelNodeIdx.insert(pair<uint32, uint32>(mapSpawns[i]->ID, i));
116

117
            // write map tree file
118
            std::stringstream mapfilename;
119
            mapfilename << iDestDir << '/' << std::setfill('0') << std::setw(4) << map_iter->first << ".vmtree";
120
            FILE* mapfile = fopen(mapfilename.str().c_str(), "wb");
121
            if (!mapfile)
122
            {
123
                success = false;
124
                printf("Cannot open %s\n", mapfilename.str().c_str());
125
                break;
126
            }
127

128
            //general info
129
            if (success && fwrite(VMAP_MAGIC, 1, 8, mapfile) != 8) success = false;
130
            uint32 globalTileID = StaticMapTree::packTileID(65, 65);
131
            pair<TileMap::iterator, TileMap::iterator> globalRange = map_iter->second->TileEntries.equal_range(globalTileID);
132
            char isTiled = globalRange.first == globalRange.second; // only maps without terrain (tiles) have global WMO
133
            if (success && fwrite(&isTiled, sizeof(char), 1, mapfile) != 1) success = false;
134
            // Nodes
135
            if (success && fwrite("NODE", 4, 1, mapfile) != 1) success = false;
136
            if (success) success = pTree.writeToFile(mapfile);
137
            // global map spawns (WDT), if any (most instances)
138
            if (success && fwrite("GOBJ", 4, 1, mapfile) != 1) success = false;
139

140
            for (TileMap::iterator glob=globalRange.first; glob != globalRange.second && success; ++glob)
141
            {
142
                success = ModelSpawn::writeToFile(mapfile, map_iter->second->UniqueEntries[glob->second]);
143
            }
144

145
            fclose(mapfile);
146

147
            // <====
148

149
            // write map tile files, similar to ADT files, only with extra BSP tree node info
150
            TileMap &tileEntries = map_iter->second->TileEntries;
151
            TileMap::iterator tile;
152
            for (tile = tileEntries.begin(); tile != tileEntries.end(); ++tile)
153
            {
154
                const ModelSpawn &spawn = map_iter->second->UniqueEntries[tile->second];
155
                if (spawn.flags & MOD_WORLDSPAWN) // WDT spawn, saved as tile 65/65 currently...
156
                    continue;
157
                uint32 nSpawns = tileEntries.count(tile->first);
158
                std::stringstream tilefilename;
159
                tilefilename.fill('0');
160
                tilefilename << iDestDir << '/' << std::setw(4) << map_iter->first << '_';
161
                uint32 x, y;
162
                StaticMapTree::unpackTileID(tile->first, x, y);
163
                tilefilename << std::setw(2) << x << '_' << std::setw(2) << y << ".vmtile";
164
                if (FILE* tilefile = fopen(tilefilename.str().c_str(), "wb"))
165
                {
166
                    // file header
167
                    if (success && fwrite(VMAP_MAGIC, 1, 8, tilefile) != 8) success = false;
168
                    // write number of tile spawns
169
                    if (success && fwrite(&nSpawns, sizeof(uint32), 1, tilefile) != 1) success = false;
170
                    // write tile spawns
171
                    for (uint32 s=0; s<nSpawns; ++s)
172
                    {
173
                        if (s)
174
                            ++tile;
175
                        const ModelSpawn &spawn2 = map_iter->second->UniqueEntries[tile->second];
176
                        success = success && ModelSpawn::writeToFile(tilefile, spawn2);
177
                        // MapTree nodes to update when loading tile:
178
                        std::map<uint32, uint32>::iterator nIdx = modelNodeIdx.find(spawn2.ID);
179
                        if (success && fwrite(&nIdx->second, sizeof(uint32), 1, tilefile) != 1) success = false;
180
                    }
181
                    fclose(tilefile);
182
                }
183
            }
184
            // break; //test, extract only first map; TODO: remvoe this line
185
        }
186

187
        // add an object models, listed in temp_gameobject_models file
188
        exportGameobjectModels();
189
        // export objects
190
        std::cout << "\nConverting Model Files" << std::endl;
191
        for (std::string const& spawnedModelFile : spawnedModelFiles)
192
        {
193
            std::cout << "Converting " << spawnedModelFile << std::endl;
194
            if (!convertRawFile(spawnedModelFile))
195
            {
196
                std::cout << "error converting " << spawnedModelFile << std::endl;
197
                success = false;
198
                break;
199
            }
200
        }
201

202
        //cleanup:
203
        for (std::pair<uint32 const, MapSpawns*>& map_iter : mapData)
204
            delete map_iter.second;
205
        
206
        return success;
207
    }
208

209
    bool TileAssembler::readMapSpawns()
210
    {
211
        std::string fname = iSrcDir + "/dir_bin";
212
        FILE* dirf = fopen(fname.c_str(), "rb");
213
        if (!dirf)
214
        {
215
            printf("Could not read dir_bin file!\n");
216
            return false;
217
        }
218
        printf("Read coordinate mapping...\n");
219
        uint32 mapID, tileX, tileY, check;
220
        G3D::Vector3 v1, v2;
221
        ModelSpawn spawn;
222
        while (!feof(dirf))
223
        {
224
            // read mapID, tileX, tileY, Flags, adtID, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
225
            check = fread(&mapID, sizeof(uint32), 1, dirf);
226
            if (check == 0) // EoF...
227
                break;
228
            fread(&tileX, sizeof(uint32), 1, dirf);
229
            fread(&tileY, sizeof(uint32), 1, dirf);
230
            if (!ModelSpawn::readFromFile(dirf, spawn))
231
                break;
232

233
            MapSpawns *current;
234
            MapData::iterator map_iter = mapData.find(mapID);
235
            if (map_iter == mapData.end())
236
            {
237
                printf("spawning Map %d\n", mapID);
238
                mapData[mapID] = current = new MapSpawns();
239
            }
240
            else 
241
                current = map_iter->second;
242

243
            current->UniqueEntries.emplace(pair<uint32, ModelSpawn>(spawn.ID, spawn));
244
            current->TileEntries.insert(pair<uint32, uint32>(StaticMapTree::packTileID(tileX, tileY), spawn.ID));
245
        }
246
        bool success = (ferror(dirf) == 0);
247
        fclose(dirf);
248
        return success;
249
    }
250

251
    bool TileAssembler::calculateTransformedBound(ModelSpawn &spawn)
252
    {
253
        std::string modelFilename(iSrcDir);
254
        modelFilename.push_back('/');
255
        modelFilename.append(spawn.name);
256

257
        ModelPosition modelPosition;
258
        modelPosition.iDir = spawn.iRot;
259
        modelPosition.iScale = spawn.iScale;
260
        modelPosition.init();
261

262
        WorldModel_Raw raw_model;
263
        if (!raw_model.Read(modelFilename.c_str()))
264
            return false;
265

266
        uint32 groups = raw_model.groupsArray.size();
267
        if (groups != 1)
268
            printf("Warning: '%s' does not seem to be a M2 model!\n", modelFilename.c_str());
269

270
        AABox modelBound;
271
        bool boundEmpty=true;
272

273
        for (uint32 g=0; g<groups; ++g) // should be only one for M2 files...
274
        {
275
            std::vector<Vector3>& vertices = raw_model.groupsArray[g].vertexArray;
276

277
            if (vertices.empty())
278
            {
279
                std::cout << "error: model '" << spawn.name << "' has no geometry!" << std::endl;
280
                continue;
281
            }
282

283
            uint32 nvectors = vertices.size();
284
            for (uint32 i = 0; i < nvectors; ++i)
285
            {
286
                Vector3 v = modelPosition.transform(vertices[i]);
287

288
                if (boundEmpty) 
289
                {
290
                    modelBound = AABox(v, v); 
291
                    boundEmpty = false;
292
                }
293
                else
294
                    modelBound.merge(v);
295
            }
296
        }
297
        spawn.iBound = modelBound + spawn.iPos;
298
        spawn.flags |= MOD_HAS_BOUND;
299
        return true;
300
    }
301

302
#pragma pack(push, 1)
303
    struct WMOLiquidHeader
304
    {
305
        int xverts, yverts, xtiles, ytiles;
306
        float pos_x;
307
        float pos_y;
308
        float pos_z;
309
        short material;
310
    };
311
#pragma pack(pop)    
312
    //=================================================================
313
    bool TileAssembler::convertRawFile(const std::string& pModelFilename)
314
    {
315
        bool success = true;
316
        std::string filename = iSrcDir;
317
        if (filename.length() >0)
318
            filename.push_back('/');
319
        filename.append(pModelFilename);
320

321
        WorldModel_Raw raw_model;
322
        if (!raw_model.Read(filename.c_str()))
323
            return false;
324

325
        // write WorldModel
326
        WorldModel model;
327
        model.setRootWmoID(raw_model.RootWMOID);
328
        if (!raw_model.groupsArray.empty())
329
        {
330
            std::vector<GroupModel> groupsArray;
331

332
            uint32 groups = raw_model.groupsArray.size();
333
            for (uint32 g = 0; g < groups; ++g)
334
            {
335
                GroupModel_Raw& raw_group = raw_model.groupsArray[g];
336
                groupsArray.push_back(GroupModel(raw_group.mogpflags, raw_group.GroupWMOID, raw_group.bounds ));
337
                groupsArray.back().setMeshData(raw_group.vertexArray, raw_group.triangles);
338
                groupsArray.back().setLiquidData(raw_group.liquid);
339
            }
340

341
            model.setGroupModels(groupsArray);
342
        }
343

344
        success = model.writeFile(iDestDir + "/" + pModelFilename + ".vmo");
345
        //std::cout << "readRawFile2: '" << pModelFilename << "' tris: " << nElements << " nodes: " << nNodes << std::endl;
346
        return success;
347
    }
348

349
    void TileAssembler::exportGameobjectModels()
350
    {
351
        FILE* model_list = fopen((iSrcDir + "/" + "temp_gameobject_models").c_str(), "rb");
352
        if (!model_list)
353
            return;
354

355
        char ident[8];
356
        if (fread(ident, 1, 8, model_list) != 8 || memcmp(ident, VMAP::RAW_VMAP_MAGIC, 8) != 0)
357
        {
358
            fclose(model_list);
359
            return;
360
        }
361

362
        FILE* model_list_copy = fopen((iDestDir + "/" + GAMEOBJECT_MODELS).c_str(), "wb");
363
        if (!model_list_copy)
364
        {
365
            fclose(model_list);
366
            return;
367
        }
368

369
        fwrite(VMAP::VMAP_MAGIC, 1, 8, model_list_copy);
370

371
        uint32 name_length, displayId;
372
        uint8 isWmo;
373
        char buff[500];
374
        while (true)
375
        {
376
            if (fread(&displayId, sizeof(uint32), 1, model_list) != 1)
377
                if (feof(model_list))   // EOF flag is only set after failed reading attempt
378
                    break;
379

380
            if (fread(&isWmo, sizeof(uint8), 1, model_list) != 1
381
                || fread(&name_length, sizeof(uint32), 1, model_list) != 1
382
                || name_length >= sizeof(buff)
383
                || fread(&buff, sizeof(char), name_length, model_list) != name_length)
384
            {
385
                std::cout << "\nFile 'temp_gameobject_models' seems to be corrupted" << std::endl;
386
                break;
387
            }
388

389
            std::string model_name(buff, name_length);
390

391
            WorldModel_Raw raw_model;
392
            if (!raw_model.Read((iSrcDir + "/" + model_name).c_str()) )
393
                continue;
394

395
            spawnedModelFiles.insert(model_name);
396
            AABox bounds;
397
            bool boundEmpty = true;
398
            for (uint32 g = 0; g < raw_model.groupsArray.size(); ++g)
399
            {
400
                std::vector<Vector3>& vertices = raw_model.groupsArray[g].vertexArray;
401

402
                uint32 nvectors = vertices.size();
403
                for (uint32 i = 0; i < nvectors; ++i)
404
                {
405
                    Vector3& v = vertices[i];
406
                    if (boundEmpty)
407
                        bounds = AABox(v, v), boundEmpty = false;
408
                    else
409
                        bounds.merge(v);
410
                }
411
            }
412

413
            if (bounds.isEmpty())
414
            {
415
                std::cout << "\nModel " << std::string(buff, name_length) << " has empty bounding box" << std::endl;
416
                continue;
417
            }
418

419
            if (!bounds.isFinite())
420
            {
421
                std::cout << "\nModel " << std::string(buff, name_length) << " has invalid bounding box" << std::endl;
422
                continue;
423
            }
424

425
            fwrite(&displayId, sizeof(uint32), 1, model_list_copy);
426
            fwrite(&isWmo, sizeof(uint8), 1, model_list_copy);
427
            fwrite(&name_length, sizeof(uint32), 1, model_list_copy);
428
            fwrite(&buff, sizeof(char), name_length, model_list_copy);
429
            fwrite(&bounds.low(), sizeof(Vector3), 1, model_list_copy);
430
            fwrite(&bounds.high(), sizeof(Vector3), 1, model_list_copy);
431
        }
432

433
        fclose(model_list);
434
        fclose(model_list_copy);
435
    }
436

437
// temporary use defines to simplify read/check code (close file and return at fail)
438
#define READ_OR_RETURN(V, S) if (fread((V), (S), 1, rf) != 1) { \
439
                                fclose(rf); printf("readfail, op = %i\n", readOperation); return(false); }
440
#define READ_OR_RETURN_WITH_DELETE(V, S) if (fread((V), (S), 1, rf) != 1) { \
441
                                fclose(rf); printf("readfail, op = %i\n", readOperation); delete[] V; return(false); };
442
#define CMP_OR_RETURN(V, S)  if (strcmp((V), (S)) != 0)        { \
443
                                fclose(rf); printf("cmpfail, %s!=%s\n", V, S);return(false); }
444

445
    bool GroupModel_Raw::Read(FILE* rf)
446
    {
447
        char blockId[5];
448
        blockId[4] = 0;
449
        int blocksize;
450
        int readOperation = 0;
451

452
        READ_OR_RETURN(&mogpflags, sizeof(uint32));
453
        READ_OR_RETURN(&GroupWMOID, sizeof(uint32));
454

455
        Vector3 vec1, vec2;
456
        READ_OR_RETURN(&vec1, sizeof(Vector3));
457

458
        READ_OR_RETURN(&vec2, sizeof(Vector3));
459
        bounds.set(vec1, vec2);
460

461
        READ_OR_RETURN(&liquidflags, sizeof(uint32));
462

463
        // will this ever be used? what is it good for anyway??
464
        uint32 branches;
465
        READ_OR_RETURN(&blockId, 4);
466
        CMP_OR_RETURN(blockId, "GRP ");
467
        READ_OR_RETURN(&blocksize, sizeof(int));
468
        READ_OR_RETURN(&branches, sizeof(uint32));
469
        for (uint32 b=0; b<branches; ++b)
470
        {
471
            uint32 indexes;
472
            // indexes for each branch (not used jet)
473
            READ_OR_RETURN(&indexes, sizeof(uint32));
474
        }
475

476
        // ---- indexes
477
        READ_OR_RETURN(&blockId, 4);
478
        CMP_OR_RETURN(blockId, "INDX");
479
        READ_OR_RETURN(&blocksize, sizeof(int));
480
        uint32 nindexes;
481
        READ_OR_RETURN(&nindexes, sizeof(uint32));
482
        if (nindexes >0)
483
        {
484
            uint16 *indexarray = new uint16[nindexes];
485
            READ_OR_RETURN_WITH_DELETE(indexarray, nindexes*sizeof(uint16));
486
            triangles.reserve(nindexes / 3);
487
            for (uint32 i=0; i<nindexes; i+=3)
488
                triangles.push_back(MeshTriangle(indexarray[i], indexarray[i+1], indexarray[i+2]));
489

490
            delete[] indexarray;
491
        }
492

493
        // ---- vectors
494
        READ_OR_RETURN(&blockId, 4);
495
        CMP_OR_RETURN(blockId, "VERT");
496
        READ_OR_RETURN(&blocksize, sizeof(int));
497
        uint32 nvectors;
498
        READ_OR_RETURN(&nvectors, sizeof(uint32));
499

500
        if (nvectors >0)
501
        {
502
            float *vectorarray = new float[nvectors*3];
503
            READ_OR_RETURN_WITH_DELETE(vectorarray, nvectors*sizeof(float)*3);
504
            for (uint32 i=0; i<nvectors; ++i)
505
                vertexArray.push_back( Vector3(vectorarray + 3*i) );
506

507
            delete[] vectorarray;
508
        }
509
        // ----- liquid
510
        liquid = nullptr;
511
        if (liquidflags & 3)
512
        {
513
            READ_OR_RETURN(&blockId, 4);
514
            CMP_OR_RETURN(blockId, "LIQU");
515
            READ_OR_RETURN(&blocksize, sizeof(int));
516
            uint32 liquidType;
517
            READ_OR_RETURN(&liquidType, sizeof(uint32));
518
            if (liquidflags & 1)
519
            {
520
                WMOLiquidHeader hlq;
521
                READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader));
522
                liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), liquidType);
523
                uint32 size = hlq.xverts * hlq.yverts;
524
                READ_OR_RETURN(liquid->GetHeightStorage(), size * sizeof(float));
525
                size = hlq.xtiles * hlq.ytiles;
526
                READ_OR_RETURN(liquid->GetFlagsStorage(), size);
527
            }
528
            else
529
            {
530
                liquid = new WmoLiquid(0, 0, Vector3::zero(), liquidType);
531
                liquid->GetHeightStorage()[0] = bounds.high().z;
532
            }
533
        }        
534

535
        return true;
536
    }
537

538
    GroupModel_Raw::~GroupModel_Raw()
539
    {
540
        delete liquid;
541
    }
542

543
    bool WorldModel_Raw::Read(const char * path)
544
    {
545
        FILE* rf = fopen(path, "rb");
546
        if (!rf)
547
        {
548
            printf("ERROR: Can't open raw model file: %s\n", path);
549
            return false;
550
        }
551

552
        char ident[9];
553
        ident[8] = '\0';
554
        int readOperation = 0;
555

556
        READ_OR_RETURN(&ident, 8);
557
        CMP_OR_RETURN(ident, RAW_VMAP_MAGIC);
558

559
        // we have to read one int. This is needed during the export and we have to skip it here
560
        uint32 tempNVectors;
561
        READ_OR_RETURN(&tempNVectors, sizeof(tempNVectors));
562

563
        uint32 groups;
564
        READ_OR_RETURN(&groups, sizeof(uint32));
565
        READ_OR_RETURN(&RootWMOID, sizeof(uint32));
566

567
        groupsArray.resize(groups);
568
        bool succeed = true;
569
        for (uint32 g = 0; g < groups && succeed; ++g)
570
            succeed = groupsArray[g].Read(rf);
571

572
        if (succeed) /// rf will be freed inside Read if the function had any errors.
573
            fclose(rf);
574
        return succeed;
575
    }
576

577
    // drop of temporary use defines
578
    #undef READ_OR_RETURN
579
    #undef CMP_OR_RETURN
580
}
581

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

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

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

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